From 17682d43971ef53045fc9f9333cdfe2e51496431 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Fri, 27 Sep 2013 20:24:23 -0700 Subject: [PATCH] Updated DUMB --- Audio/CogAudio.xcodeproj/project.pbxproj | 11 +- Cog.xcodeproj/project.pbxproj | 56 +- .../AudioOverload.xcodeproj/project.pbxproj | 17 +- .../AudioOverload/aosdk/eng_psf/peops2/spu.c | 2 +- .../Dumb/Dumb.xcodeproj/project.pbxproj | 232 +- .../xcschemes/Dumb Framework.xcscheme | 59 + .../xcschemes/xcschememanagement.plist | 22 + Frameworks/Dumb/dumb/include/dumb.h | 1471 +-- .../Dumb/dumb/include/internal/aldumb.h | 27 + .../Dumb/dumb/include/internal/barray.h | 20 + .../Dumb/dumb/include/internal/blip_buf.h | 77 + Frameworks/Dumb/dumb/include/internal/dumb.h | 122 +- .../Dumb/dumb/include/internal/dumbfile.h | 13 + .../dumb/include/internal/fir_resampler.h | 18 + Frameworks/Dumb/dumb/include/internal/it.h | 1660 +-- .../dumb/include/internal/lanczos_resampler.h | 19 + Frameworks/Dumb/dumb/include/internal/lpc.h | 30 + Frameworks/Dumb/dumb/include/internal/riff.h | 24 + .../Dumb/dumb/include/internal/stack_alloc.h | 113 + .../Dumb/dumb/include/internal/tarray.h | 25 + Frameworks/Dumb/dumb/prj/.gitignore | 3 + Frameworks/Dumb/dumb/prj/dumb/dumb.pro | 132 + Frameworks/Dumb/dumb/src/core/atexit.c | 142 +- Frameworks/Dumb/dumb/src/core/duhlen.c | 84 +- Frameworks/Dumb/dumb/src/core/duhtag.c | 76 +- Frameworks/Dumb/dumb/src/core/dumbfile.c | 819 +- Frameworks/Dumb/dumb/src/core/loadduh.c | 84 +- Frameworks/Dumb/dumb/src/core/makeduh.c | 283 +- Frameworks/Dumb/dumb/src/core/rawsig.c | 102 +- Frameworks/Dumb/dumb/src/core/readduh.c | 214 +- Frameworks/Dumb/dumb/src/core/register.c | 208 +- Frameworks/Dumb/dumb/src/core/rendduh.c | 368 +- Frameworks/Dumb/dumb/src/core/rendsig.c | 696 +- Frameworks/Dumb/dumb/src/core/unload.c | 128 +- Frameworks/Dumb/dumb/src/helpers/barray.c | 159 + Frameworks/Dumb/dumb/src/helpers/blip_buf.c | 354 + Frameworks/Dumb/dumb/src/helpers/clickrem.c | 562 +- .../Dumb/dumb/src/helpers/fir_resampler.c | 281 + .../Dumb/dumb/src/helpers/lanczos_resampler.c | 229 + Frameworks/Dumb/dumb/src/helpers/lpc.c | 320 + Frameworks/Dumb/dumb/src/helpers/memfile.c | 213 +- Frameworks/Dumb/dumb/src/helpers/resamp2.inc | 313 +- Frameworks/Dumb/dumb/src/helpers/resamp3.inc | 812 +- Frameworks/Dumb/dumb/src/helpers/resample.c | 795 +- Frameworks/Dumb/dumb/src/helpers/resample.inc | 570 +- Frameworks/Dumb/dumb/src/helpers/riff.c | 85 + Frameworks/Dumb/dumb/src/helpers/sampbuf.c | 128 +- Frameworks/Dumb/dumb/src/helpers/silence.c | 58 +- Frameworks/Dumb/dumb/src/helpers/stdfile.c | 239 +- Frameworks/Dumb/dumb/src/helpers/tarray.c | 175 + Frameworks/Dumb/dumb/src/it/itload.c | 85 +- Frameworks/Dumb/dumb/src/it/itload2.c | 58 +- Frameworks/Dumb/dumb/src/it/itmisc.c | 494 +- Frameworks/Dumb/dumb/src/it/itorder.c | 126 +- Frameworks/Dumb/dumb/src/it/itread.c | 2613 ++-- Frameworks/Dumb/dumb/src/it/itread2.c | 58 +- Frameworks/Dumb/dumb/src/it/itrender.c | 10047 ++++++++++------ Frameworks/Dumb/dumb/src/it/itunload.c | 144 +- Frameworks/Dumb/dumb/src/it/load669.c | 42 + Frameworks/Dumb/dumb/src/it/load6692.c | 34 + Frameworks/Dumb/dumb/src/it/loadamf.c | 42 + Frameworks/Dumb/dumb/src/it/loadamf2.c | 34 + Frameworks/Dumb/dumb/src/it/loadany.c | 38 + Frameworks/Dumb/dumb/src/it/loadany2.c | 29 + Frameworks/Dumb/dumb/src/it/loadasy.c | 42 + Frameworks/Dumb/dumb/src/it/loadasy2.c | 34 + Frameworks/Dumb/dumb/src/it/loadmod.c | 84 +- Frameworks/Dumb/dumb/src/it/loadmod2.c | 58 +- Frameworks/Dumb/dumb/src/it/loadmtm.c | 42 + Frameworks/Dumb/dumb/src/it/loadmtm2.c | 34 + Frameworks/Dumb/dumb/src/it/loadokt.c | 42 + Frameworks/Dumb/dumb/src/it/loadokt2.c | 29 + Frameworks/Dumb/dumb/src/it/loadoldpsm.c | 43 + Frameworks/Dumb/dumb/src/it/loadoldpsm2.c | 34 + Frameworks/Dumb/dumb/src/it/loadpsm.c | 42 + Frameworks/Dumb/dumb/src/it/loadpsm2.c | 34 + Frameworks/Dumb/dumb/src/it/loadptm.c | 42 + Frameworks/Dumb/dumb/src/it/loadptm2.c | 34 + Frameworks/Dumb/dumb/src/it/loadriff.c | 42 + Frameworks/Dumb/dumb/src/it/loadriff2.c | 29 + Frameworks/Dumb/dumb/src/it/loads3m.c | 84 +- Frameworks/Dumb/dumb/src/it/loads3m2.c | 58 +- Frameworks/Dumb/dumb/src/it/loadstm.c | 42 + Frameworks/Dumb/dumb/src/it/loadstm2.c | 29 + Frameworks/Dumb/dumb/src/it/loadxm.c | 84 +- Frameworks/Dumb/dumb/src/it/loadxm2.c | 58 +- Frameworks/Dumb/dumb/src/it/ptmeffect.c | 125 + Frameworks/Dumb/dumb/src/it/read669.c | 447 + Frameworks/Dumb/dumb/src/it/read6692.c | 29 + Frameworks/Dumb/dumb/src/it/readam.c | 787 ++ Frameworks/Dumb/dumb/src/it/readamf.c | 523 + Frameworks/Dumb/dumb/src/it/readamf2.c | 29 + Frameworks/Dumb/dumb/src/it/readany.c | 132 + Frameworks/Dumb/dumb/src/it/readany2.c | 29 + Frameworks/Dumb/dumb/src/it/readasy.c | 331 + Frameworks/Dumb/dumb/src/it/readdsmf.c | 382 + Frameworks/Dumb/dumb/src/it/readmod.c | 1233 +- Frameworks/Dumb/dumb/src/it/readmod2.c | 58 +- Frameworks/Dumb/dumb/src/it/readmtm.c | 412 + Frameworks/Dumb/dumb/src/it/readokt.c | 558 + Frameworks/Dumb/dumb/src/it/readokt2.c | 29 + Frameworks/Dumb/dumb/src/it/readoldpsm.c | 688 ++ Frameworks/Dumb/dumb/src/it/readpsm.c | 1286 ++ Frameworks/Dumb/dumb/src/it/readptm.c | 551 + Frameworks/Dumb/dumb/src/it/readriff.c | 57 + Frameworks/Dumb/dumb/src/it/reads3m.c | 1435 +-- Frameworks/Dumb/dumb/src/it/reads3m2.c | 58 +- Frameworks/Dumb/dumb/src/it/readstm.c | 395 + Frameworks/Dumb/dumb/src/it/readstm2.c | 29 + Frameworks/Dumb/dumb/src/it/readxm.c | 2424 ++-- Frameworks/Dumb/dumb/src/it/readxm2.c | 58 +- Frameworks/Dumb/dumb/src/it/xmeffect.c | 487 +- Frameworks/Dumb/dumb/vc6/dumb/.gitignore | 3 + Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj | 2007 +++ Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj | 221 + .../Dumb/dumb/vc6/dumb/dumb.vcxproj.filters | 341 + .../FLAC/flac.xcodeproj/project.pbxproj | 8 + Frameworks/GME/GME.xcodeproj/project.pbxproj | 690 +- Frameworks/GME/gme/Ay_Apu.cpp | 125 +- Frameworks/GME/gme/Ay_Apu.h | 134 +- Frameworks/GME/gme/Ay_Cpu.cpp | 1670 +-- Frameworks/GME/gme/Ay_Emu.cpp | 761 +- Frameworks/GME/gme/Ay_Emu.h | 64 +- Frameworks/GME/gme/Blip_Buffer.cpp | 955 +- Frameworks/GME/gme/Blip_Buffer.h | 687 +- Frameworks/GME/gme/Classic_Emu.cpp | 108 +- Frameworks/GME/gme/Classic_Emu.h | 144 +- Frameworks/GME/gme/Dual_Resampler.cpp | 448 +- Frameworks/GME/gme/Dual_Resampler.h | 57 +- Frameworks/GME/gme/Effects_Buffer.cpp | 1163 +- Frameworks/GME/gme/Effects_Buffer.h | 183 +- Frameworks/GME/gme/Fir_Resampler.cpp | 148 +- Frameworks/GME/gme/Fir_Resampler.h | 200 +- Frameworks/GME/gme/Gb_Apu.cpp | 541 +- Frameworks/GME/gme/Gb_Apu.h | 221 +- Frameworks/GME/gme/Gb_Cpu.cpp | 1039 +- Frameworks/GME/gme/Gb_Cpu.h | 101 +- Frameworks/GME/gme/Gb_Oscs.cpp | 846 +- Frameworks/GME/gme/Gb_Oscs.h | 213 +- Frameworks/GME/gme/Gbs_Emu.cpp | 435 +- Frameworks/GME/gme/Gbs_Emu.h | 103 +- Frameworks/GME/gme/Gme_File.cpp | 399 +- Frameworks/GME/gme/Gme_File.h | 298 +- Frameworks/GME/gme/Gym_Emu.cpp | 807 +- Frameworks/GME/gme/Gym_Emu.h | 170 +- Frameworks/GME/gme/Hes_Apu.cpp | 352 +- Frameworks/GME/gme/Hes_Apu.h | 101 +- Frameworks/GME/gme/Hes_Cpu.cpp | 1340 +-- Frameworks/GME/gme/Hes_Cpu.h | 153 +- Frameworks/GME/gme/Hes_Emu.cpp | 721 +- Frameworks/GME/gme/Hes_Emu.h | 98 +- Frameworks/GME/gme/Kss_Cpu.cpp | 1741 +-- Frameworks/GME/gme/Kss_Emu.cpp | 597 +- Frameworks/GME/gme/Kss_Emu.h | 129 +- Frameworks/GME/gme/Kss_Scc_Apu.cpp | 91 +- Frameworks/GME/gme/Kss_Scc_Apu.h | 119 +- Frameworks/GME/gme/M3u_Playlist.cpp | 902 +- Frameworks/GME/gme/M3u_Playlist.h | 154 +- Frameworks/GME/gme/Multi_Buffer.cpp | 522 +- Frameworks/GME/gme/Multi_Buffer.h | 219 +- Frameworks/GME/gme/Music_Emu.cpp | 633 +- Frameworks/GME/gme/Music_Emu.h | 463 +- Frameworks/GME/gme/Nes_Apu.cpp | 79 +- Frameworks/GME/gme/Nes_Apu.h | 125 +- Frameworks/GME/gme/Nes_Cpu.cpp | 1076 +- Frameworks/GME/gme/Nes_Cpu.h | 161 +- Frameworks/GME/gme/Nes_Fme7_Apu.cpp | 10 +- Frameworks/GME/gme/Nes_Fme7_Apu.h | 18 +- Frameworks/GME/gme/Nes_Namco_Apu.cpp | 297 +- Frameworks/GME/gme/Nes_Namco_Apu.h | 14 +- Frameworks/GME/gme/Nes_Oscs.cpp | 135 +- Frameworks/GME/gme/Nes_Oscs.h | 16 +- Frameworks/GME/gme/Nes_Vrc6_Apu.cpp | 19 +- Frameworks/GME/gme/Nes_Vrc6_Apu.h | 14 +- Frameworks/GME/gme/Nsf_Emu.cpp | 887 +- Frameworks/GME/gme/Nsf_Emu.h | 107 +- Frameworks/GME/gme/Nsfe_Emu.cpp | 659 +- Frameworks/GME/gme/Nsfe_Emu.h | 36 +- Frameworks/GME/gme/Sap_Apu.cpp | 59 +- Frameworks/GME/gme/Sap_Apu.h | 60 +- Frameworks/GME/gme/Sap_Cpu.cpp | 1025 +- Frameworks/GME/gme/Sap_Emu.cpp | 852 +- Frameworks/GME/gme/Sap_Emu.h | 78 +- Frameworks/GME/gme/Sms_Apu.cpp | 529 +- Frameworks/GME/gme/Sms_Apu.h | 139 +- Frameworks/GME/gme/Snes_Spc.cpp | 758 +- Frameworks/GME/gme/Snes_Spc.h | 352 +- Frameworks/GME/gme/Spc_Cpu.cpp | 1578 +-- Frameworks/GME/gme/Spc_Cpu.h | 1282 +- Frameworks/GME/gme/Spc_Dsp.cpp | 2085 +++- Frameworks/GME/gme/Spc_Dsp.h | 493 +- Frameworks/GME/gme/Spc_Emu.cpp | 749 +- Frameworks/GME/gme/Spc_Emu.h | 98 +- Frameworks/GME/gme/Vgm_Emu.cpp | 942 +- Frameworks/GME/gme/Vgm_Emu.h | 101 +- Frameworks/GME/gme/Ym2413_Emu.cpp | 99 +- Frameworks/GME/gme/Ym2413_Emu.h | 70 +- Frameworks/GME/gme/Ym2612_Emu.cpp | 1318 +- Frameworks/GME/gme/Ym2612_Emu.h | 26 +- Frameworks/GME/gme/blargg_common.h | 399 +- Frameworks/GME/gme/blargg_config.h | 86 +- Frameworks/GME/gme/blargg_endian.h | 361 +- Frameworks/GME/gme/blargg_source.h | 252 +- Frameworks/GME/gme/gme.cpp | 683 +- Frameworks/GME/gme/gme.h | 520 +- Frameworks/MAC/MAC.xcodeproj/project.pbxproj | 8 + .../MAC/mac-src/src/MACLib/APEDecompress.cpp | 8 +- .../MAC/mac-src/src/MACLib/APEDecompress.h | 2 +- Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp | 12 +- Frameworks/MAC/mac-src/src/MACLib/APEInfo.h | 2 +- .../MAC/mac-src/src/MACLib/APESimple.cpp | 8 +- Frameworks/MAC/mac-src/src/MACLib/MACLib.h | 2 +- Frameworks/MAC/mac-src/src/MACLib/MD5.cpp | 2 +- .../src/MACLib/Old/APEDecompressOld.cpp | 4 +- .../mac-src/src/MACLib/Old/APEDecompressOld.h | 2 +- .../MAC/mac-src/src/MACLib/Old/UnMAC.cpp | 6 +- Frameworks/MAD/MAD.xcodeproj/project.pbxproj | 10 + .../MPCDec/MPCDec.xcodeproj/project.pbxproj | 8 + Frameworks/Ogg/ogg.xcodeproj/project.pbxproj | 12 +- .../Files/shorten/include/shn_reader.h | 2 +- .../Shorten/Shorten.xcodeproj/project.pbxproj | 12 +- .../TagLib/TagLib.xcodeproj/project.pbxproj | 8 + .../Vorbis/Vorbis.xcodeproj/project.pbxproj | 8 + Frameworks/WMA/WMA.xcodeproj/project.pbxproj | 14 +- .../WavPack/WavPack.xcodeproj/project.pbxproj | 8 + Plugins/APL/APL.xcodeproj/project.pbxproj | 12 + .../AudioOverload.xcodeproj/project.pbxproj | 17 +- .../CoreAudio.xcodeproj/project.pbxproj | 8 + .../CueSheet.xcodeproj/project.pbxproj | 10 +- Plugins/Dumb/Dumb.xcodeproj/project.pbxproj | 16 + Plugins/Dumb/DumbDecoder.h | 4 + Plugins/Dumb/DumbDecoder.m | 49 +- Plugins/Dumb/DumbMetadataReader.m | 12 +- .../FileSource.xcodeproj/project.pbxproj | 8 + Plugins/Flac/Flac.xcodeproj/project.pbxproj | 8 + Plugins/GME/GME.xcodeproj/project.pbxproj | 8 + Plugins/GME/GameContainer.m | 2 +- Plugins/GME/GameDecoder.m | 28 +- Plugins/GME/GameMetadataReader.m | 16 +- .../HTTPSource.xcodeproj/project.pbxproj | 8 + Plugins/M3u/M3u.xcodeproj/project.pbxproj | 8 + Plugins/MAD/MAD.xcodeproj/project.pbxproj | 9 + .../MonkeysAudio.xcodeproj/project.pbxproj | 8 + .../Musepack.xcodeproj/project.pbxproj | 8 + Plugins/Pls/Pls.xcodeproj/project.pbxproj | 8 + .../Shorten/Shorten.xcodeproj/project.pbxproj | 8 + .../TagLib/TagLib.xcodeproj/project.pbxproj | 8 + .../Vorbis/Vorbis.xcodeproj/project.pbxproj | 8 + Plugins/WMA/WMA.xcodeproj/project.pbxproj | 13 + .../WavPack/WavPack.xcodeproj/project.pbxproj | 8 + .../General/General.xcodeproj/project.pbxproj | 15 +- .../Growl.framework/Versions/A/Growl | Bin 134672 -> 1093888 bytes .../Versions/A/Headers/Growl.h | 5 +- .../A/Headers/GrowlApplicationBridge.h | 194 +- .../Versions/A/Headers/GrowlDefines.h | 102 +- .../Versions/A/Resources/Info.plist | 20 +- 256 files changed, 47317 insertions(+), 35807 deletions(-) create mode 100644 Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme create mode 100644 Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Frameworks/Dumb/dumb/include/internal/aldumb.h create mode 100644 Frameworks/Dumb/dumb/include/internal/barray.h create mode 100644 Frameworks/Dumb/dumb/include/internal/blip_buf.h create mode 100644 Frameworks/Dumb/dumb/include/internal/dumbfile.h create mode 100644 Frameworks/Dumb/dumb/include/internal/fir_resampler.h create mode 100644 Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h create mode 100644 Frameworks/Dumb/dumb/include/internal/lpc.h create mode 100644 Frameworks/Dumb/dumb/include/internal/riff.h create mode 100644 Frameworks/Dumb/dumb/include/internal/stack_alloc.h create mode 100644 Frameworks/Dumb/dumb/include/internal/tarray.h create mode 100644 Frameworks/Dumb/dumb/prj/.gitignore create mode 100644 Frameworks/Dumb/dumb/prj/dumb/dumb.pro create mode 100644 Frameworks/Dumb/dumb/src/helpers/barray.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/blip_buf.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/fir_resampler.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/lpc.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/riff.c create mode 100644 Frameworks/Dumb/dumb/src/helpers/tarray.c create mode 100644 Frameworks/Dumb/dumb/src/it/load669.c create mode 100644 Frameworks/Dumb/dumb/src/it/load6692.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadamf.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadamf2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadany.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadany2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadasy.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadasy2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadmtm.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadmtm2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadokt.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadokt2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadoldpsm.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadoldpsm2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadpsm.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadpsm2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadptm.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadptm2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadriff.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadriff2.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadstm.c create mode 100644 Frameworks/Dumb/dumb/src/it/loadstm2.c create mode 100644 Frameworks/Dumb/dumb/src/it/ptmeffect.c create mode 100644 Frameworks/Dumb/dumb/src/it/read669.c create mode 100644 Frameworks/Dumb/dumb/src/it/read6692.c create mode 100644 Frameworks/Dumb/dumb/src/it/readam.c create mode 100644 Frameworks/Dumb/dumb/src/it/readamf.c create mode 100644 Frameworks/Dumb/dumb/src/it/readamf2.c create mode 100644 Frameworks/Dumb/dumb/src/it/readany.c create mode 100644 Frameworks/Dumb/dumb/src/it/readany2.c create mode 100644 Frameworks/Dumb/dumb/src/it/readasy.c create mode 100644 Frameworks/Dumb/dumb/src/it/readdsmf.c create mode 100644 Frameworks/Dumb/dumb/src/it/readmtm.c create mode 100644 Frameworks/Dumb/dumb/src/it/readokt.c create mode 100644 Frameworks/Dumb/dumb/src/it/readokt2.c create mode 100644 Frameworks/Dumb/dumb/src/it/readoldpsm.c create mode 100644 Frameworks/Dumb/dumb/src/it/readpsm.c create mode 100644 Frameworks/Dumb/dumb/src/it/readptm.c create mode 100644 Frameworks/Dumb/dumb/src/it/readriff.c create mode 100644 Frameworks/Dumb/dumb/src/it/readstm.c create mode 100644 Frameworks/Dumb/dumb/src/it/readstm2.c create mode 100644 Frameworks/Dumb/dumb/vc6/dumb/.gitignore create mode 100644 Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj create mode 100644 Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj create mode 100644 Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters mode change 100755 => 100644 Frameworks/GME/gme/Ay_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Ay_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Ay_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Ay_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Ay_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Blip_Buffer.cpp mode change 100755 => 100644 Frameworks/GME/gme/Blip_Buffer.h mode change 100755 => 100644 Frameworks/GME/gme/Classic_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Classic_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Dual_Resampler.cpp mode change 100755 => 100644 Frameworks/GME/gme/Dual_Resampler.h mode change 100755 => 100644 Frameworks/GME/gme/Effects_Buffer.cpp mode change 100755 => 100644 Frameworks/GME/gme/Effects_Buffer.h mode change 100755 => 100644 Frameworks/GME/gme/Fir_Resampler.cpp mode change 100755 => 100644 Frameworks/GME/gme/Fir_Resampler.h mode change 100755 => 100644 Frameworks/GME/gme/Gb_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gb_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Gb_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gb_Cpu.h mode change 100755 => 100644 Frameworks/GME/gme/Gb_Oscs.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gb_Oscs.h mode change 100755 => 100644 Frameworks/GME/gme/Gbs_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gbs_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Gme_File.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gme_File.h mode change 100755 => 100644 Frameworks/GME/gme/Gym_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Gym_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Hes_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Hes_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Hes_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Hes_Cpu.h mode change 100755 => 100644 Frameworks/GME/gme/Hes_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Hes_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Kss_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Kss_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Kss_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Kss_Scc_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Kss_Scc_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/M3u_Playlist.cpp mode change 100755 => 100644 Frameworks/GME/gme/M3u_Playlist.h mode change 100755 => 100644 Frameworks/GME/gme/Multi_Buffer.cpp mode change 100755 => 100644 Frameworks/GME/gme/Multi_Buffer.h mode change 100755 => 100644 Frameworks/GME/gme/Music_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Music_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Cpu.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Fme7_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Fme7_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Namco_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Namco_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Oscs.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Oscs.h mode change 100755 => 100644 Frameworks/GME/gme/Nes_Vrc6_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nes_Vrc6_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Nsf_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nsf_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Nsfe_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Nsfe_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Sap_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Sap_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Sap_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Sap_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Sap_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Sms_Apu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Sms_Apu.h mode change 100755 => 100644 Frameworks/GME/gme/Snes_Spc.cpp mode change 100755 => 100644 Frameworks/GME/gme/Snes_Spc.h mode change 100755 => 100644 Frameworks/GME/gme/Spc_Cpu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Spc_Cpu.h mode change 100755 => 100644 Frameworks/GME/gme/Spc_Dsp.cpp mode change 100755 => 100644 Frameworks/GME/gme/Spc_Dsp.h mode change 100755 => 100644 Frameworks/GME/gme/Spc_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Spc_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Vgm_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Vgm_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Ym2413_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Ym2413_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/Ym2612_Emu.cpp mode change 100755 => 100644 Frameworks/GME/gme/Ym2612_Emu.h mode change 100755 => 100644 Frameworks/GME/gme/blargg_common.h mode change 100755 => 100644 Frameworks/GME/gme/blargg_config.h mode change 100755 => 100644 Frameworks/GME/gme/blargg_endian.h mode change 100755 => 100644 Frameworks/GME/gme/blargg_source.h mode change 100755 => 100644 Frameworks/GME/gme/gme.cpp mode change 100755 => 100644 Frameworks/GME/gme/gme.h diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 15ad2a8c1..6281acb67 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -350,9 +350,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "CogAudio" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* CogAudio */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -405,6 +411,7 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -421,6 +428,7 @@ INSTALL_PATH = "@executable_path/../Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_NAME = CogAudio; + SDKROOT = macosx10.6; WARNING_LDFLAGS = ""; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -431,8 +439,8 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = ( - ppc, i386, + ppc, ); DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -447,6 +455,7 @@ INSTALL_PATH = "@executable_path/../Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_NAME = CogAudio; + SDKROOT = macosx10.6; WARNING_LDFLAGS = ""; WRAPPER_EXTENSION = framework; }; diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 161419cf4..6ed44b558 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -87,7 +87,6 @@ 17A8F6860D6A7FCA0095DA13 /* repeat_none.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F6830D6A7FCA0095DA13 /* repeat_none.png */; }; 17A8F6870D6A7FCA0095DA13 /* repeat_one.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F6840D6A7FCA0095DA13 /* repeat_one.png */; }; 17A8F71A0D6A89730095DA13 /* repeat_album.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F7190D6A89730095DA13 /* repeat_album.png */; }; - 17B6FA7F0D48225300C3BEF1 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17BF2B270CDD77EB007E1295 /* Sparkle.framework */; }; 17B7CF5C0F5A05EE00A47027 /* pauseBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF590F5A05EE00A47027 /* pauseBadge.png */; }; 17B7CF5D0F5A05EE00A47027 /* playBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF5A0F5A05EE00A47027 /* playBadge.png */; }; 17B7CF5E0F5A05EE00A47027 /* stopBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF5B0F5A05EE00A47027 /* stopBadge.png */; }; @@ -97,7 +96,6 @@ 17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */; }; 17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF80B8A86350009ACB1 /* CoreAudioKit.framework */; }; 17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5EA50B8A87850009ACB1 /* IOKit.framework */; }; - 17BF2B280CDD7802007E1295 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BF2B270CDD77EB007E1295 /* Sparkle.framework */; }; 17C809910C3BD201005707C4 /* WavPack.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808C80C3BD1DD005707C4 /* WavPack.bundle */; }; 17C809920C3BD206005707C4 /* Vorbis.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808BF0C3BD1D2005707C4 /* Vorbis.bundle */; }; 17C809930C3BD21D005707C4 /* TagLib.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808B60C3BD1C5005707C4 /* TagLib.bundle */; }; @@ -193,20 +191,6 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = "AudioOverload Plugin"; }; - 17BF2B260CDD77EB007E1295 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DC2EF5B0486A6940098B216; - remoteInfo = Sparkle; - }; - 17BF2B390CDD7827007E1295 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DC2EF4F0486A6940098B216; - remoteInfo = Sparkle; - }; 17C808780C3BD167005707C4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17C808710C3BD167005707C4 /* FileSource.xcodeproj */; @@ -518,7 +502,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 17B6FA7F0D48225300C3BEF1 /* Sparkle.framework in CopyFiles */, 17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */, 170680840B950164006BA573 /* Growl.framework in CopyFiles */, ); @@ -661,7 +644,6 @@ 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = ""; }; 17BB5CF80B8A86350009ACB1 /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = /System/Library/Frameworks/CoreAudioKit.framework; sourceTree = ""; }; 17BB5EA50B8A87850009ACB1 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sparkle.xcodeproj; path = Frameworks/Sparkle/Sparkle.xcodeproj; sourceTree = ""; }; 17C808660C3BD0F8005707C4 /* CoreAudio.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoreAudio.xcodeproj; path = Plugins/CoreAudio/CoreAudio.xcodeproj; sourceTree = SOURCE_ROOT; }; 17C808710C3BD167005707C4 /* FileSource.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FileSource.xcodeproj; path = Plugins/FileSource/FileSource.xcodeproj; sourceTree = ""; }; 17C8087A0C3BD173005707C4 /* Flac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Flac.xcodeproj; path = Plugins/Flac/Flac.xcodeproj; sourceTree = ""; }; @@ -778,7 +760,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 17BF2B280CDD7802007E1295 /* Sparkle.framework in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 8E6889240AAA403C00AD3950 /* Carbon.framework in Frameworks */, 17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */, @@ -826,7 +807,6 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( - 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */, 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */, 170680620B950158006BA573 /* Growl.framework */, 8E6889230AAA403C00AD3950 /* Carbon.framework */, @@ -1036,14 +1016,6 @@ name = Products; sourceTree = ""; }; - 17BF2B200CDD77EB007E1295 /* Products */ = { - isa = PBXGroup; - children = ( - 17BF2B270CDD77EB007E1295 /* Sparkle.framework */, - ); - name = Products; - sourceTree = ""; - }; 17C808720C3BD167005707C4 /* Products */ = { isa = PBXGroup; children = ( @@ -1511,7 +1483,6 @@ 17F3BB8B0CBC566200864489 /* PBXTargetDependency */, 17C8F44C0CBEDD37008D969D /* PBXTargetDependency */, 17C8F7DA0CBEF3F9008D969D /* PBXTargetDependency */, - 17BF2B3A0CDD7827007E1295 /* PBXTargetDependency */, 17643CBC0D5BD44900F0A9FE /* PBXTargetDependency */, 17B7CF960F5A0A3700A47027 /* PBXTargetDependency */, ); @@ -1526,8 +1497,11 @@ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Cog" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, @@ -1610,10 +1584,6 @@ ProductGroup = 17C808A80C3BD1BA005707C4 /* Products */; ProjectRef = 17C808A70C3BD1BA005707C4 /* Shorten.xcodeproj */; }, - { - ProductGroup = 17BF2B200CDD77EB007E1295 /* Products */; - ProjectRef = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - }, { ProductGroup = 17C808B10C3BD1C5005707C4 /* Products */; ProjectRef = 17C808B00C3BD1C5005707C4 /* TagLib.xcodeproj */; @@ -1646,13 +1616,6 @@ remoteRef = 17B7CF7B0F5A09FD00A47027 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 17BF2B270CDD77EB007E1295 /* Sparkle.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Sparkle.framework; - remoteRef = 17BF2B260CDD77EB007E1295 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 17C808790C3BD167005707C4 /* FileSource.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -1958,11 +1921,6 @@ name = "AudioOverload Plugin"; targetProxy = 17B7CF950F5A0A3700A47027 /* PBXContainerItemProxy */; }; - 17BF2B3A0CDD7827007E1295 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Sparkle; - targetProxy = 17BF2B390CDD7827007E1295 /* PBXContainerItemProxy */; - }; 17C8097E0C3BD1F5005707C4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Musepack; @@ -2166,9 +2124,11 @@ OTHER_LDFLAGS = ( "-weak_framework", CogAudio, + "-undefined", + dynamic_lookup, ); PRODUCT_NAME = Cog; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = app; ZERO_LINK = NO; }; @@ -2202,9 +2162,11 @@ OTHER_LDFLAGS = ( "-weak_framework", CogAudio, + "-undefined", + dynamic_lookup, ); PRODUCT_NAME = Cog; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = app; }; name = Release; diff --git a/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj b/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj index b8f8d6d70..9c5f74c9b 100644 --- a/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj +++ b/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj @@ -441,9 +441,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "AudioOverload" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* AudioOverload */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -522,6 +528,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -545,6 +552,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; USE_HEADERMAP = NO; WRAPPER_EXTENSION = framework; }; @@ -554,6 +562,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -574,6 +586,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; USE_HEADERMAP = NO; WRAPPER_EXTENSION = framework; }; @@ -589,7 +602,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -602,7 +615,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c b/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c index 5192df6ae..5b73ab000 100644 --- a/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c +++ b/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c @@ -540,7 +540,7 @@ static void *MAINThread(int samp2run) lastch=ch; // lastns=ns; // changemeback - return; + return 0; } } diff --git a/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj b/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj index 64b70fc19..9a71db60c 100644 --- a/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj +++ b/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 17C8F63E0CBEE797008D969D /* dumb.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60C0CBEE797008D969D /* dumb.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17C8F63F0CBEE797008D969D /* dumb.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60E0CBEE797008D969D /* dumb.h */; }; - 17C8F6400CBEE797008D969D /* it.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60F0CBEE797008D969D /* it.h */; }; + 17C8F6400CBEE797008D969D /* it.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60F0CBEE797008D969D /* it.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17C8F6410CBEE797008D969D /* atexit.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6120CBEE797008D969D /* atexit.c */; }; 17C8F6420CBEE797008D969D /* duhlen.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6130CBEE797008D969D /* duhlen.c */; }; 17C8F6430CBEE797008D969D /* duhtag.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6140CBEE797008D969D /* duhtag.c */; }; @@ -49,6 +49,61 @@ 17C8F6680CBEE797008D969D /* readxm.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63B0CBEE797008D969D /* readxm.c */; }; 17C8F6690CBEE797008D969D /* readxm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63C0CBEE797008D969D /* readxm2.c */; }; 17C8F66A0CBEE797008D969D /* xmeffect.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63D0CBEE797008D969D /* xmeffect.c */; }; + 8370B62617F60FE2001A4D7A /* barray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B61E17F60FE2001A4D7A /* barray.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8370B62817F60FE2001A4D7A /* dumbfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62017F60FE2001A4D7A /* dumbfile.h */; }; + 8370B62A17F60FE2001A4D7A /* lpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62217F60FE2001A4D7A /* lpc.h */; }; + 8370B62B17F60FE2001A4D7A /* riff.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62317F60FE2001A4D7A /* riff.h */; }; + 8370B62C17F60FE2001A4D7A /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62417F60FE2001A4D7A /* stack_alloc.h */; }; + 8370B62D17F60FE2001A4D7A /* tarray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62517F60FE2001A4D7A /* tarray.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8370B63417F61001001A4D7A /* barray.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B62E17F61001001A4D7A /* barray.c */; }; + 8370B63517F61001001A4D7A /* blip_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B62F17F61001001A4D7A /* blip_buf.c */; }; + 8370B63617F61001001A4D7A /* lanczos_resampler.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63017F61001001A4D7A /* lanczos_resampler.c */; }; + 8370B63717F61001001A4D7A /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63117F61001001A4D7A /* lpc.c */; }; + 8370B63817F61001001A4D7A /* riff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63217F61001001A4D7A /* riff.c */; }; + 8370B63917F61001001A4D7A /* tarray.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63317F61001001A4D7A /* tarray.c */; }; + 8370B66317F61038001A4D7A /* load669.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63A17F61038001A4D7A /* load669.c */; }; + 8370B66417F61038001A4D7A /* load6692.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63B17F61038001A4D7A /* load6692.c */; }; + 8370B66517F61038001A4D7A /* loadamf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63C17F61038001A4D7A /* loadamf.c */; }; + 8370B66617F61038001A4D7A /* loadamf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63D17F61038001A4D7A /* loadamf2.c */; }; + 8370B66717F61038001A4D7A /* loadany.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63E17F61038001A4D7A /* loadany.c */; }; + 8370B66817F61038001A4D7A /* loadany2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63F17F61038001A4D7A /* loadany2.c */; }; + 8370B66917F61038001A4D7A /* loadasy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64017F61038001A4D7A /* loadasy.c */; }; + 8370B66A17F61038001A4D7A /* loadasy2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64117F61038001A4D7A /* loadasy2.c */; }; + 8370B66B17F61038001A4D7A /* loadmtm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64217F61038001A4D7A /* loadmtm.c */; }; + 8370B66C17F61038001A4D7A /* loadmtm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64317F61038001A4D7A /* loadmtm2.c */; }; + 8370B66D17F61038001A4D7A /* loadokt.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64417F61038001A4D7A /* loadokt.c */; }; + 8370B66E17F61038001A4D7A /* loadokt2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64517F61038001A4D7A /* loadokt2.c */; }; + 8370B66F17F61038001A4D7A /* loadoldpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64617F61038001A4D7A /* loadoldpsm.c */; }; + 8370B67017F61038001A4D7A /* loadoldpsm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64717F61038001A4D7A /* loadoldpsm2.c */; }; + 8370B67117F61038001A4D7A /* loadpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64817F61038001A4D7A /* loadpsm.c */; }; + 8370B67217F61038001A4D7A /* loadpsm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64917F61038001A4D7A /* loadpsm2.c */; }; + 8370B67317F61038001A4D7A /* loadptm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64A17F61038001A4D7A /* loadptm.c */; }; + 8370B67417F61038001A4D7A /* loadptm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64B17F61038001A4D7A /* loadptm2.c */; }; + 8370B67517F61038001A4D7A /* loadriff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64C17F61038001A4D7A /* loadriff.c */; }; + 8370B67617F61038001A4D7A /* loadriff2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64D17F61038001A4D7A /* loadriff2.c */; }; + 8370B67717F61038001A4D7A /* loadstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64E17F61038001A4D7A /* loadstm.c */; }; + 8370B67817F61038001A4D7A /* loadstm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64F17F61038001A4D7A /* loadstm2.c */; }; + 8370B67917F61038001A4D7A /* ptmeffect.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65017F61038001A4D7A /* ptmeffect.c */; }; + 8370B67A17F61038001A4D7A /* read669.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65117F61038001A4D7A /* read669.c */; }; + 8370B67B17F61038001A4D7A /* read6692.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65217F61038001A4D7A /* read6692.c */; }; + 8370B67C17F61038001A4D7A /* readam.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65317F61038001A4D7A /* readam.c */; }; + 8370B67D17F61038001A4D7A /* readamf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65417F61038001A4D7A /* readamf.c */; }; + 8370B67E17F61038001A4D7A /* readamf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65517F61038001A4D7A /* readamf2.c */; }; + 8370B67F17F61038001A4D7A /* readany.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65617F61038001A4D7A /* readany.c */; }; + 8370B68017F61038001A4D7A /* readany2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65717F61038001A4D7A /* readany2.c */; }; + 8370B68117F61038001A4D7A /* readasy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65817F61038001A4D7A /* readasy.c */; }; + 8370B68217F61038001A4D7A /* readdsmf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65917F61038001A4D7A /* readdsmf.c */; }; + 8370B68317F61038001A4D7A /* readmtm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65A17F61038001A4D7A /* readmtm.c */; }; + 8370B68417F61038001A4D7A /* readokt.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65B17F61038001A4D7A /* readokt.c */; }; + 8370B68517F61038001A4D7A /* readokt2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65C17F61038001A4D7A /* readokt2.c */; }; + 8370B68617F61038001A4D7A /* readoldpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65D17F61038001A4D7A /* readoldpsm.c */; }; + 8370B68717F61038001A4D7A /* readpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65E17F61038001A4D7A /* readpsm.c */; }; + 8370B68817F61038001A4D7A /* readptm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65F17F61038001A4D7A /* readptm.c */; }; + 8370B68917F61038001A4D7A /* readriff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66017F61038001A4D7A /* readriff.c */; }; + 8370B68A17F61038001A4D7A /* readstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66117F61038001A4D7A /* readstm.c */; }; + 8370B68B17F61038001A4D7A /* readstm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66217F61038001A4D7A /* readstm2.c */; }; + 8370B7E917F62A40001A4D7A /* blip_buf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B7E717F62A40001A4D7A /* blip_buf.h */; }; + 8370B7EA17F62A40001A4D7A /* lanczos_resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -103,6 +158,61 @@ 17C8F63B0CBEE797008D969D /* readxm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = readxm.c; sourceTree = ""; }; 17C8F63C0CBEE797008D969D /* readxm2.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = readxm2.c; sourceTree = ""; }; 17C8F63D0CBEE797008D969D /* xmeffect.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = xmeffect.c; sourceTree = ""; }; + 8370B61E17F60FE2001A4D7A /* barray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = barray.h; sourceTree = ""; }; + 8370B62017F60FE2001A4D7A /* dumbfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dumbfile.h; sourceTree = ""; }; + 8370B62217F60FE2001A4D7A /* lpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lpc.h; sourceTree = ""; }; + 8370B62317F60FE2001A4D7A /* riff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff.h; sourceTree = ""; }; + 8370B62417F60FE2001A4D7A /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = ""; }; + 8370B62517F60FE2001A4D7A /* tarray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tarray.h; sourceTree = ""; }; + 8370B62E17F61001001A4D7A /* barray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = barray.c; sourceTree = ""; }; + 8370B62F17F61001001A4D7A /* blip_buf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blip_buf.c; sourceTree = ""; }; + 8370B63017F61001001A4D7A /* lanczos_resampler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lanczos_resampler.c; sourceTree = ""; }; + 8370B63117F61001001A4D7A /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = ""; }; + 8370B63217F61001001A4D7A /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = ""; }; + 8370B63317F61001001A4D7A /* tarray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tarray.c; sourceTree = ""; }; + 8370B63A17F61038001A4D7A /* load669.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = load669.c; sourceTree = ""; }; + 8370B63B17F61038001A4D7A /* load6692.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = load6692.c; sourceTree = ""; }; + 8370B63C17F61038001A4D7A /* loadamf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadamf.c; sourceTree = ""; }; + 8370B63D17F61038001A4D7A /* loadamf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadamf2.c; sourceTree = ""; }; + 8370B63E17F61038001A4D7A /* loadany.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadany.c; sourceTree = ""; }; + 8370B63F17F61038001A4D7A /* loadany2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadany2.c; sourceTree = ""; }; + 8370B64017F61038001A4D7A /* loadasy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadasy.c; sourceTree = ""; }; + 8370B64117F61038001A4D7A /* loadasy2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadasy2.c; sourceTree = ""; }; + 8370B64217F61038001A4D7A /* loadmtm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadmtm.c; sourceTree = ""; }; + 8370B64317F61038001A4D7A /* loadmtm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadmtm2.c; sourceTree = ""; }; + 8370B64417F61038001A4D7A /* loadokt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadokt.c; sourceTree = ""; }; + 8370B64517F61038001A4D7A /* loadokt2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadokt2.c; sourceTree = ""; }; + 8370B64617F61038001A4D7A /* loadoldpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadoldpsm.c; sourceTree = ""; }; + 8370B64717F61038001A4D7A /* loadoldpsm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadoldpsm2.c; sourceTree = ""; }; + 8370B64817F61038001A4D7A /* loadpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadpsm.c; sourceTree = ""; }; + 8370B64917F61038001A4D7A /* loadpsm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadpsm2.c; sourceTree = ""; }; + 8370B64A17F61038001A4D7A /* loadptm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadptm.c; sourceTree = ""; }; + 8370B64B17F61038001A4D7A /* loadptm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadptm2.c; sourceTree = ""; }; + 8370B64C17F61038001A4D7A /* loadriff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadriff.c; sourceTree = ""; }; + 8370B64D17F61038001A4D7A /* loadriff2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadriff2.c; sourceTree = ""; }; + 8370B64E17F61038001A4D7A /* loadstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadstm.c; sourceTree = ""; }; + 8370B64F17F61038001A4D7A /* loadstm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadstm2.c; sourceTree = ""; }; + 8370B65017F61038001A4D7A /* ptmeffect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ptmeffect.c; sourceTree = ""; }; + 8370B65117F61038001A4D7A /* read669.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read669.c; sourceTree = ""; }; + 8370B65217F61038001A4D7A /* read6692.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read6692.c; sourceTree = ""; }; + 8370B65317F61038001A4D7A /* readam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readam.c; sourceTree = ""; }; + 8370B65417F61038001A4D7A /* readamf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readamf.c; sourceTree = ""; }; + 8370B65517F61038001A4D7A /* readamf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readamf2.c; sourceTree = ""; }; + 8370B65617F61038001A4D7A /* readany.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readany.c; sourceTree = ""; }; + 8370B65717F61038001A4D7A /* readany2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readany2.c; sourceTree = ""; }; + 8370B65817F61038001A4D7A /* readasy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readasy.c; sourceTree = ""; }; + 8370B65917F61038001A4D7A /* readdsmf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readdsmf.c; sourceTree = ""; }; + 8370B65A17F61038001A4D7A /* readmtm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readmtm.c; sourceTree = ""; }; + 8370B65B17F61038001A4D7A /* readokt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readokt.c; sourceTree = ""; }; + 8370B65C17F61038001A4D7A /* readokt2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readokt2.c; sourceTree = ""; }; + 8370B65D17F61038001A4D7A /* readoldpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readoldpsm.c; sourceTree = ""; }; + 8370B65E17F61038001A4D7A /* readpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readpsm.c; sourceTree = ""; }; + 8370B65F17F61038001A4D7A /* readptm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readptm.c; sourceTree = ""; }; + 8370B66017F61038001A4D7A /* readriff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readriff.c; sourceTree = ""; }; + 8370B66117F61038001A4D7A /* readstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readstm.c; sourceTree = ""; }; + 8370B66217F61038001A4D7A /* readstm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readstm2.c; sourceTree = ""; }; + 8370B7E717F62A40001A4D7A /* blip_buf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blip_buf.h; sourceTree = ""; }; + 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lanczos_resampler.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* Dumb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dumb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -197,6 +307,14 @@ 17C8F60D0CBEE797008D969D /* internal */ = { isa = PBXGroup; children = ( + 8370B7E717F62A40001A4D7A /* blip_buf.h */, + 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */, + 8370B61E17F60FE2001A4D7A /* barray.h */, + 8370B62017F60FE2001A4D7A /* dumbfile.h */, + 8370B62217F60FE2001A4D7A /* lpc.h */, + 8370B62317F60FE2001A4D7A /* riff.h */, + 8370B62417F60FE2001A4D7A /* stack_alloc.h */, + 8370B62517F60FE2001A4D7A /* tarray.h */, 17C8F60E0CBEE797008D969D /* dumb.h */, 17C8F60F0CBEE797008D969D /* it.h */, ); @@ -236,6 +354,12 @@ 17C8F61E0CBEE797008D969D /* helpers */ = { isa = PBXGroup; children = ( + 8370B62E17F61001001A4D7A /* barray.c */, + 8370B62F17F61001001A4D7A /* blip_buf.c */, + 8370B63017F61001001A4D7A /* lanczos_resampler.c */, + 8370B63117F61001001A4D7A /* lpc.c */, + 8370B63217F61001001A4D7A /* riff.c */, + 8370B63317F61001001A4D7A /* tarray.c */, 17C8F61F0CBEE797008D969D /* clickrem.c */, 17C8F6200CBEE797008D969D /* memfile.c */, 17C8F6210CBEE797008D969D /* resamp2.inc */, @@ -252,6 +376,47 @@ 17C8F6280CBEE797008D969D /* it */ = { isa = PBXGroup; children = ( + 8370B63A17F61038001A4D7A /* load669.c */, + 8370B63B17F61038001A4D7A /* load6692.c */, + 8370B63C17F61038001A4D7A /* loadamf.c */, + 8370B63D17F61038001A4D7A /* loadamf2.c */, + 8370B63E17F61038001A4D7A /* loadany.c */, + 8370B63F17F61038001A4D7A /* loadany2.c */, + 8370B64017F61038001A4D7A /* loadasy.c */, + 8370B64117F61038001A4D7A /* loadasy2.c */, + 8370B64217F61038001A4D7A /* loadmtm.c */, + 8370B64317F61038001A4D7A /* loadmtm2.c */, + 8370B64417F61038001A4D7A /* loadokt.c */, + 8370B64517F61038001A4D7A /* loadokt2.c */, + 8370B64617F61038001A4D7A /* loadoldpsm.c */, + 8370B64717F61038001A4D7A /* loadoldpsm2.c */, + 8370B64817F61038001A4D7A /* loadpsm.c */, + 8370B64917F61038001A4D7A /* loadpsm2.c */, + 8370B64A17F61038001A4D7A /* loadptm.c */, + 8370B64B17F61038001A4D7A /* loadptm2.c */, + 8370B64C17F61038001A4D7A /* loadriff.c */, + 8370B64D17F61038001A4D7A /* loadriff2.c */, + 8370B64E17F61038001A4D7A /* loadstm.c */, + 8370B64F17F61038001A4D7A /* loadstm2.c */, + 8370B65017F61038001A4D7A /* ptmeffect.c */, + 8370B65117F61038001A4D7A /* read669.c */, + 8370B65217F61038001A4D7A /* read6692.c */, + 8370B65317F61038001A4D7A /* readam.c */, + 8370B65417F61038001A4D7A /* readamf.c */, + 8370B65517F61038001A4D7A /* readamf2.c */, + 8370B65617F61038001A4D7A /* readany.c */, + 8370B65717F61038001A4D7A /* readany2.c */, + 8370B65817F61038001A4D7A /* readasy.c */, + 8370B65917F61038001A4D7A /* readdsmf.c */, + 8370B65A17F61038001A4D7A /* readmtm.c */, + 8370B65B17F61038001A4D7A /* readokt.c */, + 8370B65C17F61038001A4D7A /* readokt2.c */, + 8370B65D17F61038001A4D7A /* readoldpsm.c */, + 8370B65E17F61038001A4D7A /* readpsm.c */, + 8370B65F17F61038001A4D7A /* readptm.c */, + 8370B66017F61038001A4D7A /* readriff.c */, + 8370B66117F61038001A4D7A /* readstm.c */, + 8370B66217F61038001A4D7A /* readstm2.c */, 17C8F6290CBEE797008D969D /* itload.c */, 17C8F62A0CBEE797008D969D /* itload2.c */, 17C8F62B0CBEE797008D969D /* itmisc.c */, @@ -285,8 +450,16 @@ buildActionMask = 2147483647; files = ( 17C8F63E0CBEE797008D969D /* dumb.h in Headers */, - 17C8F63F0CBEE797008D969D /* dumb.h in Headers */, 17C8F6400CBEE797008D969D /* it.h in Headers */, + 8370B62D17F60FE2001A4D7A /* tarray.h in Headers */, + 8370B62617F60FE2001A4D7A /* barray.h in Headers */, + 8370B7EA17F62A40001A4D7A /* lanczos_resampler.h in Headers */, + 8370B7E917F62A40001A4D7A /* blip_buf.h in Headers */, + 17C8F63F0CBEE797008D969D /* dumb.h in Headers */, + 8370B62B17F60FE2001A4D7A /* riff.h in Headers */, + 8370B62A17F60FE2001A4D7A /* lpc.h in Headers */, + 8370B62817F60FE2001A4D7A /* dumbfile.h in Headers */, + 8370B62C17F60FE2001A4D7A /* stack_alloc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -317,9 +490,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Dumb" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Dumb */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -346,29 +525,56 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8370B66D17F61038001A4D7A /* loadokt.c in Sources */, + 8370B68A17F61038001A4D7A /* readstm.c in Sources */, 17C8F6410CBEE797008D969D /* atexit.c in Sources */, 17C8F6420CBEE797008D969D /* duhlen.c in Sources */, + 8370B66E17F61038001A4D7A /* loadokt2.c in Sources */, + 8370B63817F61001001A4D7A /* riff.c in Sources */, 17C8F6430CBEE797008D969D /* duhtag.c in Sources */, + 8370B63517F61001001A4D7A /* blip_buf.c in Sources */, + 8370B68117F61038001A4D7A /* readasy.c in Sources */, + 8370B67217F61038001A4D7A /* loadpsm2.c in Sources */, 17C8F6440CBEE797008D969D /* dumbfile.c in Sources */, + 8370B68317F61038001A4D7A /* readmtm.c in Sources */, 17C8F6450CBEE797008D969D /* loadduh.c in Sources */, + 8370B67D17F61038001A4D7A /* readamf.c in Sources */, + 8370B63717F61001001A4D7A /* lpc.c in Sources */, 17C8F6460CBEE797008D969D /* makeduh.c in Sources */, 17C8F6470CBEE797008D969D /* rawsig.c in Sources */, 17C8F6480CBEE797008D969D /* readduh.c in Sources */, + 8370B67917F61038001A4D7A /* ptmeffect.c in Sources */, 17C8F6490CBEE797008D969D /* register.c in Sources */, + 8370B67C17F61038001A4D7A /* readam.c in Sources */, 17C8F64A0CBEE797008D969D /* rendduh.c in Sources */, + 8370B63417F61001001A4D7A /* barray.c in Sources */, + 8370B67817F61038001A4D7A /* loadstm2.c in Sources */, + 8370B66A17F61038001A4D7A /* loadasy2.c in Sources */, + 8370B68717F61038001A4D7A /* readpsm.c in Sources */, + 8370B67B17F61038001A4D7A /* read6692.c in Sources */, 17C8F64B0CBEE797008D969D /* rendsig.c in Sources */, 17C8F64C0CBEE797008D969D /* unload.c in Sources */, 17C8F64D0CBEE797008D969D /* clickrem.c in Sources */, 17C8F64E0CBEE797008D969D /* memfile.c in Sources */, 17C8F6510CBEE797008D969D /* resample.c in Sources */, + 8370B66F17F61038001A4D7A /* loadoldpsm.c in Sources */, 17C8F6530CBEE797008D969D /* sampbuf.c in Sources */, 17C8F6540CBEE797008D969D /* silence.c in Sources */, 17C8F6550CBEE797008D969D /* stdfile.c in Sources */, + 8370B67417F61038001A4D7A /* loadptm2.c in Sources */, + 8370B68817F61038001A4D7A /* readptm.c in Sources */, + 8370B67317F61038001A4D7A /* loadptm.c in Sources */, + 8370B66417F61038001A4D7A /* load6692.c in Sources */, 17C8F6560CBEE797008D969D /* itload.c in Sources */, 17C8F6570CBEE797008D969D /* itload2.c in Sources */, + 8370B63617F61001001A4D7A /* lanczos_resampler.c in Sources */, 17C8F6580CBEE797008D969D /* itmisc.c in Sources */, + 8370B67517F61038001A4D7A /* loadriff.c in Sources */, + 8370B66917F61038001A4D7A /* loadasy.c in Sources */, 17C8F6590CBEE797008D969D /* itorder.c in Sources */, + 8370B66317F61038001A4D7A /* load669.c in Sources */, 17C8F65A0CBEE797008D969D /* itread.c in Sources */, + 8370B68B17F61038001A4D7A /* readstm2.c in Sources */, 17C8F65B0CBEE797008D969D /* itread2.c in Sources */, 17C8F65C0CBEE797008D969D /* itrender.c in Sources */, 17C8F65D0CBEE797008D969D /* itunload.c in Sources */, @@ -376,14 +582,34 @@ 17C8F65F0CBEE797008D969D /* loadmod2.c in Sources */, 17C8F6600CBEE797008D969D /* loads3m.c in Sources */, 17C8F6610CBEE797008D969D /* loads3m2.c in Sources */, + 8370B63917F61001001A4D7A /* tarray.c in Sources */, + 8370B66B17F61038001A4D7A /* loadmtm.c in Sources */, + 8370B68517F61038001A4D7A /* readokt2.c in Sources */, 17C8F6620CBEE797008D969D /* loadxm.c in Sources */, 17C8F6630CBEE797008D969D /* loadxm2.c in Sources */, + 8370B67117F61038001A4D7A /* loadpsm.c in Sources */, + 8370B67617F61038001A4D7A /* loadriff2.c in Sources */, + 8370B66C17F61038001A4D7A /* loadmtm2.c in Sources */, + 8370B67A17F61038001A4D7A /* read669.c in Sources */, + 8370B66717F61038001A4D7A /* loadany.c in Sources */, + 8370B68017F61038001A4D7A /* readany2.c in Sources */, 17C8F6640CBEE797008D969D /* readmod.c in Sources */, + 8370B67017F61038001A4D7A /* loadoldpsm2.c in Sources */, + 8370B68217F61038001A4D7A /* readdsmf.c in Sources */, 17C8F6650CBEE797008D969D /* readmod2.c in Sources */, + 8370B68617F61038001A4D7A /* readoldpsm.c in Sources */, 17C8F6660CBEE797008D969D /* reads3m.c in Sources */, 17C8F6670CBEE797008D969D /* reads3m2.c in Sources */, 17C8F6680CBEE797008D969D /* readxm.c in Sources */, + 8370B68917F61038001A4D7A /* readriff.c in Sources */, 17C8F6690CBEE797008D969D /* readxm2.c in Sources */, + 8370B66617F61038001A4D7A /* loadamf2.c in Sources */, + 8370B68417F61038001A4D7A /* readokt.c in Sources */, + 8370B66817F61038001A4D7A /* loadany2.c in Sources */, + 8370B66517F61038001A4D7A /* loadamf.c in Sources */, + 8370B67717F61038001A4D7A /* loadstm.c in Sources */, + 8370B67E17F61038001A4D7A /* readamf2.c in Sources */, + 8370B67F17F61038001A4D7A /* readany.c in Sources */, 17C8F66A0CBEE797008D969D /* xmeffect.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -421,6 +647,7 @@ OBJROOT = ../../build; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; @@ -448,6 +675,7 @@ OBJROOT = ../../build; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme new file mode 100644 index 000000000..92fd667c2 --- /dev/null +++ b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..50d544562 --- /dev/null +++ b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Dumb Framework.xcscheme + + orderHint + 10 + + + SuppressBuildableAutocreation + + 8DC2EF4F0486A6940098B216 + + primary + + + + + diff --git a/Frameworks/Dumb/dumb/include/dumb.h b/Frameworks/Dumb/dumb/include/dumb.h index 2789d7ee3..77d8fc1e1 100644 --- a/Frameworks/Dumb/dumb/include/dumb.h +++ b/Frameworks/Dumb/dumb/include/dumb.h @@ -1,687 +1,784 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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 - - -#ifdef __cplusplus - extern "C" { -#endif - - -#define DUMB_MAJOR_VERSION 0 -#define DUMB_MINOR_VERSION 9 -#define DUMB_REVISION_VERSION 3 - -#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) - -#define DUMB_VERSION_STR "0.9.3" - -#define DUMB_NAME "DUMB v"DUMB_VERSION_STR - -#define DUMB_YEAR 2005 -#define DUMB_MONTH 8 -#define DUMB_DAY 7 - -#define DUMB_YEAR_STR2 "05" -#define DUMB_YEAR_STR4 "2005" -#define DUMB_MONTH_STR1 "8" -#define DUMB_DAY_STR1 "7" - -#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) )) - - - -#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 -#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 - - -/* 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 *(*open)(const char *filename); - int (*skip)(void *f, long n); - int (*getc)(void *f); - long (*getnc)(char *ptr, long n, void *f); - void (*close)(void *f); -} -DUMBFILE_SYSTEM; - -typedef struct DUMBFILE DUMBFILE; - -void register_dumbfile_system(DUMBFILE_SYSTEM *dfs); - -DUMBFILE *dumbfile_open(const char *filename); -DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs); - -long dumbfile_pos(DUMBFILE *f); -int dumbfile_skip(DUMBFILE *f, long n); - -int dumbfile_getc(DUMBFILE *f); - -int dumbfile_igetw(DUMBFILE *f); -int dumbfile_mgetw(DUMBFILE *f); - -long dumbfile_igetl(DUMBFILE *f); -long dumbfile_mgetl(DUMBFILE *f); - -unsigned long dumbfile_cgetul(DUMBFILE *f); -signed long dumbfile_cgetsl(DUMBFILE *f); - -long dumbfile_getnc(char *ptr, long n, DUMBFILE *f); - -int dumbfile_error(DUMBFILE *f); -int dumbfile_close(DUMBFILE *f); - - -/* stdio File Input Module */ - -void dumb_register_stdfiles(void); - -DUMBFILE *dumbfile_open_stdfile(FILE *p); - - -/* Memory File Input Module */ - -DUMBFILE *dumbfile_open_memory(const char *data, long size); - - -/* DUH Management */ - -typedef struct DUH DUH; - -#define DUH_SIGNATURE DUMB_ID('D','U','H','!') - -void unload_duh(DUH *duh); - -DUH *load_duh(const char *filename); -DUH *read_duh(DUMBFILE *f); - -long duh_get_length(DUH *duh); - -const char *duh_get_tag(DUH *duh, const char *key); - - -/* Signal Rendering Functions */ - -typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; - -DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos); - -#ifdef DUMB_DECLARE_DEPRECATED -typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long 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, long 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, long length); - -void duh_sigrenderer_set_sample_analyser_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data -); - -int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); -long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); - -void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value); - -#ifdef DUMB_DECLARE_DEPRECATED -long duh_sigrenderer_get_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long 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 - -long duh_sigrenderer_generate_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -); - -void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples); - -void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); - - -/* DUH Rendering Functions */ - -long duh_render( - DUH_SIGRENDERER *sigrenderer, - int bits, int unsign, - float volume, float delta, - long size, void *sptr -); - -#ifdef DUMB_DECLARE_DEPRECATED - -long duh_render_signal( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long 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, long pos) DUMB_DEPRECATED; -/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */ - -int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -long 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 *duh_get_it_sigdata(DUH *duh); -DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos); -DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); - -DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder); - -void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); -void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); -void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data); - -int dumb_it_callback_terminate(void *data); -int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte); - -DUH *dumb_load_it(const char *filename); -DUH *dumb_load_xm(const char *filename); -DUH *dumb_load_s3m(const char *filename); -DUH *dumb_load_mod(const char *filename); - -DUH *dumb_read_it(DUMBFILE *f); -DUH *dumb_read_xm(DUMBFILE *f); -DUH *dumb_read_s3m(DUMBFILE *f); -DUH *dumb_read_mod(DUMBFILE *f); - -DUH *dumb_load_it_quick(const char *filename); -DUH *dumb_load_xm_quick(const char *filename); -DUH *dumb_load_s3m_quick(const char *filename); -DUH *dumb_load_mod_quick(const char *filename); - -DUH *dumb_read_it_quick(DUMBFILE *f); -DUH *dumb_read_xm_quick(DUMBFILE *f); -DUH *dumb_read_s3m_quick(DUMBFILE *f); -DUH *dumb_read_mod_quick(DUMBFILE *f); - -long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata); -void dumb_it_do_initial_runthrough(DUH *duh); - -const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd); - -int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd); -int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd); -int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd); - -const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i); - -int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv); - -int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv); - -int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed); - -int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo); - -int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel); -void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume); - -int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr); -int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr); - -int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr); -void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv); - -int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr); -void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo); - -int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr); -void 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 dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel); -void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume); - -int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel); -void 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 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, - long pos -); - -typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)( - sigrenderer_t *sigrenderer, - unsigned char id, long value -); - -typedef long (*DUH_SIGRENDERER_GENERATE_SAMPLES)( - sigrenderer_t *sigrenderer, - float volume, float delta, - long size, sample_t **samples -); - -typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( - sigrenderer_t *sigrenderer, - float 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 -{ - long 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 dumb_register_sigtype(DUH_SIGTYPE_DESC *desc); - - -// Decide where to put these functions; new heading? - -sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type); - -DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos); -sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type); - - -/* 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, long 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 **allocate_sample_buffer(int n_channels, long length); -void destroy_sample_buffer(sample_t **samples); - - -/* Silencing Helper */ - -void dumb_silence(sample_t *samples, long length); - - -/* Click Removal Helpers */ - -typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER; - -DUMB_CLICK_REMOVER *dumb_create_click_remover(void); -void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step); -void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife); -sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr); -void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr); - -DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n); -void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); -void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); -void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife); -void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset); -void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr); - - -/* Resampling Helpers */ - -#define DUMB_RQ_ALIASING 0 -#define DUMB_RQ_LINEAR 1 -#define DUMB_RQ_CUBIC 2 -#define DUMB_RQ_N_LEVELS 3 -extern int dumb_resampling_quality; - -typedef struct DUMB_RESAMPLER DUMB_RESAMPLER; - -typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data); - -struct DUMB_RESAMPLER -{ - void *src; - long pos; - int subpos; - long start, end; - int dir; - DUMB_RESAMPLE_PICKUP pickup; - void *pickup_data; - int min_quality; - int max_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; -}; - -void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, long pos, long start, long end); -long dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_2_2(DUMB_RESAMPLER *resampler, float volume_left, float 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, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, long pos, long start, long end); -long dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_16_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_16_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_16_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_16_2_2(DUMB_RESAMPLER *resampler, float volume_left, float 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, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, long pos, long start, long end); -long dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_8_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_8_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_8_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_8_2_2(DUMB_RESAMPLER *resampler, float volume_left, float 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, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end); -long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler); - - -/* DUH Construction */ - -DUH *make_duh( - long length, - int n_tags, - const char *const tag[][2], - int n_signals, - DUH_SIGTYPE_DESC *desc[], - sigdata_t *sigdata[] -); - -void duh_set_length(DUH *duh, long length); - - -#ifdef __cplusplus - } -#endif - - -#endif /* DUMB_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + +#define DUMB_MAJOR_VERSION 0 +#define DUMB_MINOR_VERSION 9 +#define DUMB_REVISION_VERSION 3 + +#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) + +#define DUMB_VERSION_STR "0.9.3" + +#define DUMB_NAME "DUMB v" DUMB_VERSION_STR + +#define DUMB_YEAR 2005 +#define DUMB_MONTH 8 +#define DUMB_DAY 7 + +#define DUMB_YEAR_STR2 "05" +#define DUMB_YEAR_STR4 "2005" +#define DUMB_MONTH_STR1 "8" +#define DUMB_DAY_STR1 "7" + +#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) )) + + + +#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 +#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 + + +/* 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 *(*open)(const char *filename); + int (*skip)(void *f, long n); + int (*getc)(void *f); + long (*getnc)(char *ptr, long n, void *f); + void (*close)(void *f); + int (*seek)(void *f, long n); + long (*get_size)(void *f); +} +DUMBFILE_SYSTEM; + +typedef struct DUMBFILE DUMBFILE; + +void register_dumbfile_system(const DUMBFILE_SYSTEM *dfs); + +DUMBFILE *dumbfile_open(const char *filename); +DUMBFILE *dumbfile_open_ex(void *file, const DUMBFILE_SYSTEM *dfs); + +long dumbfile_pos(DUMBFILE *f); +int dumbfile_skip(DUMBFILE *f, long n); + +#define DFS_SEEK_SET 0 +#define DFS_SEEK_CUR 1 +#define DFS_SEEK_END 2 + +int dumbfile_seek(DUMBFILE *f, long n, int origin); + +long dumbfile_get_size(DUMBFILE *f); + +int dumbfile_getc(DUMBFILE *f); + +int dumbfile_igetw(DUMBFILE *f); +int dumbfile_mgetw(DUMBFILE *f); + +long dumbfile_igetl(DUMBFILE *f); +long dumbfile_mgetl(DUMBFILE *f); + +unsigned long dumbfile_cgetul(DUMBFILE *f); +signed long dumbfile_cgetsl(DUMBFILE *f); + +long dumbfile_getnc(char *ptr, long n, DUMBFILE *f); + +int dumbfile_error(DUMBFILE *f); +int dumbfile_close(DUMBFILE *f); + + +/* stdio File Input Module */ + +void dumb_register_stdfiles(void); + +DUMBFILE *dumbfile_open_stdfile(FILE *p); + + +/* Memory File Input Module */ + +DUMBFILE *dumbfile_open_memory(const char *data, long size); + + +/* DUH Management */ + +typedef struct DUH DUH; + +#define DUH_SIGNATURE DUMB_ID('D','U','H','!') + +void unload_duh(DUH *duh); + +DUH *load_duh(const char *filename); +DUH *read_duh(DUMBFILE *f); + +long duh_get_length(DUH *duh); + +const char *duh_get_tag(DUH *duh, const char *key); + +/* Signal Rendering Functions */ + +typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; + +DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos); + +#ifdef DUMB_DECLARE_DEPRECATED +typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long 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, long 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, long length); + +void duh_sigrenderer_set_sample_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data +); + +int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); +long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); + +void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value); + +#ifdef DUMB_DECLARE_DEPRECATED +long duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long 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 + +long duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +); + +void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples); + +void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); + + +/* DUH Rendering Functions */ + +long duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + long size, void *sptr +); + +#ifdef DUMB_DECLARE_DEPRECATED + +long duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long 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, long pos) DUMB_DEPRECATED; +/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */ + +int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +long 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 *duh_get_it_sigdata(DUH *duh); +DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos); +DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); + +int dumb_it_trim_silent_patterns(DUH * duh); + +typedef int (*dumb_scan_callback)(void *, int, long); +int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data); + +DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder); + +void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality); + +void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style); + +void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); +void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); +void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data); +void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); + +int dumb_it_callback_terminate(void *data); +int 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 *dumb_load_it(const char *filename); +DUH *dumb_load_xm(const char *filename); +DUH *dumb_load_s3m(const char *filename); +DUH *dumb_load_stm(const char *filename); +DUH *dumb_load_mod(const char *filename, int restrict_); +DUH *dumb_load_ptm(const char *filename); +DUH *dumb_load_669(const char *filename); +DUH *dumb_load_psm(const char *filename, int subsong); +DUH *dumb_load_old_psm(const char * filename); +DUH *dumb_load_mtm(const char *filename); +DUH *dumb_load_riff(const char *filename); +DUH *dumb_load_asy(const char *filename); +DUH *dumb_load_amf(const char *filename); +DUH *dumb_load_okt(const char *filename); + +DUH *dumb_read_it(DUMBFILE *f); +DUH *dumb_read_xm(DUMBFILE *f); +DUH *dumb_read_s3m(DUMBFILE *f); +DUH *dumb_read_stm(DUMBFILE *f); +DUH *dumb_read_mod(DUMBFILE *f, int restrict_); +DUH *dumb_read_ptm(DUMBFILE *f); +DUH *dumb_read_669(DUMBFILE *f); +DUH *dumb_read_psm(DUMBFILE *f, int subsong); +DUH *dumb_read_old_psm(DUMBFILE *f); +DUH *dumb_read_mtm(DUMBFILE *f); +DUH *dumb_read_riff(DUMBFILE *f); +DUH *dumb_read_asy(DUMBFILE *f); +DUH *dumb_read_amf(DUMBFILE *f); +DUH *dumb_read_okt(DUMBFILE *f); + +DUH *dumb_load_it_quick(const char *filename); +DUH *dumb_load_xm_quick(const char *filename); +DUH *dumb_load_s3m_quick(const char *filename); +DUH *dumb_load_stm_quick(const char *filename); +DUH *dumb_load_mod_quick(const char *filename, int restrict_); +DUH *dumb_load_ptm_quick(const char *filename); +DUH *dumb_load_669_quick(const char *filename); +DUH *dumb_load_psm_quick(const char *filename, int subsong); +DUH *dumb_load_old_psm_quick(const char * filename); +DUH *dumb_load_mtm_quick(const char *filename); +DUH *dumb_load_riff_quick(const char *filename); +DUH *dumb_load_asy_quick(const char *filename); +DUH *dumb_load_amf_quick(const char *filename); +DUH *dumb_load_okt_quick(const char *filename); + +DUH *dumb_read_it_quick(DUMBFILE *f); +DUH *dumb_read_xm_quick(DUMBFILE *f); +DUH *dumb_read_s3m_quick(DUMBFILE *f); +DUH *dumb_read_stm_quick(DUMBFILE *f); +DUH *dumb_read_mod_quick(DUMBFILE *f, int restrict_); +DUH *dumb_read_ptm_quick(DUMBFILE *f); +DUH *dumb_read_669_quick(DUMBFILE *f); +DUH *dumb_read_psm_quick(DUMBFILE *f, int subsong); +DUH *dumb_read_old_psm_quick(DUMBFILE *f); +DUH *dumb_read_mtm_quick(DUMBFILE *f); +DUH *dumb_read_riff_quick(DUMBFILE *f); +DUH *dumb_read_asy_quick(DUMBFILE *f); +DUH *dumb_read_amf_quick(DUMBFILE *f); +DUH *dumb_read_okt_quick(DUMBFILE *f); + +DUH *dumb_read_any_quick(DUMBFILE *f, int restrict_, int subsong); +DUH *dumb_read_any(DUMBFILE *f, int restrict_, int subsong); + +DUH *dumb_load_any_quick(const char *filename, int restrict_, int subsong); +DUH *dumb_load_any(const char *filename, int restrict_, int subsong); + +long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder); +void dumb_it_do_initial_runthrough(DUH *duh); + +int dumb_get_psm_subsong_count(DUMBFILE *f); + +const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd); + +int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd); +int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd); +int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd); + +const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i); + +int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv); + +int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv); + +int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed); + +int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo); + +int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel); +void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume); + +int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr); +int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr); + +int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr); +void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv); + +int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr); +void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo); + +int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr); +void 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 dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel); +void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume); + +int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel); +void 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 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, + long pos +); + +typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)( + sigrenderer_t *sigrenderer, + unsigned char id, long value +); + +typedef long (*DUH_SIGRENDERER_GENERATE_SAMPLES)( + sigrenderer_t *sigrenderer, + float volume, float delta, + long size, sample_t **samples +); + +typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( + sigrenderer_t *sigrenderer, + float volume, + sample_t *samples +); + +typedef long (*DUH_SIGRENDERER_GET_POSITION)( + sigrenderer_t *sigrenderer +); + +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 +{ + long 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_SIGRENDERER_GET_POSITION sigrenderer_get_position; + DUH_END_SIGRENDERER end_sigrenderer; + DUH_UNLOAD_SIGDATA unload_sigdata; +} +DUH_SIGTYPE_DESC; + +void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc); + +int duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata); + + +// Decide where to put these functions; new heading? + +sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type); + +DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos); +sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type); + + +/* 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, long 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 **allocate_sample_buffer(int n_channels, long length); +void destroy_sample_buffer(sample_t **samples); + + +/* Silencing Helper */ + +void dumb_silence(sample_t *samples, long length); + + +/* Click Removal Helpers */ + +typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER; + +DUMB_CLICK_REMOVER *dumb_create_click_remover(void); +void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step); +void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife); +sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr); +void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr); + +DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n); +void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); +void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); +void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife); +void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset); +void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr); + + +/* Resampling Helpers */ + +#define DUMB_RQ_ALIASING 0 +#define DUMB_RQ_LINEAR 1 +#define DUMB_RQ_CUBIC 2 +#define DUMB_RQ_FIR 3 +#define DUMB_RQ_N_LEVELS 4 +extern int dumb_resampling_quality; + +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); + +typedef struct blip_t blip_t; + +struct DUMB_RESAMPLER +{ + void *src; + long pos; + int subpos; + long 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; + int last_clock; + int last_amp[2]; + blip_t* blip_buffer[2]; + double fir_resampler_ratio; + void* fir_resampler[2]; +}; + +struct DUMB_VOLUME_RAMP_INFO +{ + float volume; + float delta; + float target; + float mix; +}; + +void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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); + + +/* DUH Construction */ + +DUH *make_duh( + long length, + int n_tags, + const char *const tag[][2], + int n_signals, + DUH_SIGTYPE_DESC *desc[], + sigdata_t *sigdata[] +); + +void duh_set_length(DUH *duh, long length); + + +#ifdef __cplusplus + } +#endif + + +#endif /* DUMB_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/aldumb.h b/Frameworks/Dumb/dumb/include/internal/aldumb.h new file mode 100644 index 000000000..9c02c01ff --- /dev/null +++ b/Frameworks/Dumb/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/Frameworks/Dumb/dumb/include/internal/barray.h b/Frameworks/Dumb/dumb/include/internal/barray.h new file mode 100644 index 000000000..53c9a6cf3 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/barray.h @@ -0,0 +1,20 @@ +#ifndef _B_ARRAY_H_ +#define _B_ARRAY_H_ + +#include + +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); +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_merge(void * array, void * source, size_t offset); +void bit_array_mask(void * array, void * source, size_t offset); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/blip_buf.h b/Frameworks/Dumb/dumb/include/internal/blip_buf.h new file mode 100644 index 000000000..6888fa37a --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/blip_buf.h @@ -0,0 +1,77 @@ +/** \file +Sample buffer that resamples from input clock rate to output sample rate */ + +/* blip_buf 1.1.0 */ +#ifndef BLIP_BUF_H +#define BLIP_BUF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** First parameter of most functions is blip_t*, or const blip_t* if nothing +is changed. */ +typedef struct blip_t blip_t; + +/** Creates new buffer that can hold at most sample_count samples. Sets rates +so that there are blip_max_ratio clocks per sample. Returns pointer to new +buffer, or NULL if insufficient memory. */ +blip_t* blip_new( int sample_count ); + +blip_t* blip_dup( blip_t* ); + +/** Sets approximate input clock rate and output sample rate. For every +clock_rate input clocks, approximately sample_rate samples are generated. */ +void blip_set_rates( blip_t*, double clock_rate, double sample_rate ); + +enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate, +clock_rate must not be greater than sample_rate*blip_max_ratio. */ +blip_max_ratio = 1 << 20 }; + +/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ +void blip_clear( blip_t* ); + +/** Adds positive/negative delta into buffer at specified clock time. */ +void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); + +/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ +void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); + +/** Length of time frame, in clocks, needed to make sample_count additional +samples available. */ +int blip_clocks_needed( const blip_t*, int sample_count ); + +enum { /** Maximum number of samples that can be generated from one time frame. */ +blip_max_frame = 4000 }; + +/** Makes input clocks before clock_duration available for reading as output +samples. Also begins new time frame at clock_duration, so that clock time 0 in +the new time frame specifies the same clock as clock_duration in the old time +frame specified. Deltas can have been added slightly past clock_duration (up to +however many clocks there are in two output samples). */ +void blip_end_frame( blip_t*, unsigned int clock_duration ); + +/** Number of buffered samples available for reading. */ +int blip_samples_avail( const blip_t* ); + +/** Reads and removes at most 'count' samples and writes them to 'out'. If +'stereo' is true, writes output to every other element of 'out', allowing easy +interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed +samples. Returns number of samples actually read. */ +int blip_read_samples( blip_t*, int out [], int count ); + +/** Reads the current integrator and returns it */ +int blip_peek_sample( blip_t* ); + +/** Frees buffer. No effect if NULL is passed. */ +void blip_delete( blip_t* ); + + +/* Deprecated */ +typedef blip_t blip_buffer_t; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/dumb.h b/Frameworks/Dumb/dumb/include/internal/dumb.h index 99823f15c..bed595666 100644 --- a/Frameworks/Dumb/dumb/include/internal/dumb.h +++ b/Frameworks/Dumb/dumb/include/internal/dumb.h @@ -1,61 +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 -{ - long length; - - int n_tags; - char *(*tag)[2]; - - int n_signals; - DUH_SIGNAL **signal; -}; - - -DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type); - - -#endif /* INTERNAL_DUMB_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 +{ + long length; + + int n_tags; + char *(*tag)[2]; + + int n_signals; + DUH_SIGNAL **signal; +}; + + +DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type); + + +#endif /* INTERNAL_DUMB_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/dumbfile.h b/Frameworks/Dumb/dumb/include/internal/dumbfile.h new file mode 100644 index 000000000..c83cc9a00 --- /dev/null +++ b/Frameworks/Dumb/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/Frameworks/Dumb/dumb/include/internal/fir_resampler.h b/Frameworks/Dumb/dumb/include/internal/fir_resampler.h new file mode 100644 index 000000000..140eefb39 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/fir_resampler.h @@ -0,0 +1,18 @@ +#ifndef _FIR_RESAMPLER_H_ +#define _FIR_RESAMPLER_H_ + +void fir_init(); + +void * fir_resampler_create(); +void fir_resampler_delete(void *); +void * fir_resampler_dup(void *); + +int fir_resampler_get_free_count(void *); +void fir_resampler_write_sample(void *, short sample); +void fir_resampler_set_rate( void *, double new_factor ); +int fir_resampler_ready(void *); +void fir_resampler_clear(void *); +int fir_resampler_get_sample(void *); +void fir_resampler_remove_sample(void *); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/it.h b/Frameworks/Dumb/dumb/include/internal/it.h index 0fbbeacd0..8ca5fada6 100644 --- a/Frameworks/Dumb/dumb/include/internal/it.h +++ b/Frameworks/Dumb/dumb/include/internal/it.h @@ -1,733 +1,927 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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 - - - -#include - - - -/** 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 INVALID_NOTES_CAUSE_NOTE_CUT -#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') - - - -/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get - * the interval between ticks. - */ -#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6)) - - - -/* 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)) - - - -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_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; - unsigned 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]; -}; - - - -#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 /* Ramp down */ -#define IT_VIBRATO_SQUARE 2 -#define IT_VIBRATO_RANDOM 3 - -struct IT_SAMPLE -{ - unsigned char name[29]; - unsigned char filename[14]; - 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 - */ - - long length; - long loop_start; - long loop_end; - long C5_speed; - long sus_loop_start; - long sus_loop_end; - - unsigned char vibrato_speed; - unsigned char vibrato_depth; - unsigned char vibrato_rate; - unsigned char vibrato_waveform; - - void *data; -}; - - - -#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 - -#define IT_N_EFFECTS 34 - -/* 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_ORDER_END 255 -#define IT_ORDER_SKIP 254 - -struct DUMB_IT_SIGDATA -{ - unsigned char name[29]; - - unsigned char *song_message; - - int n_orders; - int n_instruments; - int n_samples; - int n_patterns; - - 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 - -struct IT_PLAYING -{ - int flags; - - IT_CHANNEL *channel; - IT_SAMPLE *sample; - IT_INSTRUMENT *instrument; - IT_INSTRUMENT *env_instrument; - - unsigned short sampnum; - unsigned char instnum; - - unsigned char channel_volume; - - unsigned char volume; - unsigned short pan; - - unsigned char note; - - 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 tremolo_speed; - unsigned char tremolo_depth; - unsigned char tremolo_time; - - unsigned char sample_vibrato_time; - int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */ - - int slide; - float delta; - - 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. - */ - long time_lost; -}; - - - -#define IT_CHANNEL_MUTED 1 - -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; - - int arpeggio; - 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. */ - - int portamento; - int toneporta; - unsigned char destnote; - - /** 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 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; - - IT_PLAYING *playing; -}; - - - -struct DUMB_IT_SIGRENDERER -{ - DUMB_IT_SIGDATA *sigdata; - - int n_channels; - - unsigned char globalvolume; - signed char globalvolslide; - - unsigned char 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 pat_loop_row; - - int n_rows; - - IT_ENTRY *entry_start; - IT_ENTRY *entry; - IT_ENTRY *entry_end; - - long time_left; /* Time before the next tick is processed */ - int sub_time_left; - - DUMB_CLICK_REMOVER **click_remover; - - IT_CALLBACKS *callbacks; -}; - - - -struct IT_CHECKPOINT -{ - IT_CHECKPOINT *next; - long time; - DUMB_IT_SIGRENDERER *sigrenderer; -}; - - - -struct IT_CALLBACKS -{ - int (*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 (*xm_speed_zero)(void *data); - void *xm_speed_zero_data; - /* Return 1 to terminate the mod, without letting samples fade. */ - - int (*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. - */ -}; - - - -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_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_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 _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata); - - - -#endif /* INTERNAL_IT_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 + +#ifdef __FRAMEWORK__ +#include +#include +#else +#include "barray.h" +#include "tarray.h" +#endif + + +/** 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') + + +/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get + * the interval between ticks. + */ +#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6)) + + + +/* 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 + */ + + long length; + long loop_start; + long loop_end; + long C5_speed; + long sus_loop_start; + long 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 declick_volume; + + 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. + */ + long time_lost; + + //int output; +}; + + + +#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; + + long 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; + + /* + Loop indicator for internal processes, may also be useful for external processes + 0 - Not looped + 1 - Looped + -1 - Continued past loop + */ + int looped; + + /* + Kept until looped + */ + LONG_LONG time_played; + + void * row_timekeeper; +#endif + + long gvz_time; + int gvz_sub_time; + + int ramp_style; + + //int max_output; +}; + + + +struct IT_CHECKPOINT +{ + IT_CHECKPOINT *next; + long time; + DUMB_IT_SIGRENDERER *sigrenderer; +}; + + + +struct IT_CALLBACKS +{ + int (*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 (*xm_speed_zero)(void *data); + void *xm_speed_zero_data; + /* Return 1 to terminate the mod, without letting samples fade. */ + + int (*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 (*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); + +long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f); + +void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample); + +#endif /* INTERNAL_IT_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h b/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h new file mode 100644 index 000000000..a691697f0 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h @@ -0,0 +1,19 @@ +#ifndef _LANCZOS_RESAMPLER_H_ +#define _LANCZOS_RESAMPLER_H_ + +void lanczos_init(); + +void * lanczos_resampler_create(); +void lanczos_resampler_delete(void *); +void * lanczos_resampler_dup(void *); + +int lanczos_resampler_get_free_count(void *); +void lanczos_resampler_write_sample(void *, short sample); +void lanczos_resampler_set_rate( void *, double new_factor ); +int lanczos_resampler_ready(void *); +void lanczos_resampler_clear(void *); +int lanczos_resampler_get_sample_count(void *); +int lanczos_resampler_get_sample(void *); +void lanczos_resampler_remove_sample(void *); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/lpc.h b/Frameworks/Dumb/dumb/include/internal/lpc.h new file mode 100644 index 000000000..8c585fa5f --- /dev/null +++ b/Frameworks/Dumb/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/Frameworks/Dumb/dumb/include/internal/riff.h b/Frameworks/Dumb/dumb/include/internal/riff.h new file mode 100644 index 000000000..d8705a95f --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/riff.h @@ -0,0 +1,24 @@ +#ifndef RIFF_H +#define RIFF_H + +struct riff; + +struct riff_chunk +{ + unsigned type; + long offset; + unsigned size; + struct riff * nested; +}; + +struct riff +{ + unsigned type; + unsigned chunk_count; + struct riff_chunk * chunks; +}; + +struct riff * riff_parse( DUMBFILE * f, long offset, long size, unsigned proper ); +void riff_free( struct riff * ); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/stack_alloc.h b/Frameworks/Dumb/dumb/include/internal/stack_alloc.h new file mode 100644 index 000000000..b82edec05 --- /dev/null +++ b/Frameworks/Dumb/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/Frameworks/Dumb/dumb/include/internal/tarray.h b/Frameworks/Dumb/dumb/include/internal/tarray.h new file mode 100644 index 000000000..4cb4771a9 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/tarray.h @@ -0,0 +1,25 @@ +#ifndef _T_ARRAY_H_ +#define _T_ARRAY_H_ + +#include + +#ifdef __FRAMEWORK__ +#include +#else +#include "../dumb.h" +#endif + +void * timekeeping_array_create(size_t size); +void timekeeping_array_destroy(void * array); +void * timekeeping_array_dup(void * array); + +void timekeeping_array_reset(void * array, size_t loop_start); + +void timekeeping_array_push(void * array, size_t index, LONG_LONG time); +void timekeeping_array_bump(void * array, size_t index); + +unsigned int timekeeping_array_get_count(void * array, size_t index); + +LONG_LONG timekeeping_array_get_item(void * array, size_t index); + +#endif diff --git a/Frameworks/Dumb/dumb/prj/.gitignore b/Frameworks/Dumb/dumb/prj/.gitignore new file mode 100644 index 000000000..36d588baa --- /dev/null +++ b/Frameworks/Dumb/dumb/prj/.gitignore @@ -0,0 +1,3 @@ +dumb-build-Desktop-Release +dumb-build-Desktop-Debug +*.user diff --git a/Frameworks/Dumb/dumb/prj/dumb/dumb.pro b/Frameworks/Dumb/dumb/prj/dumb/dumb.pro new file mode 100644 index 000000000..e013fa5ce --- /dev/null +++ b/Frameworks/Dumb/dumb/prj/dumb/dumb.pro @@ -0,0 +1,132 @@ +#------------------------------------------------- +# +# 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/blip_buf.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/lanczos_resampler.c \ + ../../src/helpers/lpc.c + +HEADERS += \ + ../../include/dumb.h \ + ../../include/internal/riff.h \ + ../../include/internal/it.h \ + ../../include/internal/dumb.h \ + ../../include/internal/blip_buf.h \ + ../../include/internal/barray.h \ + ../../include/internal/tarray.h \ + ../../include/internal/aldumb.h \ + ../../include/internal/lanczos_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/Frameworks/Dumb/dumb/src/core/atexit.c b/Frameworks/Dumb/dumb/src/core/atexit.c index 16c6abdb2..64814efda 100644 --- a/Frameworks/Dumb/dumb/src/core/atexit.c +++ b/Frameworks/Dumb/dumb/src/core/atexit.c @@ -1,71 +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; - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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/Frameworks/Dumb/dumb/src/core/duhlen.c b/Frameworks/Dumb/dumb/src/core/duhlen.c index 4d79fc099..2c3a35767 100644 --- a/Frameworks/Dumb/dumb/src/core/duhlen.c +++ b/Frameworks/Dumb/dumb/src/core/duhlen.c @@ -1,42 +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" - - - -long duh_get_length(DUH *duh) -{ - return duh ? duh->length : 0; -} - - - -void duh_set_length(DUH *duh, long length) -{ - if (duh) - duh->length = length; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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" + + + +long duh_get_length(DUH *duh) +{ + return duh ? duh->length : 0; +} + + + +void duh_set_length(DUH *duh, long length) +{ + if (duh) + duh->length = length; +} diff --git a/Frameworks/Dumb/dumb/src/core/duhtag.c b/Frameworks/Dumb/dumb/src/core/duhtag.c index b150467d3..77061094e 100644 --- a/Frameworks/Dumb/dumb/src/core/duhtag.c +++ b/Frameworks/Dumb/dumb/src/core/duhtag.c @@ -1,38 +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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/core/dumbfile.c b/Frameworks/Dumb/dumb/src/core/dumbfile.c index 71108c0c3..65e89746b 100644 --- a/Frameworks/Dumb/dumb/src/core/dumbfile.c +++ b/Frameworks/Dumb/dumb/src/core/dumbfile.c @@ -1,401 +1,418 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * dumbfile.c - Hookable, strictly sequential / / \ \ - * file input functions. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" - - - -static DUMBFILE_SYSTEM *the_dfs = NULL; - - - -void register_dumbfile_system(DUMBFILE_SYSTEM *dfs) -{ - ASSERT(dfs); - ASSERT(dfs->open); - ASSERT(dfs->getc); - ASSERT(dfs->close); - the_dfs = dfs; -} - - - -struct DUMBFILE -{ - DUMBFILE_SYSTEM *dfs; - void *file; - long pos; -}; - - - -DUMBFILE *dumbfile_open(const char *filename) -{ - DUMBFILE *f; - - ASSERT(the_dfs); - - f = 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 *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs) -{ - DUMBFILE *f; - - ASSERT(dfs); - ASSERT(dfs->getc); - ASSERT(file); - - f = malloc(sizeof(*f)); - - if (!f) { - if (dfs->close) - (*dfs->close)(file); - return NULL; - } - - f->dfs = dfs; - f->file = file; - - f->pos = 0; - - return f; -} - - - -long dumbfile_pos(DUMBFILE *f) -{ - ASSERT(f); - - return f->pos; -} - - - -int 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 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 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 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); -} - - - -long dumbfile_igetl(DUMBFILE *f) -{ - unsigned long rv, b; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - rv = (*f->dfs->getc)(f->file); - if ((signed long)rv < 0) { - f->pos = -1; - return rv; - } - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 8; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 16; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 24; - - f->pos += 4; - - return rv; -} - - - -long dumbfile_mgetl(DUMBFILE *f) -{ - unsigned long rv, b; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - rv = (*f->dfs->getc)(f->file); - if ((signed long)rv < 0) { - f->pos = -1; - return rv; - } - rv <<= 24; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 16; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 8; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b; - - f->pos += 4; - - return rv; -} - - - -unsigned long dumbfile_cgetul(DUMBFILE *f) -{ - unsigned long rv = 0; - int v; - - do { - v = dumbfile_getc(f); - - if (v < 0) - return v; - - rv <<= 7; - rv |= v & 0x7F; - } while (v & 0x80); - - return rv; -} - - - -signed long dumbfile_cgetsl(DUMBFILE *f) -{ - unsigned long rv = dumbfile_cgetul(f); - - if (f->pos < 0) - return rv; - - return (rv >> 1) | (rv << 31); -} - - - -long dumbfile_getnc(char *ptr, long n, DUMBFILE *f) -{ - long 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 dumbfile_error(DUMBFILE *f) -{ - ASSERT(f); - - return f->pos < 0; -} - - - -int 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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 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 *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 *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; +} + + + +long dumbfile_pos(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos; +} + + + +int 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 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 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 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); +} + + + +long dumbfile_igetl(DUMBFILE *f) +{ + unsigned long rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((signed long)rv < 0) { + f->pos = -1; + return rv; + } + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 24; + + f->pos += 4; + + return rv; +} + + + +long dumbfile_mgetl(DUMBFILE *f) +{ + unsigned long rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((signed long)rv < 0) { + f->pos = -1; + return rv; + } + rv <<= 24; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b; + + f->pos += 4; + + return rv; +} + + + +unsigned long dumbfile_cgetul(DUMBFILE *f) +{ + unsigned long rv = 0; + int v; + + do { + v = dumbfile_getc(f); + + if (v < 0) + return v; + + rv <<= 7; + rv |= v & 0x7F; + } while (v & 0x80); + + return rv; +} + + + +signed long dumbfile_cgetsl(DUMBFILE *f) +{ + unsigned long rv = dumbfile_cgetul(f); + + if (f->pos < 0) + return rv; + + return (rv >> 1) | (rv << 31); +} + + + +long dumbfile_getnc(char *ptr, long n, DUMBFILE *f) +{ + long 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 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); +} + + + +long dumbfile_get_size(DUMBFILE *f) +{ + return (*f->dfs->get_size)(f->file); +} + + + +int dumbfile_error(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos < 0; +} + + + +int 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/Frameworks/Dumb/dumb/src/core/loadduh.c b/Frameworks/Dumb/dumb/src/core/loadduh.c index 7dfe5cc10..e954fe24c 100644 --- a/Frameworks/Dumb/dumb/src/core/loadduh.c +++ b/Frameworks/Dumb/dumb/src/core/loadduh.c @@ -1,42 +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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/core/makeduh.c b/Frameworks/Dumb/dumb/src/core/makeduh.c index 8345fe4fe..345e2d65b 100644 --- a/Frameworks/Dumb/dumb/src/core/makeduh.c +++ b/Frameworks/Dumb/dumb/src/core/makeduh.c @@ -1,132 +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( - long 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 += 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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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( + long 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 += 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 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/Frameworks/Dumb/dumb/src/core/rawsig.c b/Frameworks/Dumb/dumb/src/core/rawsig.c index 926c99065..8a750a67e 100644 --- a/Frameworks/Dumb/dumb/src/core/rawsig.c +++ b/Frameworks/Dumb/dumb/src/core/rawsig.c @@ -1,44 +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 *duh_get_raw_sigdata(DUH *duh, int sig, long type) -{ - DUH_SIGNAL *signal; - - if (!duh) return NULL; - - if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL; - - signal = duh->signal[sig]; - - if (signal && signal->desc->type == type) - return signal->sigdata; - - return NULL; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *duh_get_raw_sigdata(DUH *duh, int sig, long 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/Frameworks/Dumb/dumb/src/core/readduh.c b/Frameworks/Dumb/dumb/src/core/readduh.c index 514b04a07..0fb775b2b 100644 --- a/Frameworks/Dumb/dumb/src/core/readduh.c +++ b/Frameworks/Dumb/dumb/src/core/readduh.c @@ -1,107 +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; - long 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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; + long 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 *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/Frameworks/Dumb/dumb/src/core/register.c b/Frameworks/Dumb/dumb/src/core/register.c index 66dd45241..2e16c9a7e 100644 --- a/Frameworks/Dumb/dumb/src/core/register.c +++ b/Frameworks/Dumb/dumb/src/core/register.c @@ -1,104 +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 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(long 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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 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(long 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/Frameworks/Dumb/dumb/src/core/rendduh.c b/Frameworks/Dumb/dumb/src/core/rendduh.c index 1639b938e..1effa3eb4 100644 --- a/Frameworks/Dumb/dumb/src/core/rendduh.c +++ b/Frameworks/Dumb/dumb/src/core/rendduh.c @@ -1,184 +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, long pos) -{ - return duh_start_sigrenderer(duh, 0, n_channels, pos); -} - - - -long duh_render( - DUH_SIGRENDERER *sigrenderer, - int bits, int unsign, - float volume, float delta, - long size, void *sptr -) -{ - long 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 */ -long 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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, long pos) +{ + return duh_start_sigrenderer(duh, 0, n_channels, pos); +} + + + +long duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + long size, void *sptr +) +{ + long 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 */ +long 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/Frameworks/Dumb/dumb/src/core/rendsig.c b/Frameworks/Dumb/dumb/src/core/rendsig.c index 1b7cf65f0..0111c52a2 100644 --- a/Frameworks/Dumb/dumb/src/core/rendsig.c +++ b/Frameworks/Dumb/dumb/src/core/rendsig.c @@ -1,344 +1,352 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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; - - long pos; - int subpos; - - DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback; - void *callback_data; -}; - - - -DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos) -{ - DUH_SIGRENDERER *sigrenderer; - - DUH_SIGNAL *signal; - DUH_START_SIGRENDERER proc; - - 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; -} - - - -#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"); -} - - - -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 duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) -{ - return sigrenderer ? sigrenderer->n_channels : 0; -} - - - -long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) -{ - return sigrenderer ? sigrenderer->pos : -1; -} - - - -void duh_sigrenderer_set_sigparam( - DUH_SIGRENDERER *sigrenderer, - unsigned char id, long 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 = %ld 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)); -} - - - -long duh_sigrenderer_generate_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - long 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 += (long)(t >> 16); - sigrenderer->subpos = (int)t & 65535; - } - - return rendered; -} - - - -/* DEPRECATED */ -long duh_sigrenderer_get_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - sample_t **s; - long rendered; - long 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 */ -long duh_render_signal( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - sample_t **s; - long rendered; - long 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 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 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 *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long 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 *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long 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, long 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 +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; + + long pos; + int subpos; + + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback; + void *callback_data; +}; + + + +DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos) +{ + DUH_SIGRENDERER *sigrenderer; + + DUH_SIGNAL *signal; + DUH_START_SIGRENDERER proc; + + 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; +} + + + +#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"); +} + + + +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 duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer ? sigrenderer->n_channels : 0; +} + + + +long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) +{ + DUH_SIGRENDERER_GET_POSITION proc; + + if (!sigrenderer) return -1; + + proc = sigrenderer->desc->sigrenderer_get_position; + if (proc) + return (*proc)(sigrenderer->sigrenderer); + else + return sigrenderer->pos; +} + + + +void duh_sigrenderer_set_sigparam( + DUH_SIGRENDERER *sigrenderer, + unsigned char id, long 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 = %ld 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)); +} + + + +long duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + long 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 += (long)(t >> 16); + sigrenderer->subpos = (int)t & 65535; + } + + return rendered; +} + + + +/* DEPRECATED */ +long duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + sample_t **s; + long rendered; + long 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 */ +long duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + sample_t **s; + long rendered; + long 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 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 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 *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long 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 *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long 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, long 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/Frameworks/Dumb/dumb/src/core/unload.c b/Frameworks/Dumb/dumb/src/core/unload.c index 11d81e26e..f241f718d 100644 --- a/Frameworks/Dumb/dumb/src/core/unload.c +++ b/Frameworks/Dumb/dumb/src/core/unload.c @@ -1,64 +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 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); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 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/Frameworks/Dumb/dumb/src/helpers/barray.c b/Frameworks/Dumb/dumb/src/helpers/barray.c new file mode 100644 index 000000000..95fe7af10 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/barray.c @@ -0,0 +1,159 @@ +#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)); + } + } +} + +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_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/Frameworks/Dumb/dumb/src/helpers/blip_buf.c b/Frameworks/Dumb/dumb/src/helpers/blip_buf.c new file mode 100644 index 000000000..9f26f71cb --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/blip_buf.c @@ -0,0 +1,354 @@ +/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ + +#include "internal/blip_buf.h" + +#include +#include +#include +#include + +/* Library Copyright (C) 2003-2009 Shay 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#if defined (BLARGG_TEST) && BLARGG_TEST + #include "blargg_test.h" +#endif + +/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000. +Avoids constants that don't fit in 32 bits. */ +#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF + typedef unsigned long fixed_t; + enum { pre_shift = 32 }; + +#elif defined(ULLONG_MAX) + typedef unsigned long long fixed_t; + enum { pre_shift = 32 }; + +#else + typedef unsigned fixed_t; + enum { pre_shift = 0 }; + +#endif + +enum { time_bits = pre_shift + 20 }; + +static fixed_t const time_unit = (fixed_t) 1 << time_bits; + +enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */ +enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */ + +enum { half_width = 8 }; +enum { buf_extra = half_width*2 + end_frame_extra }; +enum { phase_bits = 5 }; +enum { phase_count = 1 << phase_bits }; +enum { delta_bits = 15 }; +enum { delta_unit = 1 << delta_bits }; +enum { frac_bits = time_bits - pre_shift }; + +/* We could eliminate avail and encode whole samples in offset, but that would +limit the total buffered samples to blip_max_frame. That could only be +increased by decreasing time_bits, which would reduce resample ratio accuracy. +*/ + +/** Sample buffer that resamples to output rate and accumulates samples +until they're read out */ +struct blip_t +{ + fixed_t factor; + fixed_t offset; + int avail; + int size; + int integrator; +}; + +typedef int buf_t; + +/* probably not totally portable */ +#define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) + +/* Arithmetic (sign-preserving) right shift */ +#define ARITH_SHIFT( n, shift ) \ + ((n) >> (shift)) + +enum { max_sample = +32767 }; +enum { min_sample = -32768 }; + +#define CLAMP( n ) \ + {\ + if ( (short) n != n )\ + n = ARITH_SHIFT( n, 16 ) ^ max_sample;\ + } + +static void check_assumptions( void ) +{ + int n; + + #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" + #endif + + assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */ + + n = max_sample * 2; + CLAMP( n ); + assert( n == max_sample ); + + n = min_sample * 2; + CLAMP( n ); + assert( n == min_sample ); + + assert( blip_max_ratio <= time_unit ); + assert( blip_max_frame <= (fixed_t) -1 >> time_bits ); +} + +blip_t* blip_new( int size ) +{ + blip_t* m; + assert( size >= 0 ); + + m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); + if ( m ) + { + m->factor = time_unit / blip_max_ratio; + m->size = size; + blip_clear( m ); + check_assumptions(); + } + return m; +} + +blip_t* blip_dup( blip_t* m ) +{ + size_t size = sizeof *m + (m->size + buf_extra) * sizeof(buf_t); + blip_t* r = (blip_t*) malloc( size ); + if ( r ) memcpy( r, m, size ); + return r; +} + +void blip_delete( blip_t* m ) +{ + if ( m != NULL ) + { + /* Clear fields in case user tries to use after freeing */ + memset( m, 0, sizeof *m ); + free( m ); + } +} + +void blip_set_rates( blip_t* m, double clock_rate, double sample_rate ) +{ + double factor = time_unit * sample_rate / clock_rate; + m->factor = (fixed_t) factor; + + /* Fails if clock_rate exceeds maximum, relative to sample_rate */ + assert( 0 <= factor - m->factor && factor - m->factor < 1 ); + + /* Avoid requiring math.h. Equivalent to + m->factor = (int) ceil( factor ) */ + if ( m->factor < factor ) + m->factor++; + + /* At this point, factor is most likely rounded up, but could still + have been rounded down in the floating-point calculation. */ +} + +void blip_clear( blip_t* m ) +{ + /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if + factor is rounded up. factor-1 is suitable if factor is rounded down. + Since we don't know rounding direction, factor/2 accommodates either, + with the slight loss of showing an error in half the time. Since for + a 64-bit factor this is years, the halving isn't a problem. */ + + m->offset = m->factor / 2; + m->avail = 0; + m->integrator = 0; + memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); +} + +int blip_clocks_needed( const blip_t* m, int samples ) +{ + fixed_t needed; + + /* Fails if buffer can't hold that many more samples */ + assert( samples >= 0 && m->avail + samples <= m->size ); + + needed = (fixed_t) samples * time_unit; + if ( needed < m->offset ) + return 0; + + return (needed - m->offset + m->factor - 1) / m->factor; +} + +void blip_end_frame( blip_t* m, unsigned t ) +{ + fixed_t off = t * m->factor + m->offset; + m->avail += off >> time_bits; + m->offset = off & (time_unit - 1); + + /* Fails if buffer size was exceeded */ + assert( m->avail <= m->size ); +} + +int blip_samples_avail( const blip_t* m ) +{ + return m->avail; +} + +static void remove_samples( blip_t* m, int count ) +{ + buf_t* buf = SAMPLES( m ); + int remain = m->avail + buf_extra - count; + m->avail -= count; + + memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); + memset( &buf [remain], 0, count * sizeof buf [0] ); +} + +int blip_read_samples( blip_t* m, int out [], int count ) +{ + assert( count >= 0 ); + + if ( count > m->avail ) + count = m->avail; + + if ( count ) + { + buf_t const* in = SAMPLES( m ); + buf_t const* end = in + count; + int sum = m->integrator; + do + { + /* Eliminate fraction */ + int s = ARITH_SHIFT( sum, delta_bits - 8 ); + + sum += *in++; + + *out = s; + out++; + + /* High-pass filter */ + sum -= s >> (8 - (delta_bits - bass_shift)); //<< (delta_bits - bass_shift - 8); + } + while ( in != end ); + m->integrator = sum; + + remove_samples( m, count ); + } + + return count; +} + +int blip_peek_sample( blip_t* m ) +{ + return ARITH_SHIFT( m->integrator, delta_bits - 8 ); +} + +/* Things that didn't help performance on x86: + __attribute__((aligned(128))) + #define short int + restrict +*/ + +/* Sinc_Generator( 0.9, 0.55, 4.5 ) */ +static short const bl_step [phase_count + 1] [half_width] = +{ +{ 43, -115, 350, -488, 1136, -914, 5861,21022}, +{ 44, -118, 348, -473, 1076, -799, 5274,21001}, +{ 45, -121, 344, -454, 1011, -677, 4706,20936}, +{ 46, -122, 336, -431, 942, -549, 4156,20829}, +{ 47, -123, 327, -404, 868, -418, 3629,20679}, +{ 47, -122, 316, -375, 792, -285, 3124,20488}, +{ 47, -120, 303, -344, 714, -151, 2644,20256}, +{ 46, -117, 289, -310, 634, -17, 2188,19985}, +{ 46, -114, 273, -275, 553, 117, 1758,19675}, +{ 44, -108, 255, -237, 471, 247, 1356,19327}, +{ 43, -103, 237, -199, 390, 373, 981,18944}, +{ 42, -98, 218, -160, 310, 495, 633,18527}, +{ 40, -91, 198, -121, 231, 611, 314,18078}, +{ 38, -84, 178, -81, 153, 722, 22,17599}, +{ 36, -76, 157, -43, 80, 824, -241,17092}, +{ 34, -68, 135, -3, 8, 919, -476,16558}, +{ 32, -61, 115, 34, -60, 1006, -683,16001}, +{ 29, -52, 94, 70, -123, 1083, -862,15422}, +{ 27, -44, 73, 106, -184, 1152,-1015,14824}, +{ 25, -36, 53, 139, -239, 1211,-1142,14210}, +{ 22, -27, 34, 170, -290, 1261,-1244,13582}, +{ 20, -20, 16, 199, -335, 1301,-1322,12942}, +{ 18, -12, -3, 226, -375, 1331,-1376,12293}, +{ 15, -4, -19, 250, -410, 1351,-1408,11638}, +{ 13, 3, -35, 272, -439, 1361,-1419,10979}, +{ 11, 9, -49, 292, -464, 1362,-1410,10319}, +{ 9, 16, -63, 309, -483, 1354,-1383, 9660}, +{ 7, 22, -75, 322, -496, 1337,-1339, 9005}, +{ 6, 26, -85, 333, -504, 1312,-1280, 8355}, +{ 4, 31, -94, 341, -507, 1278,-1205, 7713}, +{ 3, 35, -102, 347, -506, 1238,-1119, 7082}, +{ 1, 40, -110, 350, -499, 1190,-1021, 6464}, +{ 0, 43, -115, 350, -488, 1136, -914, 5861} +}; + +/* Shifting by pre_shift allows calculation using unsigned int rather than +possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient. +And by having pre_shift 32, a 32-bit platform can easily do the shift by +simply ignoring the low half. */ + +void blip_add_delta( blip_t* m, unsigned time, int delta ) +{ + unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); + buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); + + int const phase_shift = frac_bits - phase_bits; + int phase = fixed >> phase_shift & (phase_count - 1); + short const* in = bl_step [phase]; + short const* rev = bl_step [phase_count - phase]; + + int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1); + int delta2 = (delta * interp) >> delta_bits; + delta -= delta2; + + /* Fails if buffer size was exceeded */ + assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); + + out [0] += in[0]*delta + in[half_width+0]*delta2; + out [1] += in[1]*delta + in[half_width+1]*delta2; + out [2] += in[2]*delta + in[half_width+2]*delta2; + out [3] += in[3]*delta + in[half_width+3]*delta2; + out [4] += in[4]*delta + in[half_width+4]*delta2; + out [5] += in[5]*delta + in[half_width+5]*delta2; + out [6] += in[6]*delta + in[half_width+6]*delta2; + out [7] += in[7]*delta + in[half_width+7]*delta2; + + in = rev; + out [ 8] += in[7]*delta + in[7-half_width]*delta2; + out [ 9] += in[6]*delta + in[6-half_width]*delta2; + out [10] += in[5]*delta + in[5-half_width]*delta2; + out [11] += in[4]*delta + in[4-half_width]*delta2; + out [12] += in[3]*delta + in[3-half_width]*delta2; + out [13] += in[2]*delta + in[2-half_width]*delta2; + out [14] += in[1]*delta + in[1-half_width]*delta2; + out [15] += in[0]*delta + in[0-half_width]*delta2; +} + +void blip_add_delta_fast( blip_t* m, unsigned time, int delta ) +{ + unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); + buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); + + int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1); + int delta2 = delta * interp; + + /* Fails if buffer size was exceeded */ + assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); + + out [7] += delta * delta_unit - delta2; + out [8] += delta2; +} diff --git a/Frameworks/Dumb/dumb/src/helpers/clickrem.c b/Frameworks/Dumb/dumb/src/helpers/clickrem.c index 9109e3241..336b492de 100644 --- a/Frameworks/Dumb/dumb/src/helpers/clickrem.c +++ b/Frameworks/Dumb/dumb/src/helpers/clickrem.c @@ -1,281 +1,281 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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; -}; - - -struct DUMB_CLICK -{ - DUMB_CLICK *next; - long pos; - sample_t step; -}; - - - -DUMB_CLICK_REMOVER *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; - - return cr; -} - - - -void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step) -{ - DUMB_CLICK *click; - - ASSERT(pos >= 0); - - if (!cr || !step) return; - - if (pos == 0) { - cr->offset -= step; - return; - } - - click = malloc(sizeof(*click)); - 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 dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife) -{ - DUMB_CLICK *click; - long 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); - 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 dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr) -{ - return cr ? cr->offset : 0; -} - - - -void 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; - } - free(cr); - } -} - - - -DUMB_CLICK_REMOVER **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 dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) - dumb_record_click(cr[i], pos, step[i]); - } -} - - - -void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) - dumb_record_click(cr[i], pos, -step[i]); - } -} - - - -void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float 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 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 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); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; +}; + + +struct DUMB_CLICK +{ + DUMB_CLICK *next; + long pos; + sample_t step; +}; + + + +DUMB_CLICK_REMOVER *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; + + return cr; +} + + + +void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step) +{ + DUMB_CLICK *click; + + ASSERT(pos >= 0); + + if (!cr || !step) return; + + if (pos == 0) { + cr->offset -= step; + return; + } + + click = malloc(sizeof(*click)); + 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 dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife) +{ + DUMB_CLICK *click; + long 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); + 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 dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr) +{ + return cr ? cr->offset : 0; +} + + + +void 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; + } + free(cr); + } +} + + + +DUMB_CLICK_REMOVER **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 dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, step[i]); + } +} + + + +void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, -step[i]); + } +} + + + +void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float 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 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 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/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c b/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c new file mode 100644 index 000000000..cc6460769 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include "internal/fir_resampler.h" + +enum { fir_width = 16 }; + +enum { fir_max_res = 1024 }; +enum { fir_min_width = (fir_width < 4 ? 4 : fir_width) }; +enum { fir_adj_width = fir_min_width / 4 * 4 + 2 }; +enum { fir_stereo = 1 }; /* channel count, not boolean value */ +enum { fir_write_offset = fir_adj_width * fir_stereo }; + +enum { fir_buffer_size = fir_width * 2 }; + +typedef short fir_impulse[fir_adj_width]; + +/* exp slope to 31/32 of ln(8) */ +static const double fir_ratios[32] = { + 1.000, 1.067, 1.139, 1.215, 1.297, 1.384, 1.477, 1.576, + 1.682, 1.795, 1.915, 2.044, 2.181, 2.327, 2.484, 2.650, + 2.828, 3.018, 3.221, 3.437, 3.668, 3.914, 4.177, 4.458, + 4.757, 5.076, 5.417, 5.781, 6.169, 6.583, 7.025, 7.497 +}; + +static fir_impulse fir_impulses[32][fir_max_res]; + +#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 ); + + double angle = (count / 2 - 1 + offset) * -step; + + scale /= maxh * 2; + + while ( count-- ) + { + double w; + *out++ = 0; + 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; + } +} + +typedef struct fir_resampler +{ + int write_pos, write_filled; + int read_pos, read_filled; + unsigned short phase; + unsigned int phase_inc; + unsigned int ratio_set; + int buffer_in[fir_buffer_size * 2]; + int buffer_out[fir_buffer_size]; +} fir_resampler; + +void * fir_resampler_create() +{ + fir_resampler * r = ( fir_resampler * ) malloc( sizeof(fir_resampler) ); + if ( !r ) return 0; + + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + r->phase_inc = 0; + r->ratio_set = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + + return r; +} + +void fir_resampler_delete(void * _r) +{ + free( _r ); +} + +void * fir_resampler_dup(void * _r) +{ + fir_resampler * r_in = ( fir_resampler * ) _r; + fir_resampler * r_out = ( fir_resampler * ) malloc( sizeof(fir_resampler) ); + if ( !r_out ) return 0; + + 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->ratio_set = r_in->ratio_set; + 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) ); + + return r_out; +} + +int fir_resampler_get_free_count(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + return fir_buffer_size - r->write_filled; +} + +int fir_resampler_ready(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + return r->write_filled > fir_adj_width; +} + +void fir_resampler_clear(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); +} + +void fir_resampler_set_rate(void *_r, double new_factor) +{ + fir_resampler * r = ( fir_resampler * ) _r; + r->phase_inc = (int)( new_factor * 65536.0 ); + r->ratio_set = 0; + while ( r->ratio_set < 31 && new_factor > fir_ratios[ r->ratio_set ] ) r->ratio_set++; +} + +void fir_resampler_write_sample(void *_r, short s) +{ + fir_resampler * r = ( fir_resampler * ) _r; + + if ( r->write_filled < fir_buffer_size ) + { + int s32 = s; + + r->buffer_in[ r->write_pos ] = s32; + r->buffer_in[ r->write_pos + fir_buffer_size ] = s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % fir_buffer_size; + } +} + +void fir_init() +{ + double const rolloff = 0.999; + double const gain = 1.0; + + int const res = fir_max_res; + + int i; + + for (i = 0; i < 32; i++) + { + double const ratio_ = fir_ratios[ i ]; + + double fraction = 1.0 / (double)fir_max_res; + + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + short* out = (short*) fir_impulses[ i ]; + int n; + for ( n = res; --n >= 0; ) + { + gen_sinc( rolloff, (int) (fir_adj_width * filter + 1) & ~1, pos, filter, + (double) (0x7FFF * gain * filter), (int) fir_adj_width, out ); + out += fir_adj_width; + + pos += fraction; + } + } +} + +int fir_resampler_run(void *_r, int ** out_, int * out_end) +{ + fir_resampler * r = ( fir_resampler * ) _r; + int in_size = r->write_filled; + int const* in_ = r->buffer_in + fir_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= fir_write_offset; + if ( in_size > 0 ) + { + int* out = *out_; + int const* in = in_; + int const* const in_end = in + in_size; + int phase = r->phase; + int phase_inc = r->phase_inc; + int ratio_set = r->ratio_set; + + do + { + // accumulate in extended precision + short const* imp = fir_impulses[ratio_set][(phase & 0xFFC0) >> 6]; + int pt = imp [0]; + int s = pt * in [0]; + int n; + if ( out >= out_end ) + break; + for ( n = (fir_adj_width - 2) / 2; n; --n ) + { + pt = imp [1]; + s += pt * in [1]; + + // pre-increment more efficient on some RISC processors + imp += 2; + pt = imp [0]; + in += 2; + s += pt * in [0]; + } + pt = imp [1]; + s += pt * in [1]; + + phase += phase_inc; + + in += (phase >> 16) - fir_adj_width + 2; + + phase &= 65535; + + *out++ = (int) (s >> 7); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = in - in_; + + r->write_filled -= used; + } + + return used; +} + +int fir_resampler_get_sample(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + if ( r->read_filled < 1 ) + { + int write_pos = ( r->read_pos + r->read_filled ) % fir_buffer_size; + int write_size = fir_buffer_size - write_pos; + int * out = r->buffer_out + write_pos; + if ( write_size > ( fir_buffer_size - r->read_filled ) ) + write_size = fir_buffer_size - r->read_filled; + fir_resampler_run( r, &out, out + write_size ); + r->read_filled += out - r->buffer_out - write_pos; + } + if ( r->read_filled < 1 ) + return 0; + return r->buffer_out[ r->read_pos ]; +} + +void fir_resampler_remove_sample(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + if ( r->read_filled > 0 ) + { + --r->read_filled; + r->read_pos = ( r->read_pos + 1 ) % fir_buffer_size; + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c b/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c new file mode 100644 index 000000000..d99abf595 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c @@ -0,0 +1,229 @@ +#include +#include +#define _USE_MATH_DEFINES +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "internal/lanczos_resampler.h" + +enum { LANCZOS_RESOLUTION = 8192 }; +enum { LANCZOS_WIDTH = 8 }; +enum { LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH }; + +static double lanczos_lut[LANCZOS_SAMPLES + 1]; + +enum { lanczos_buffer_size = LANCZOS_WIDTH * 4 }; + +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); +} + +void lanczos_init() +{ + unsigned i; + double dx = (double)(LANCZOS_WIDTH) / LANCZOS_SAMPLES, x = 0.0; + for (i = 0; i < LANCZOS_SAMPLES + 1; ++i, x += dx) + lanczos_lut[i] = abs(x) < LANCZOS_WIDTH ? sinc(x) * sinc(x / LANCZOS_WIDTH) : 0.0; +} + +typedef struct lanczos_resampler +{ + int write_pos, write_filled; + int read_pos, read_filled; + unsigned short phase; + unsigned int phase_inc; + float buffer_in[lanczos_buffer_size * 2]; + int buffer_out[lanczos_buffer_size]; +} lanczos_resampler; + +void * lanczos_resampler_create() +{ + lanczos_resampler * r = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) ); + if ( !r ) return 0; + + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + r->phase_inc = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + + return r; +} + +void lanczos_resampler_delete(void * _r) +{ + free( _r ); +} + +void * lanczos_resampler_dup(void * _r) +{ + lanczos_resampler * r_in = ( lanczos_resampler * ) _r; + lanczos_resampler * r_out = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) ); + if ( !r_out ) return 0; + + 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; + 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) ); + + return r_out; +} + +int lanczos_resampler_get_free_count(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + return lanczos_buffer_size - r->write_filled; +} + +int lanczos_resampler_ready(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + return r->write_filled > (LANCZOS_WIDTH * 2); +} + +void lanczos_resampler_clear(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; +} + +void lanczos_resampler_set_rate(void *_r, double new_factor) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + r->phase_inc = (int)( new_factor * LANCZOS_RESOLUTION ); +} + +void lanczos_resampler_write_sample(void *_r, short s) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + + if ( r->write_filled < lanczos_buffer_size ) + { + float s32 = s; + + r->buffer_in[ r->write_pos ] = s32; + r->buffer_in[ r->write_pos + lanczos_buffer_size ] = s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % lanczos_buffer_size; + } +} + +static int lanczos_resampler_run(lanczos_resampler * r, int ** out_, int * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + lanczos_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= LANCZOS_WIDTH * 2; + if ( in_size > 0 ) + { + int* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + int phase = r->phase; + int phase_inc = r->phase_inc; + + int step = phase_inc > LANCZOS_RESOLUTION ? LANCZOS_RESOLUTION * LANCZOS_RESOLUTION / phase_inc : LANCZOS_RESOLUTION; + + do + { + // accumulate in extended precision + double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0; + int i = LANCZOS_WIDTH; + int phase_adj = phase * step / LANCZOS_RESOLUTION; + double sample; + + if ( out >= out_end ) + break; + + for (; i >= -LANCZOS_WIDTH + 1; --i) + { + int pos = i * step; + kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = lanczos_lut[abs(phase_adj - pos)]; + } + for (sample = 0, i = 0; i < LANCZOS_WIDTH * 2; ++i) + sample += in[i] * kernel[i]; + *out++ = (int) (sample / kernel_sum * 256.0); + + phase += phase_inc; + + in += phase >> 13; + + phase &= 8191; + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = in - in_; + + r->write_filled -= used; + } + + return used; +} + +static void lanczos_resampler_fill(lanczos_resampler * r) +{ + while ( r->write_filled > (LANCZOS_WIDTH * 2) && + r->read_filled < lanczos_buffer_size ) + { + int write_pos = ( r->read_pos + r->read_filled ) % lanczos_buffer_size; + int write_size = lanczos_buffer_size - write_pos; + int * out = r->buffer_out + write_pos; + if ( write_size > ( lanczos_buffer_size - r->read_filled ) ) + write_size = lanczos_buffer_size - r->read_filled; + lanczos_resampler_run( r, &out, out + write_size ); + r->read_filled += out - r->buffer_out - write_pos; + } +} + +int lanczos_resampler_get_sample_count(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled < 1 ) + lanczos_resampler_fill( r ); + return r->read_filled; +} + +int lanczos_resampler_get_sample(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled < 1 ) + lanczos_resampler_fill( r ); + if ( r->read_filled < 1 ) + return 0; + return r->buffer_out[ r->read_pos ]; +} + +void lanczos_resampler_remove_sample(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled > 0 ) + { + --r->read_filled; + r->read_pos = ( r->read_pos + 1 ) % lanczos_buffer_size; + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/lpc.c b/Frameworks/Dumb/dumb/src/helpers/lpc.c new file mode 100644 index 000000000..6eb53a977 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/lpc.c @@ -0,0 +1,320 @@ +/******************************************************************** + * * + * 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" + +/* 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 ] = lpc_output[ o ]; + s16[ o * 2 + 1 ] = 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 ] = lpc_output[ o ]; + s8[ o * 2 + 1 ] = 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 ] = 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 ] = 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/Frameworks/Dumb/dumb/src/helpers/memfile.c b/Frameworks/Dumb/dumb/src/helpers/memfile.c index b65ab5f78..623c4d6d0 100644 --- a/Frameworks/Dumb/dumb/src/helpers/memfile.c +++ b/Frameworks/Dumb/dumb/src/helpers/memfile.c @@ -1,96 +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; - long left; -}; - - - -static int 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 dumb_memfile_getc(void *f) -{ - MEMFILE *m = f; - if (m->left <= 0) return -1; - m->left--; - return *(const unsigned char *)m->ptr++; -} - - - -static long dumb_memfile_getnc(char *ptr, long 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 dumb_memfile_close(void *f) -{ - free(f); -} - - - -static DUMBFILE_SYSTEM memfile_dfs = { - NULL, - &dumb_memfile_skip, - &dumb_memfile_getc, - &dumb_memfile_getnc, - &dumb_memfile_close -}; - - - -DUMBFILE *dumbfile_open_memory(const char *data, long size) -{ - MEMFILE *m = malloc(sizeof(*m)); - if (!m) return NULL; - - m->ptr = data; - m->left = size; - - return dumbfile_open_ex(m, &memfile_dfs); -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 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 dumb_memfile_getc(void *f) +{ + MEMFILE *m = f; + if (m->left <= 0) return -1; + m->left--; + return *(const unsigned char *)m->ptr++; +} + + + +static long dumb_memfile_getnc(char *ptr, long 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 dumb_memfile_close(void *f) +{ + free(f); +} + + +static int 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 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 *dumbfile_open_memory(const char *data, long 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/Frameworks/Dumb/dumb/src/helpers/resamp2.inc b/Frameworks/Dumb/dumb/src/helpers/resamp2.inc index 2619bd713..b343d23b2 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resamp2.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resamp2.inc @@ -1,134 +1,179 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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 _1 - -/* 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. */ -#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS -#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES -#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES -#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO -#define MIX_ALIAS(op, offset) MONO_DEST_MIX_ALIAS(op, offset) -#define MIX_LINEAR(op, o0, o1) MONO_DEST_MIX_LINEAR(op, o0, o1) -#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) -#define MIX_ZEROS(op) *dst++ op 0 -#include "resamp3.inc" - -/* Create stereo destination resampler. */ -#define SUFFIX3 _2 -#define VOLUME_PARAMETERS float volume_left, float volume_right -#define VOLUME_VARIABLES lvol, rvol -#define SET_VOLUME_VARIABLES { \ - lvol = (int)floor(volume_left * 65536.0 + 0.5); \ - rvol = (int)floor(volume_right * 65536.0 + 0.5); \ -} -#define VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0) -#define MIX_ALIAS(op, offset) STEREO_DEST_MIX_ALIAS(op, offset) -#define MIX_LINEAR(op, o0, o1) STEREO_DEST_MIX_LINEAR(op, o0, o1) -#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) -#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } -#include "resamp3.inc" - - - -#undef STEREO_DEST_MIX_CUBIC -#undef MONO_DEST_MIX_CUBIC -#undef STEREO_DEST_MIX_LINEAR -#undef MONO_DEST_MIX_LINEAR -#undef STEREO_DEST_MIX_ALIAS -#undef MONO_DEST_MIX_ALIAS -#undef MONO_DEST_VOLUMES_ARE_ZERO -#undef SET_MONO_DEST_VOLUME_VARIABLES -#undef MONO_DEST_VOLUME_ZEROS -#undef MONO_DEST_VOLUME_VARIABLES -#undef MONO_DEST_VOLUME_PARAMETERS -#undef COPYSRC2 -#undef COPYSRC -#undef DIVIDE_BY_SRC_CHANNELS -#undef SRC_CHANNELS -#undef SUFFIX2 +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 _1 + +/* 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. */ +#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 MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count) +#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS +#define PEEK_FIR MONO_DEST_PEEK_FIR +#define MIX_FIR MONO_DEST_MIX_FIR +#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1) +#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) +#define MIX_ZEROS(op) *dst++ op 0 +#include "resamp3.inc" + +/* 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 = (int)(volume_left->volume * 16777216.0); \ + lvold = (int)(volume_left->delta * 16777216.0); \ + lvolt = (int)(volume_left->target * 16777216.0); \ + lvolm = (int)(volume_left->mix * 16777216.0); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = (int)(volume_right->volume * 16777216.0); \ + rvold = (int)(volume_right->delta * 16777216.0); \ + rvolt = (int)(volume_right->target * 16777216.0); \ + rvolm = (int)(volume_right->mix * 16777216.0); \ + 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(count) STEREO_DEST_MIX_ALIAS(count) +#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS +#define PEEK_FIR STEREO_DEST_PEEK_FIR +#define MIX_FIR STEREO_DEST_MIX_FIR +#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 MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } +#include "resamp3.inc" + + + +#undef STEREO_DEST_MIX_CUBIC +#undef MONO_DEST_MIX_CUBIC +#undef STEREO_DEST_MIX_LINEAR +#undef MONO_DEST_MIX_LINEAR +#undef STEREO_DEST_MIX_ALIAS +#undef MONO_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 MONO_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/Frameworks/Dumb/dumb/src/helpers/resamp3.inc b/Frameworks/Dumb/dumb/src/helpers/resamp3.inc index 4bdada3dd..e0bc8ee78 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resamp3.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resamp3.inc @@ -1,371 +1,441 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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. - */ - - - -long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta) -{ - int dt; - int VOLUME_VARIABLES; - long done; - long todo; - int quality; - - if (!resampler || resampler->dir == 0) return 0; - ASSERT(resampler->dir == -1 || resampler->dir == 1); - - done = 0; - dt = (int)(delta * 65536.0 + 0.5); - SET_VOLUME_VARIABLES; - - if (VOLUMES_ARE_ZERO) dst = NULL; - - init_cubic(); - - quality = dumb_resampling_quality; - if (quality > resampler->max_quality) quality = resampler->max_quality; - else if (quality < resampler->min_quality) quality = resampler->min_quality; - - while (done < dst_size) { - if (process_pickup(resampler)) return done; - - if ((resampler->dir ^ dt) < 0) - dt = -dt; - - if (resampler->dir < 0) - todo = (long)((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt); - else - todo = (long)((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt); - - if (todo < 0) - todo = 0; - else if (todo > dst_size - done) - todo = dst_size - done; - - 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(+=, 0); - subpos += dt; - pos += subpos >> 16; - x -= (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = xstart = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - MIX_ALIAS(+=, 2); - subpos += dt; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); - } else if (quality <= DUMB_RQ_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(+=, 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, 2); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } else { - /* 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(+=, 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(+=, x, x, 0, 1, 2, 3); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } - 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 (dumb_resampling_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(+=, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = xstart = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - MIX_ALIAS(+=, -2); - subpos += dt; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); - } else if (dumb_resampling_quality <= DUMB_RQ_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, 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(+=, -2, -1); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } else { - /* 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(+=, 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(+=, x, x, -3, -2, -1, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } - 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 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; } - - init_cubic(); - - quality = dumb_resampling_quality; - if (quality > resampler->max_quality) quality = resampler->max_quality; - else if (quality < resampler->min_quality) quality = resampler->min_quality; - - src = resampler->src; - pos = resampler->pos; - subpos = resampler->subpos; - x = resampler->X; - - if (resampler->dir < 0) { - HEAVYASSERT(pos >= resampler->start); - if (dumb_resampling_quality <= 0) { - /* Aliasing, backwards */ - MIX_ALIAS(=, 1); - } else if (quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, backwards */ - MIX_LINEAR(=, 2, 1); - } else { - /* Cubic interpolation, backwards */ - MIX_CUBIC(=, src, x, pos, 2, 1, 0); - } - } else { - HEAVYASSERT(pos < resampler->end); - if (dumb_resampling_quality <= 0) { - /* Aliasing */ - MIX_ALIAS(=, 1); - } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, forwards */ - MIX_LINEAR(=, 1, 2); - } else { - /* Cubic interpolation, forwards */ - MIX_CUBIC(=, x, src, 0, 1, 2, pos); - } - } -} - - - -#undef MIX_ZEROS -#undef MIX_CUBIC -#undef MIX_LINEAR -#undef MIX_ALIAS -#undef VOLUMES_ARE_ZERO -#undef SET_VOLUME_VARIABLES -#undef VOLUME_VARIABLES -#undef VOLUME_PARAMETERS -#undef SUFFIX3 +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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. + */ + + + +long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta) +{ + int dt, inv_dt; + int VOLUME_VARIABLES; + long done; + long todo; + LONG_LONG todo64; + int quality; + int blip_samples[256*SRC_CHANNELS]; + + if (!resampler || resampler->dir == 0) return 0; + ASSERT(resampler->dir == -1 || resampler->dir == 1); + + done = 0; + dt = (int)(delta * 65536.0 + 0.5); + if (dt == 0 || dt == (int)-0x80000000) return 0; + inv_dt = (int)(1.0 / delta * 65536.0 + 0.5); + SET_VOLUME_VARIABLES; + + if (VOLUMES_ARE_ZERO) dst = NULL; + + 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) + todo64 = ((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt); + else + todo64 = ((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt); + + if (todo64 < 0) + todo = 0; + else if (todo64 > dst_size - done) + todo = dst_size - done; + else + todo = (long) todo64; + + 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 */ + int todo_clocks = todo << 16, todo_clocks_set = todo_clocks; + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) { + // TODO: check what happens when multiple tempo slides occur per row + HEAVYASSERT(pos >= resampler->start); + POKE_ALIAS(0); + pos--; + x += SRC_CHANNELS; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo_clocks ) { + todo_clocks_set = todo_clocks; + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + todo_clocks -= todo_clocks_set; + while ( resampler->last_clock < todo_clocks_set ) + { + POKE_ALIAS(2); + pos--; + x -= SRC_CHANNELS; + } + todo = todo_clocks_set >> 16; + MIX_ALIAS( todo ); + } + } else if (quality <= DUMB_RQ_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_RQ_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 ) { + lanczos_resampler_set_rate( resampler->fir_resampler[0], delta ); + lanczos_resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) && + pos >= resampler->start ) + { + POKE_FIR(0); + pos--; + x -= SRC_CHANNELS; + } + if ( !lanczos_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 */ + int todo_clocks = todo << 16, todo_clocks_set = todo_clocks; + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + POKE_ALIAS(0); + pos++; + x += SRC_CHANNELS; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo_clocks ) { + todo_clocks_set = todo_clocks; + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + todo_clocks -= todo_clocks_set; + while ( resampler->last_clock < todo_clocks_set ) + { + POKE_ALIAS(-2); + pos++; + x += SRC_CHANNELS; + } + todo = todo_clocks_set >> 16; + MIX_ALIAS( todo ); + } + } else if (quality <= DUMB_RQ_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_RQ_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 ) { + lanczos_resampler_set_rate( resampler->fir_resampler[0], delta ); + lanczos_resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) && + pos < resampler->end ) + { + POKE_FIR(0); + pos++; + x += SRC_CHANNELS; + } + if ( !lanczos_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; } + + 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 */ + PEEK_ALIAS; + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, backwards */ + MIX_LINEAR(=, 0, 2, 1); + } else if (quality <= DUMB_RQ_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 */ + PEEK_ALIAS; + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, forwards */ + MIX_LINEAR(=, 0, 1, 2); + } else if (quality <= DUMB_RQ_CUBIC) { + /* Cubic interpolation, forwards */ + MIX_CUBIC(=, 0, x, src, 0, 1, 2, pos); + } else { + /* FIR resampling, forwards */ + PEEK_FIR; + } + } +} + + + +#undef MIX_ZEROS +#undef MIX_CUBIC +#undef MIX_LINEAR +#undef MIX_ALIAS +#undef MIX_FIR +#undef PEEK_ALIAS +#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/Frameworks/Dumb/dumb/src/helpers/resample.c b/Frameworks/Dumb/dumb/src/helpers/resample.c index 3baafd0da..cb3683266 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resample.c +++ b/Frameworks/Dumb/dumb/src/helpers/resample.c @@ -1,385 +1,410 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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" - - - -/* 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 - - - -/* 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_LINEAR - * 2 - DUMB_RQ_CUBIC - nicest - * - * Values outside the range 0-2 will behave the same as the nearest - * value within the range. - */ -int dumb_resampling_quality = DUMB_RQ_CUBIC; - - - -//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16)) -//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14) -#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32)) -#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32)) - - - -/* Executes the content 'iterator' times. - * Clobbers the 'iterator' variable. - * The loop is unrolled by four. - */ -#define LOOP4(iterator, CONTENT) \ -{ \ - if ((iterator) & 2) { \ - CONTENT; \ - CONTENT; \ - } \ - if ((iterator) & 1) { \ - CONTENT; \ - } \ - (iterator) >>= 2; \ - while (iterator) { \ - CONTENT; \ - CONTENT; \ - CONTENT; \ - CONTENT; \ - (iterator)--; \ - } \ -} - - - -#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]; - -static void init_cubic(void) -{ - unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */ - static int done = 0; - if (done) return; - done = 1; - 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); - } -} - - - -/* 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 SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \ - c = (x2 - x0) >> 1; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol) -*/ -#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) -#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 SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = (3 * (x1 - x2) + (x3 - x0)) << 7; \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \ - c = (x2 - x0) << 7; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol) -*/ -#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) (int)((LONG_LONG)(x) * (vol << 10) >> 32) -#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 SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = 3 * (x1 - x2) + (x3 - x0); \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \ - c = (x2 - x0) << 15; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol) -*/ -#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) (int)((LONG_LONG)(x) * (vol << 12) >> 32) -#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, long pos, long start, long end) -{ - if (n == 8) - dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end); - else if (n == 16) - dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end); - else - dumb_reset_resampler(resampler, src, src_channels, pos, start, end); -} - - - -DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end) -{ - if (n == 8) - return dumb_start_resampler_8(src, src_channels, pos, start, end); - else if (n == 16) - return dumb_start_resampler_16(src, src_channels, pos, start, end); - else - return dumb_start_resampler(src, src_channels, pos, start, end); -} - - - -long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float 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); -} - - - -long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float 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); -} - - - -long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float 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); -} - - - -long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float 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); -} - - - -void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float 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); -} - - - -void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float 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); -} - - - -void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float 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); -} - - - -void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float 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); -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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/blip_buf.h" +#include "internal/lanczos_resampler.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_LINEAR + * 2 - DUMB_RQ_CUBIC + * 3 - DUMB_RQ_FIR - nicest + * + * Values outside the range 0-3 will behave the same as the nearest + * value within the range. + */ +int dumb_resampling_quality = DUMB_RQ_CUBIC; + + + +//#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 MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32)) +#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32)) + + + +/* 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]; + +/*static*/ void init_cubic(void) +{ + unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */ + static int done = 0; + if (done) return; + done = 1; + 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); + } + + lanczos_init(); +} + + + +/* 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) (x >> 8) +#define FIR(x) (x >> 8) +#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos)) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \ + c = (x2 - x0) >> 1; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol) +*/ +#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) +#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) (x) +#define FIR(x) (x) +#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos)) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = (3 * (x1 - x2) + (x3 - x0)) << 7; \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \ + c = (x2 - x0) << 7; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol) +*/ +#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) (int)((LONG_LONG)(x) * (vol << 10) >> 32) +#include "resample.inc" + +/* Create resamplers for 8-bit source samples. */ +#define SUFFIX _8 +#define SRCTYPE signed char +#define SRCBITS 8 +#define ALIAS(x) (x << 8) +#define FIR(x) (x << 8) +#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = 3 * (x1 - x2) + (x3 - x0); \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \ + c = (x2 - x0) << 15; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol) +*/ +#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) (int)((LONG_LONG)(x) * (vol << 12) >> 32) +#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, long pos, long start, long 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, long pos, long start, long 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); +} + + + +long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float 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); +} + + + +long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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); +} + + + +long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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); +} + + + +long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float 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); +} + + + +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); +} + + + +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); +} + + + +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); +} + + + +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/Frameworks/Dumb/dumb/src/helpers/resample.inc b/Frameworks/Dumb/dumb/src/helpers/resample.inc index 7a80423ac..bf520559d 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resample.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resample.inc @@ -1,167 +1,403 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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, long pos, long start, long end) -{ - 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; - resampler->min_quality = 0; - resampler->max_quality = DUMB_RQ_N_LEVELS - 1; - for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0; - resampler->overshot = -1; -} - - - -DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end) -{ - DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); - if (!resampler) return NULL; - dumb_reset_resampler(resampler, src, src_channels, pos, start, end); - return resampler; -} - - - -/* Create mono source resampler. */ -#define SUFFIX2 _1 -#define SRC_CHANNELS 1 -#define DIVIDE_BY_SRC_CHANNELS(x) (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 float volume -#define MONO_DEST_VOLUME_VARIABLES vol -#define MONO_DEST_VOLUME_ZEROS 0 -#define SET_MONO_DEST_VOLUME_VARIABLES vol = (int)floor(volume * 65536.0 + 0.5) -#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0) -#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[offset], vol) -#define STEREO_DEST_MIX_ALIAS(op, offset) { \ - int xm = x[offset]; \ - *dst++ op ALIAS(xm, lvol); \ - *dst++ op ALIAS(xm, rvol); \ -} -#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol) -#define STEREO_DEST_MIX_LINEAR(op, o0, o1) { \ - int xm = LINEAR(x[o0], x[o1]); \ - *dst++ op MULSC(xm, lvol); \ - *dst++ op MULSC(xm, rvol); \ -} -#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol) -#define STEREO_DEST_MIX_CUBIC(op, 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); \ -} -#include "resamp2.inc" - -/* Create stereo source resampler. */ -#define SUFFIX2 _2 -#define SRC_CHANNELS 2 -#define DIVIDE_BY_SRC_CHANNELS(x) ((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 float volume_left, float volume_right -#define MONO_DEST_VOLUME_VARIABLES lvol, rvol -#define MONO_DEST_VOLUME_ZEROS 0, 0 -#define SET_MONO_DEST_VOLUME_VARIABLES { \ - lvol = (int)floor(volume_left * 65536.0 + 0.5); \ - rvol = (int)floor(volume_right * 65536.0 + 0.5); \ -} -#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0) -#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol) -#define STEREO_DEST_MIX_ALIAS(op, offset) { \ - *dst++ op ALIAS(x[(offset)*2], lvol); \ - *dst++ op ALIAS(x[(offset)*2+1], rvol); \ -} -#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol) -#define STEREO_DEST_MIX_LINEAR(op, 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); \ -} -#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op \ - CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol) + \ - CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol) -#define STEREO_DEST_MIX_CUBIC(op, 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); \ -} -#include "resamp2.inc" - - - -void dumb_end_resampler(DUMB_RESAMPLER *resampler) -{ - if (resampler) - free(resampler); -} - - - -#undef CUBICVOL -#undef CUBIC -#undef LINEAR -#undef ALIAS -#undef SRCBITS -#undef SRCTYPE -#undef SUFFIX +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, long pos, long start, long 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->last_clock = 0; + resampler->last_amp[0] = 0; + resampler->last_amp[1] = 0; + blip_clear(resampler->blip_buffer[0]); + blip_clear(resampler->blip_buffer[1]); + resampler->fir_resampler_ratio = 0; + lanczos_resampler_clear(resampler->fir_resampler[0]); + lanczos_resampler_clear(resampler->fir_resampler[1]); +} + + + +DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end, int quality) +{ + DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); + if (!resampler) return NULL; + resampler->blip_buffer[0] = blip_new( 256 ); + if (!resampler->blip_buffer[0]) + { + free(resampler); + return NULL; + } + resampler->blip_buffer[1] = blip_new( 256 ); + if (!resampler->blip_buffer[1]) + { + free(resampler->blip_buffer[0]); + free(resampler); + return NULL; + } + blip_set_rates(resampler->blip_buffer[0], 65536, 1); + blip_set_rates(resampler->blip_buffer[1], 65536, 1); + 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; \ + 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) (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 +#define SET_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume ) { \ + volr = (int)(volume->volume * 16777216.0); \ + vold = (int)(volume->delta * 16777216.0); \ + volt = (int)(volume->target * 16777216.0); \ + volm = (int)(volume->mix * 16777216.0); \ + 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 POKE_ALIAS(offset) { \ + int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \ + resampler->last_amp[0] += delta; \ + if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \ + resampler->last_clock += inv_dt; \ +} +#define POKE_FIR(offset) { \ + lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \ +} +#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol ) +#define MONO_DEST_PEEK_FIR *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol ) +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol ); \ + UPDATE_VOLUME( volume, vol ); \ +} +#define ADVANCE_FIR lanczos_resampler_remove_sample( resampler->fir_resampler[0] ) +#define MONO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], vol ); \ + n++; \ + UPDATE_VOLUME( volume, vol ); \ + ); \ +} +#define STEREO_DEST_PEEK_ALIAS { \ + int sample = blip_peek_sample( resampler->blip_buffer[0] ); \ + *dst++ = MULSC( sample, lvol ); \ + *dst++ = MULSC( sample, rvol ); \ +} +#define STEREO_DEST_PEEK_FIR { \ + int sample = lanczos_resampler_get_sample( resampler->fir_resampler[0] ); \ + *dst++ = MULSC( sample, lvol ); \ + *dst++ = MULSC( sample, rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + int sample = lanczos_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 ); \ +} +#define STEREO_DEST_MIX_ALIAS(count) { \ + int sample, n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + LOOP4( count, \ + sample = blip_samples[n++]; \ + *dst++ += MULSC( sample, lvol ); \ + *dst++ += MULSC( sample, rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \ + if ( upd ) UPDATE_VOLUME( volume, vol ); \ +} +#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 MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol); \ + if ( upd ) UPDATE_VOLUME( volume, vol ); \ +} +#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 ); \ +} +#include "resamp2.inc" + +/* Create stereo source resampler. */ +#define SUFFIX2 _2 +#define SRC_CHANNELS 2 +#define DIVIDE_BY_SRC_CHANNELS(x) ((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 = (int)(volume_left->volume * 16777216.0); \ + lvold = (int)(volume_left->delta * 16777216.0); \ + lvolt = (int)(volume_left->target * 16777216.0); \ + lvolm = (int)(volume_left->mix * 16777216.0); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = (int)(volume_right->volume * 16777216.0); \ + rvold = (int)(volume_right->delta * 16777216.0); \ + rvolt = (int)(volume_right->target * 16777216.0); \ + rvolm = (int)(volume_right->mix * 16777216.0); \ + 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 POKE_ALIAS(offset) { \ + int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \ + int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \ + resampler->last_amp[0] += deltal; \ + resampler->last_amp[1] += deltar; \ + if ( deltal ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, deltal ); \ + if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \ + resampler->last_clock += inv_dt; \ +} +#define POKE_FIR(offset) { \ + lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \ + lanczos_resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \ +} +#define MONO_DEST_PEEK_ALIAS { \ + *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \ + MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \ +} +#define MONO_DEST_PEEK_FIR { \ + *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define ADVANCE_FIR { \ + lanczos_resampler_remove_sample( resampler->fir_resampler[0] ); \ + lanczos_resampler_remove_sample( resampler->fir_resampler[1] ); \ +} +#define MONO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], lvol ) + MULSC( blip_samples[256+n], rvol ); \ + n++; \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define STEREO_DEST_PEEK_ALIAS { \ + *dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \ + *dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \ +} +#define STEREO_DEST_PEEK_FIR { \ + *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], lvol); \ + *dst++ += MULSC( blip_samples[256+n], rvol); \ + n++; \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + 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_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 MONO_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) + \ + 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 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 ); \ +} +#include "resamp2.inc" + + + +void dumb_end_resampler(DUMB_RESAMPLER *resampler) +{ + if (resampler) + free(resampler); +} + + + +#undef CUBICVOL +#undef CUBIC +#undef LINEAR +#undef ALIAS +#undef FIR +#undef SRCBITS +#undef SRCTYPE +#undef SUFFIX diff --git a/Frameworks/Dumb/dumb/src/helpers/riff.c b/Frameworks/Dumb/dumb/src/helpers/riff.c new file mode 100644 index 000000000..22e0f9136 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/riff.c @@ -0,0 +1,85 @@ +#include "dumb.h" +#include "internal/riff.h" + +#include + +struct riff * riff_parse( DUMBFILE * f, long offset, long 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 > 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/Frameworks/Dumb/dumb/src/helpers/sampbuf.c b/Frameworks/Dumb/dumb/src/helpers/sampbuf.c index 6a80f1fac..488db4490 100644 --- a/Frameworks/Dumb/dumb/src/helpers/sampbuf.c +++ b/Frameworks/Dumb/dumb/src/helpers/sampbuf.c @@ -1,64 +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, long 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 **allocate_sample_buffer(int n_channels, long 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 destroy_sample_buffer(sample_t **samples) -{ - if (samples) { - free(samples[0]); - free(samples); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, long 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 **allocate_sample_buffer(int n_channels, long 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 destroy_sample_buffer(sample_t **samples) +{ + if (samples) { + free(samples[0]); + free(samples); + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/silence.c b/Frameworks/Dumb/dumb/src/helpers/silence.c index 4d5fdcf4d..794ae8315 100644 --- a/Frameworks/Dumb/dumb/src/helpers/silence.c +++ b/Frameworks/Dumb/dumb/src/helpers/silence.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * silence.c - Silencing helper. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include "dumb.h" - - - -void dumb_silence(sample_t *samples, long length) -{ - memset(samples, 0, length * sizeof(*samples)); -} - +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * silence.c - Silencing helper. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include "dumb.h" + + + +void dumb_silence(sample_t *samples, long length) +{ + memset(samples, 0, length * sizeof(*samples)); +} + diff --git a/Frameworks/Dumb/dumb/src/helpers/stdfile.c b/Frameworks/Dumb/dumb/src/helpers/stdfile.c index 2f02539a9..ec042ecac 100644 --- a/Frameworks/Dumb/dumb/src/helpers/stdfile.c +++ b/Frameworks/Dumb/dumb/src/helpers/stdfile.c @@ -1,93 +1,146 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * stdfile.c - stdio file input module. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" - - - -static void *dumb_stdfile_open(const char *filename) -{ - return fopen(filename, "rb"); -} - - - -static int dumb_stdfile_skip(void *f, long n) -{ - return fseek(f, n, SEEK_CUR); -} - - - -static int dumb_stdfile_getc(void *f) -{ - return fgetc(f); -} - - - -static long dumb_stdfile_getnc(char *ptr, long n, void *f) -{ - return fread(ptr, 1, n, f); -} - - - -static void dumb_stdfile_close(void *f) -{ - fclose(f); -} - - - -static DUMBFILE_SYSTEM stdfile_dfs = { - &dumb_stdfile_open, - &dumb_stdfile_skip, - &dumb_stdfile_getc, - &dumb_stdfile_getnc, - &dumb_stdfile_close -}; - - - -void dumb_register_stdfiles(void) -{ - register_dumbfile_system(&stdfile_dfs); -} - - - -static DUMBFILE_SYSTEM stdfile_dfs_leave_open = { - NULL, - &dumb_stdfile_skip, - &dumb_stdfile_getc, - &dumb_stdfile_getnc, - NULL -}; - - - -DUMBFILE *dumbfile_open_stdfile(FILE *p) -{ - DUMBFILE *d = dumbfile_open_ex(p, &stdfile_dfs_leave_open); - - return d; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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 dumb_stdfile_skip(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_CUR); +} + + + +static int dumb_stdfile_getc(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fgetc(file->file); +} + + + +static long dumb_stdfile_getnc(char *ptr, long n, void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fread(ptr, 1, n, file->file); +} + + + +static void dumb_stdfile_close(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + fclose(file->file); + free(f); +} + + + +static void dumb_stdfile_noclose(void *f) +{ + free(f); +} + + + +static int dumb_stdfile_seek(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_SET); +} + + + +static long 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 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 *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/Frameworks/Dumb/dumb/src/helpers/tarray.c b/Frameworks/Dumb/dumb/src/helpers/tarray.c new file mode 100644 index 000000000..f3ba422d8 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/tarray.c @@ -0,0 +1,175 @@ +#include "internal/tarray.h" + +#include + + /* + Structures which contain the play times of each pattern and row combination in the song, + not guaranteed to be valid for the whole song until the loop status is no longer zero. + The initial count and restart count will both be zero on song start, then both will be + incremented until the song loops. Restart count will be reset to zero on loop for all + rows which have a time equal to or greater than the loop start point, so time keeping + functions will know which timestamp the song is currently located at. + + Timestamp lists are guaranteed to be allocated in blocks of 16 timestamps at a time. + */ + + /* + We don't need full timekeeping because the player loop only wants the first play time + of the loop start order/row. We also don't really want full timekeeping because it + involves a lot of memory allocations, which is also slow. + */ + +#undef FULL_TIMEKEEPING + +typedef struct DUMB_IT_ROW_TIME +{ + unsigned int count, restart_count; +#ifndef FULL_TIMEKEEPING + LONG_LONG first_time; +#else + LONG_LONG * times; +#endif +} DUMB_IT_ROW_TIME; + +void * timekeeping_array_create(size_t size) +{ + size_t * _size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * size ); + if ( _size ) { + *_size = size; + } + return _size; +} + +void timekeeping_array_destroy(void * array) +{ +#ifdef FULL_TIMEKEEPING + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + for (i = 0; i < *size; i++) { + if (s[i].times) free(s[i].times); + } +#endif + + free(array); +} + +void * timekeeping_array_dup(void * array) +{ + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + size_t * new_size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * *size ); + if ( new_size ) { + DUMB_IT_ROW_TIME * new_s = (DUMB_IT_ROW_TIME *)(new_size + 1); + + *new_size = *size; + + for (i = 0; i < *size; i++) { + new_s[i].count = s[i].count; + new_s[i].restart_count = s[i].restart_count; + +#ifndef FULL_TIMEKEEPING + new_s[i].first_time = s[i].first_time; +#else + if ( s[i].times ) { + size_t time_count = ( s[i].count + 15 ) & ~15; + new_s[i].times = (LONG_LONG *) malloc( sizeof(LONG_LONG) * time_count ); + if ( new_s[i].times == (void *)0 ) { + timekeeping_array_destroy( new_size ); + return (void *) 0; + } + memcpy( new_s[i].times, s[i].times, sizeof(LONG_LONG) * s[i].count ); + } +#endif + } + } + + return new_size; +} + +void timekeeping_array_reset(void * array, size_t loop_start) +{ + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + DUMB_IT_ROW_TIME * s_loop_start = s + loop_start; + LONG_LONG loop_start_time; + + if ( loop_start >= *size || s_loop_start->count < 1 ) return; + +#ifndef FULL_TIMEKEEPING + loop_start_time = s_loop_start->first_time; +#else + loop_start_time = s_loop_start->times[0]; +#endif + + for ( i = 0; i < *size; i++ ) { +#ifndef FULL_TIMEKEEPING + if ( s[i].count && s[i].first_time >= loop_start_time ) { +#else + if ( s[i].count && s[i].times[0] >= loop_start_time ) { +#endif + s[i].restart_count = 0; + } + } +} + +void timekeeping_array_push(void * array, size_t index, LONG_LONG time) +{ +#ifdef FULL_TIMEKEEPING + size_t i; + size_t time_count; +#endif + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return; + +#ifndef FULL_TIMEKEEPING + if ( !s[index].count++ ) + s[index].first_time = time; +#else + time_count = ( s[index].count + 16 ) & ~15; + + s[index].times = (LONG_LONG *) realloc( s[index].times, sizeof(LONG_LONG) * time_count ); + + s[index].times[s[index].count++] = time; +#endif +} + +void timekeeping_array_bump(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return; + + s[index].restart_count++; +} + +unsigned int timekeeping_array_get_count(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return 0; + + return s[index].count; +} + +LONG_LONG timekeeping_array_get_item(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size || s[index].restart_count >= s[index].count) return 0; + +#ifndef FULL_TIMEKEEPING + return s[index].first_time; +#else + return s[index].times[s[index].restart_count]; +#endif +} diff --git a/Frameworks/Dumb/dumb/src/it/itload.c b/Frameworks/Dumb/dumb/src/it/itload.c index a26f5e102..300042330 100644 --- a/Frameworks/Dumb/dumb/src/it/itload.c +++ b/Frameworks/Dumb/dumb/src/it/itload.c @@ -1,42 +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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/itload2.c b/Frameworks/Dumb/dumb/src/it/itload2.c index 2dd65a71e..15cff1d0a 100644 --- a/Frameworks/Dumb/dumb/src/it/itload2.c +++ b/Frameworks/Dumb/dumb/src/it/itload2.c @@ -1,29 +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 *dumb_load_it(const char *filename) -{ - DUH *duh = dumb_load_it_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_load_it(const char *filename) +{ + DUH *duh = dumb_load_it_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/itmisc.c b/Frameworks/Dumb/dumb/src/it/itmisc.c index fc62c188f..c891bd457 100644 --- a/Frameworks/Dumb/dumb/src/it/itmisc.c +++ b/Frameworks/Dumb/dumb/src/it/itmisc.c @@ -1,247 +1,247 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itmisc.c - Miscellaneous functions relating / / \ \ - * to module files. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh) -{ - return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT); -} - - - -const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->song_message : NULL; -} - - - -int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_orders : 0; -} - - - -int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_samples : 0; -} - - - -int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_instruments : 0; -} - - - -const unsigned char *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 *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 *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 *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 dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->global_volume : 0; -} - - - -void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv) -{ - if (sd) sd->global_volume = gv; -} - - - -int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->mixing_volume : 0; -} - - - -void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv) -{ - if (sd) sd->mixing_volume = mv; -} - - - -int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->speed : 0; -} - - - -void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed) -{ - if (sd) sd->speed = speed; -} - - - -int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->tempo : 0; -} - - - -void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo) -{ - if (sd) sd->tempo = tempo; -} - - - -int 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 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 dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->order : -1; -} - - - -int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->row : -1; -} - - - -int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->globalvolume : 0; -} - - - -void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv) -{ - if (sr) sr->globalvolume = gv; -} - - - -int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->tempo : 0; -} - - - -void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo) -{ - if (sr) sr->tempo = tempo; -} - - - -int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->speed : 0; -} - - - -void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed) -{ - if (sr) sr->speed = speed; -} - - - -int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel) -{ - return sr ? sr->channel[channel].channelvolume : 0; -} - - - -void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume) -{ - if (sr) sr->channel[channel].channelvolume = volume; -} - - - -void 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 dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel) -{ - return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itmisc.c - Miscellaneous functions relating / / \ \ + * to module files. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh) +{ + return duh_get_raw_sigdata(duh, -1, SIGTYPE_IT); +} + + + +const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->song_message : NULL; +} + + + +int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_orders : 0; +} + + + +int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_samples : 0; +} + + + +int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_instruments : 0; +} + + + +const unsigned char *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 *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 *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 *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 dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->global_volume : 0; +} + + + +void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv) +{ + if (sd) sd->global_volume = gv; +} + + + +int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->mixing_volume : 0; +} + + + +void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv) +{ + if (sd) sd->mixing_volume = mv; +} + + + +int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->speed : 0; +} + + + +void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed) +{ + if (sd) sd->speed = speed; +} + + + +int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->tempo : 0; +} + + + +void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo) +{ + if (sd) sd->tempo = tempo; +} + + + +int 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 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 dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->order : -1; +} + + + +int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->row : -1; +} + + + +int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->globalvolume : 0; +} + + + +void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv) +{ + if (sr) sr->globalvolume = gv; +} + + + +int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->tempo : 0; +} + + + +void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo) +{ + if (sr) sr->tempo = tempo; +} + + + +int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->speed : 0; +} + + + +void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed) +{ + if (sr) sr->speed = speed; +} + + + +int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel) +{ + return sr ? sr->channel[channel].channelvolume : 0; +} + + + +void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume) +{ + if (sr) sr->channel[channel].channelvolume = volume; +} + + + +void 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 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/Frameworks/Dumb/dumb/src/it/itorder.c b/Frameworks/Dumb/dumb/src/it/itorder.c index 6959f0544..c3fe51cbe 100644 --- a/Frameworks/Dumb/dumb/src/it/itorder.c +++ b/Frameworks/Dumb/dumb/src/it/itorder.c @@ -1,63 +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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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/Frameworks/Dumb/dumb/src/it/itread.c b/Frameworks/Dumb/dumb/src/it/itread.c index 532fb65fe..0ed0b1b9a 100644 --- a/Frameworks/Dumb/dumb/src/it/itread.c +++ b/Frameworks/Dumb/dumb/src/it/itread.c @@ -1,1202 +1,1411 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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" - - - -#define INVESTIGATE_OLD_INSTRUMENTS - - - -static int it_seek(DUMBFILE *f, long offset) -{ - long pos = dumbfile_pos(f); - - if (pos > offset) - return -1; - - if (pos < offset) - if (dumbfile_skip(f, offset - pos)) - return -1; - - return 0; -} - - - -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long dword; - - - -static unsigned char *sourcebuf = NULL; -static unsigned char *sourcepos = NULL; -static unsigned char *sourceend; -static int rembits = 0; - - - -static int readblock(DUMBFILE *f) -{ - long size; - int c; - - size = dumbfile_igetw(f); - if (size < 0) - return size; - - sourcebuf = malloc(size); - if (!sourcebuf) - return -1; - - c = dumbfile_getnc((char *)sourcebuf, size, f); - if (c < size) { - free(sourcebuf); - sourcebuf = NULL; - return -1; - } - - sourcepos = sourcebuf; - sourceend = sourcebuf + size; - rembits = 8; - return 0; -} - - - -static void freeblock(void) -{ - free(sourcebuf); - sourcebuf = NULL; -} - - - -static int readbits(int bitwidth) -{ - int val = 0; - int b = 0; - - if (sourcepos >= sourceend) return val; - - while (bitwidth > rembits) { - val |= *sourcepos++ << b; - if (sourcepos >= sourceend) return val; - b += rembits; - bitwidth -= rembits; - rembits = 8; - } - - val |= (*sourcepos & ((1 << bitwidth) - 1)) << b; - *sourcepos >>= bitwidth; - 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 cmwt) -{ - int blocklen, blockpos; - byte bitwidth; - word val; - char d1, d2; - - memset(data, 0, len * sizeof(*data)); - - while (len > 0) { - //Read a block of compressed data: - if (readblock(f)) - 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); - //Check for bit width change: - - if (bitwidth < 7) { //Method 1: - if (val == (1 << (bitwidth - 1))) { - val = (word)readbits(3) + 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(); - 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++ = cmwt == 0x215 ? d2 : d1; - len--; - blockpos++; - } - freeblock(); - } - return 0; -} - - - -static int decompress16(DUMBFILE *f, short *data, int len, int cmwt) -{ - int blocklen, blockpos; - byte bitwidth; - long val; - short d1, d2; - - memset(data, 0, len * sizeof(*data)); - - while (len > 0) { - //Read a block of compressed data: - if (readblock(f)) - 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); - //Check for bit width change: - - if (bitwidth < 7) { //Method 1: - if (val == (1 << (bitwidth - 1))) { - val = readbits(4) + 1; - bitwidth = (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 = val < bitwidth ? val : val + 1; - continue; - } - } - else if (bitwidth == 17) { //Method 3 - if (val & 0x10000) { - bitwidth = (val + 1) & 0xFF; - continue; - } - } - else { //Illegal width, abort ? - freeblock(); - 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++ = cmwt == 0x215 ? d2 : d1; - len--; - blockpos++; - } - freeblock(); - } - 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); - 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; - - dumbfile_getnc(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(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 n; - - if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) - return -1; - - dumbfile_getnc(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(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); - - 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; - - return 0; -} - - - -static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f) -{ - if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE) - return -1; - - dumbfile_getnc(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(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) { - 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); - sample->vibrato_rate = dumbfile_getc(f); - sample->vibrato_waveform = dumbfile_getc(f); - - return dumbfile_error(f); -} - - - -static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f) -{ - long n; - - long 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 & 8) { - /* If the sample is packed, then we must unpack it. */ - - /** WARNING - unresolved business here... test with ModPlug? */ - - if (sample->flags & IT_SAMPLE_STEREO) - exit(37); // TODO: if this ever happens, maybe sample->length should be doubled below? - -/* -//#ifndef STEREO_SAMPLES_COUNT_AS_TWO - ASSERT(!(sample->flags & IT_SAMPLE_STEREO)); -//#endif -*/ - if (sample->flags & IT_SAMPLE_16BIT) - decompress16(f, sample->data, datasize, cmwt); - else - decompress8(f, sample->data, datasize, cmwt); - } else if (sample->flags & IT_SAMPLE_16BIT) { - 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 - 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(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 char n; - long 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 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[256]; - - 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(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(sigdata->channel_pan, DUMB_IT_N_CHANNELS, f); - dumbfile_getnc(sigdata->channel_volume, DUMB_IT_N_CHANNELS, f); - - if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || 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_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(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++) { - long 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(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(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++) { - long offset; - int m; - - if (it_seek(f, component[n].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - switch (component[n].type) { - - case IT_COMPONENT_SONG_MESSAGE: - sigdata->song_message = malloc(message_length + 1); - if (sigdata->song_message) { - if (dumbfile_getnc(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); - - 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 (it_seek(f, component[m].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (it_read_sample_data(cmwt, &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; - } - } - - free(buffer); - free(component); - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -DUH *dumb_read_it_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_load_sigdata(f); - printf("sigdata: %i\n", sigdata); - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 unsigned long 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) +{ + long 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; + long 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 = (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 = val < bitwidth ? val : val + 1; + continue; + } + } + else if (bitwidth == 17) { //Method 3 + if (val & 0x10000) { + bitwidth = (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); + 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); + if (envelope->n_nodes > 25) + envelope->n_nodes = 25; + 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; + + /*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 ) { + long 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, long *offset, DUMBFILE *f) +{ + /* XXX + if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE) + return -1;*/ + int hax = 0; + long 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); +} + +long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f) +{ + long 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++ = delta; + if (ptr >= end) break; + delta += compression_table[b >> 4]; + *ptr++ = delta; + } + + return 0; +} + + +static long it_read_sample_data(IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f) +{ + long n; + + long 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; + long 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 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++) { + long 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++) { + long 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 *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/Frameworks/Dumb/dumb/src/it/itread2.c b/Frameworks/Dumb/dumb/src/it/itread2.c index e152737e4..202b8bb96 100644 --- a/Frameworks/Dumb/dumb/src/it/itread2.c +++ b/Frameworks/Dumb/dumb/src/it/itread2.c @@ -1,29 +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 *dumb_read_it(DUMBFILE *f) -{ - DUH *duh = dumb_read_it_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_read_it(DUMBFILE *f) +{ + DUH *duh = dumb_read_it_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/itrender.c b/Frameworks/Dumb/dumb/src/it/itrender.c index b74b84526..1a9e2f905 100644 --- a/Frameworks/Dumb/dumb/src/it/itrender.c +++ b/Frameworks/Dumb/dumb/src/it/itrender.c @@ -1,3739 +1,6308 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itrender.c - Code to render an Impulse Tracker / / \ \ - * module. | < / \_ - * | \/ /\ / - * Written - painstakingly - by entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -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; - - 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->channel_volume = src->channel_volume; - - dst->volume = src->volume; - dst->pan = src->pan; - - dst->note = src->note; - - 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->tremolo_speed = src->tremolo_speed; - dst->tremolo_depth = src->tremolo_depth; - dst->tremolo_time = src->tremolo_time; - - dst->sample_vibrato_time = src->sample_vibrato_time; - dst->sample_vibrato_depth = src->sample_vibrato_depth; - - dst->slide = src->slide; - dst->delta = src->delta; - - 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->time_lost = src->time_lost; - - 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->arpeggio = src->arpeggio; - dst->retrig = src->retrig; - dst->xm_retrig = src->xm_retrig; - dst->retrig_tick = src->retrig_tick; - - dst->tremor_time = src->tremor_time; - - dst->portamento = src->portamento; - dst->toneporta = src->toneporta; - dst->destnote = src->destnote; - - 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->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->playing = dup_playing(src->playing, dst, src); -} - - - -/* 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->sigdata = src->sigdata; - - dst->n_channels = n_channels; - - 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->pat_loop_row = src->pat_loop_row; - - 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->click_remover = NULL; - - dst->callbacks = callbacks; - - return dst; -} - - - -static 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(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) -{ - sample_t currsample = state->currsample; - sample_t prevsample = state->prevsample; - - float a, b, c; - - 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; - 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 MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32)) -#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; -} - -#undef LOG10 - - - -static 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 0 -/** 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 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 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 -}; - -#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 void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - sigrenderer->globalvolslide = 0; - sigrenderer->temposlide = 0; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - channel->volslide = 0; - channel->xm_volslide = 0; - channel->panslide = 0; - channel->channelvolslide = 0; - channel->arpeggio = 0; - 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->playing) { - channel->playing->vibrato_n = 0; - channel->playing->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - } - } -} - - - -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_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->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->sample->flags & IT_SAMPLE_PINGPONG_LOOP) - playing->resampler.pickup = &it_pickup_pingpong_loop; - else - playing->resampler.pickup = &it_pickup_loop; - } 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, long pos) -{ - int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; - int channels = playing->sample->flags & IT_SAMPLE_STEREO ? 2 : 1; - dumb_reset_resampler_n(bits, &playing->resampler, playing->sample->data, channels, pos, 0, 0); - playing->resampler.pickup_data = playing; - playing->time_lost = 0; - playing->flags &= ~IT_PLAYING_DEAD; - it_playing_update_resamplers(playing); -} - - - -static void update_retrig(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->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->retrig_tick = channel->retrig & 0x0F; - } - } -} - - - -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) { - playing->vibrato_time += playing->vibrato_n * - (playing->vibrato_speed << 2); - playing->tremolo_time += playing->tremolo_speed << 2; - } - } -} - - - -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) { - channel->volume += channel->volslide; - if (channel->volume > 64) { - if (channel->volslide >= 0) - channel->volume = 64; - else - channel->volume = 0; - } - } - - if (channel->panslide && !IT_IS_SURROUND(channel->pan)) { - if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { - if (channel->panslide == -128) - channel->truepan = 32; - else - channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64); - } else { - 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; - } - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; - } - - update_tremor(channel); - - channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8); - channel->arpeggio &= 0xFFF; - - update_retrig(channel); - - 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_smooth_effects(sigrenderer); -} - - - -// 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: - sigrenderer->breakrow = 0; - sigrenderer->processorder = entry->effectvalue - 1; - sigrenderer->processrow = 0xFFFE; - break; - - case IT_S: - { - unsigned char effectvalue = entry->effectvalue; - if (effectvalue == 0) - effectvalue = channel->lastS; - channel->lastS = effectvalue; - switch (effectvalue >> 4) { - //case IT_S7: - case IT_S_PATTERN_LOOP: - { - unsigned char v = effectvalue & 15; - if (v == 0) - channel->pat_loop_row = sigrenderer->processrow; - else { - if (channel->pat_loop_count == 0) { - channel->pat_loop_count = v; - sigrenderer->breakrow = channel->pat_loop_row; - if (sigrenderer->sigdata->flags & 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 < 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) { - sigrenderer->breakrow = channel->pat_loop_row; - if (sigrenderer->sigdata->flags & 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 < 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) { - 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 < 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; - } - } - 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))) - 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; - } -} - - - -static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - channel->playing->volume_envelope.next_node = 0; - channel->playing->volume_envelope.tick = 0; - channel->playing->pan_envelope.next_node = 0; - channel->playing->pan_envelope.tick = 0; - 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 (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->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 (!(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 it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - unsigned char nna; - int i; - - if (channel->playing) { -#ifdef INVALID_NOTES_CAUSE_NOTE_CUT - if (channel->note == IT_NOTE_OFF) - nna = NNA_NOTE_OFF; - else if (channel->note >= 120 || !channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) - nna = NNA_NOTE_CUT; - else - nna = channel->playing->instrument->new_note_action; -#else - if (channel->note == IT_NOTE_CUT) - nna = NNA_NOTE_CUT; - if (channel->note >= 120) - nna = NNA_NOTE_OFF; - else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) - nna = NNA_NOTE_CUT; - else - nna = channel->playing->instrument->new_note_action; -#endif - - switch (nna) { - case NNA_NOTE_CUT: - free(channel->playing); - channel->playing = NULL; - 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; - } - } - - 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; - } - } -/** 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(channel->playing); - - channel->playing = malloc(sizeof(*channel->playing)); - - if (!channel->playing) - return; - - channel->playing->flags = 0; - 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->channel_volume = channel->channelvolume; - channel->playing->note = channel->truenote; - 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->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - channel->playing->tremolo_time = 0; - channel->playing->sample_vibrato_time = 0; - channel->playing->sample_vibrato_depth = 0; - channel->playing->slide = 0; - channel->playing->volume_envelope.next_node = 0; - channel->playing->volume_envelope.tick = 0; - channel->playing->pan_envelope.next_node = 0; - channel->playing->pan_envelope.tick = 0; - channel->playing->pitch_envelope.next_node = 0; - channel->playing->pitch_envelope.tick = 0; - 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 (!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, 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); - } - } -} - - - -/* 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_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) - sigrenderer->tick = sigrenderer->speed = entry->effectvalue; - else if (sigdata->flags & IT_WAS_AN_XM) { - 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; - sigrenderer->processrow = 0xFFFE; - break; - - case IT_VOLSLIDE_VIBRATO: - if (channel->playing) { - channel->playing->vibrato_speed = channel->lastHspeed; - channel->playing->vibrato_depth = channel->lastHdepth; - channel->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 ((v & 0x0F) == 0) { /* Dx0 */ - channel->volslide = v >> 4; - if (channel->volslide == 15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume += 15; - if (channel->volume > 64) channel->volume = 64; - } - } else if ((v & 0xF0) == 0) { /* D0x */ - channel->volslide = -v; - if (channel->volslide == -15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume -= 15; - if (channel->volume > 64) channel->volume = 0; - } - } 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) { - 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 (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide -= (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide -= (v & 15) << 2; - else - channel->portamento -= v << 4; - } - } - break; - case IT_PORTAMENTO_UP: - { - unsigned char v = entry->effectvalue; - if (sigdata->flags & IT_WAS_AN_XM) { - 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 (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide += (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide += (v & 15) << 2; - else - 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: - { - 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 <<= 3; - else - depth <<= 2; - channel->lastHdepth = depth; - } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; - } - } - break; - case IT_TREMOR: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastI; - else if (!(sigdata->flags & IT_OLD_EFFECTS)) { - if (v & 0xF0) v -= 0x10; - if (v & 0x0F) v -= 0x01; - } - 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 (v == 0) - v = channel->lastJ; - channel->lastJ = v; - } - channel->arpeggio = v; - } - 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 (offset < end) - it_playing_reset_resamplers(playing, offset); - else if (sigdata->flags & IT_OLD_EFFECTS) - it_playing_reset_resamplers(playing, end); - } - } - } - 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) - p >>= 6; - else { - 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 (!IT_IS_SURROUND(channel->pan)) { - 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; - } 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(channel); - } else - update_retrig(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); - break; - case IT_TREMOLO: - { - unsigned char speed = entry->effectvalue >> 4; - unsigned char depth = entry->effectvalue & 15; - if (speed == 0) - speed = channel->lastRspeed; - channel->lastRspeed = speed; - if (depth == 0) - depth = channel->lastRdepth; - channel->lastRdepth = depth; - if (channel->playing) { - channel->playing->tremolo_speed = speed; - channel->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: - //case IT_S_SET_GLISSANDO_CONTROL: - //case IT_S_FINETUNE: - //case IT_S_SET_VIBRATO_WAVEFORM: - //case IT_S_SET_TREMOLO_WAVEFORM: - //case IT_S_SET_PANBRELLO_WAVEFORM: - /* Waveforms for commands S3x, S4x and S5x: - * 0: Sine wave - * 1: Ramp down - * 2: Square wave - * 3: Random wave - */ - case IT_S_FINE_PATTERN_DELAY: - sigrenderer->tick += effectvalue & 15; - break; - //case IT_S7: - 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; - break; - case IT_S_SET_SURROUND_SOUND: - if ((effectvalue & 15) == 1) { - channel->pan = IT_SURROUND; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - 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) - channel->volume = 0; - else - channel->note_cut_count = 1; - } - break; - case IT_S_SET_MIDI_MACRO: - 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; - } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; - } - } - break; - case IT_SET_GLOBAL_VOLUME: - 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 { - channel->pan = (entry->effectvalue + 2) >> 2; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - break; - //case IT_PANBRELLO: - case IT_MIDI_MACRO: - { - 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; - } - } - - 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) - 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? - } - } - } - - 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; - } - if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { - if (channel->sample) - channel->destnote = channel->truenote; - else - channel->destnote = channel->note; - } - channel->toneporta = v << 4; - } else { - /* 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) - v = channel->lastEF; - channel->lastEF = v; - } - if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { - if (channel->sample) - channel->destnote = channel->truenote; - else - channel->destnote = channel->note; - } - channel->toneporta = v << 4; - } - 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); - 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]; - - if (entry->mask & IT_ENTRY_INSTRUMENT) { - channel->instrument = entry->instrument; - instrument_to_sample(sigdata, channel); - if (channel->playing) { - /* 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; - if (!(sigdata->flags & IT_WAS_A_MOD)) - channel->truepan = 32 + channel->playing->sample->default_pan*64; - - retrigger_xm_envelopes(channel->playing); - } - } - - 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) { - free(channel->playing); - channel->playing = NULL; - } - 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 = malloc(sizeof(*channel->playing)); - if (!channel->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); - } - - channel->playing->flags = 0; - 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->channel_volume = channel->channelvolume; - channel->playing->note = channel->truenote; - 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->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - channel->playing->tremolo_time = 0; - channel->playing->sample_vibrato_time = 0; - channel->playing->sample_vibrato_depth = 0; - channel->playing->slide = 0; - 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 | 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->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->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; - } - } -} - - - -/* 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_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) - channel->volume = 0; - else if (channel->playing) { - free(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) -{ - if (!(envelope->flags & IT_ENVELOPE_ON)) - return 0; - - ASSERT(envelope->n_nodes > 0); - - 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; - return 1; - } 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++; - while (pe->tick >= envelope->node_t[pe->next_node]) { - pe->next_node++; - if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { - if (pe->next_node > envelope->sus_loop_end) { - pe->next_node = envelope->sus_loop_start; - 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->next_node > envelope->loop_end) { - pe->next_node = envelope->loop_start; - ASSERT(pe->next_node < envelope->n_nodes); - pe->tick = envelope->node_t[envelope->loop_start]; - return 0; - } - } - if (pe->next_node >= envelope->n_nodes) - return 0; - } - 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)) { - 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); - update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope); -} - - - -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 void process_playing(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing) -{ - if (playing->instrument) { - if (sigdata->flags & IT_WAS_AN_XM) - update_xm_envelopes(playing); - else - update_it_envelopes(playing); - update_fadeout(sigdata, playing); - } - - //Calculate final volume if required - //Calculate final pan if required - - 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; -} - - - -static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - int i; - - 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 = it_sine[playing->vibrato_time]; - 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 (sigdata->flags & IT_LINEAR_SLIDES) { - int currpitch = ((playing->note - 60) << 8) + playing->slide - + vibrato_shift; - - /* 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 / 65536.0f; - } else { - int slide = playing->slide + vibrato_shift; - - playing->delta = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); - /* 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; - continue; - } - - playing->delta = (1.0f / 65536.0f) / playing->delta; - } - - playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8); - - 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(sigdata, sigrenderer->channel[i].playing); - 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(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(sigdata, sigrenderer->playing[i]); - if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { - free(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; - - sigrenderer->processrow++; - - if (sigrenderer->processrow >= sigrenderer->n_rows) { - IT_PATTERN *pattern; - int n; - int processorder = sigrenderer->processorder; - - if (sigrenderer->processrow == 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; - - if (sigrenderer->processorder == 0xFFFF) - sigrenderer->processorder = sigrenderer->order - 1; - - for (;;) { - sigrenderer->processorder++; - - if (sigrenderer->processorder >= sigdata->n_orders) { - sigrenderer->processorder = sigdata->restart_position; - if (sigrenderer->processorder >= sigdata->n_orders) { - /* Restarting beyond end. We'll loop for now. */ - sigrenderer->processorder = -1; - continue; - } - } - - n = sigdata->order[sigrenderer->processorder]; - - if (n < sigdata->n_patterns) - break; - -#ifdef INVALID_ORDERS_END_SONG - if (n != IT_ORDER_SKIP) - sigrenderer->processorder = -1; -#else - if (n == IT_ORDER_END) - sigrenderer->processorder = -1; -#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. */ - if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder)) { - if (sigrenderer->callbacks->loop) { - if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) - return 1; - 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 { - sigrenderer->entry = sigrenderer->entry_start; - sigrenderer->row = 0; - } - } - - 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_OLD_EFFECTS)) - update_smooth_effects(sigrenderer); - } else { - { - IT_ENTRY *entry = sigrenderer->entry; - - while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) - 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. - */ - } - - update_effects(sigrenderer); - } - } else { - speed0: - update_effects(sigrenderer); - update_tick_counts(sigrenderer); - } - - process_all_playing(sigrenderer); - - { - LONG_LONG t = sigrenderer->sub_time_left + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; - sigrenderer->time_left += (int)(t >> 16); - sigrenderer->sub_time_left = (int)t & 65535; - } - - return 0; -} - - - -int dumb_it_max_to_mix = 64; - - - -static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume) -{ - if (volume != 0) { - int vol; - - if (playing->channel->flags & IT_CHANNEL_MUTED) - return 0; - - if ((playing->channel->tremor_time & 192) == 128) - return 0; - - vol = it_sine[playing->tremolo_time]; - vol *= playing->tremolo_depth; - - vol = (playing->volume << 5) + vol; - - if (vol <= 0) - return 0; - - if (vol > 64 << 5) - vol = 64 << 5; - - 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->env_instrument->volume_envelope.flags & IT_ENVELOPE_ON) { - 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 volume; -} - - - -static int apply_pan_envelope(IT_PLAYING *playing) -{ - int pan = playing->pan; - if (pan <= 64 << IT_ENVELOPE_SHIFT && playing->env_instrument && (playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_ON)) { - 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; -} - - - -/* 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 long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix) -{ - int bits; - - int pan; - float span; /* separated pan, range -1 to 1; garbage for surround */ - - long size_rendered; - - if (playing->flags & IT_PLAYING_DEAD) - return 0; - - if (*left_to_mix <= 0) - volume = 0; - - bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; - - pan = apply_pan_envelope(playing); - span = (pan - (32<<8)) * sigrenderer->sigdata->pan_separation * (1.0f / ((32<<8) * 128)); - - if (volume == 0) { - if (playing->sample->flags & IT_SAMPLE_STEREO) - size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta); - else - size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); - } else { - if (sigrenderer->n_channels == 2) { - float lvol, rvol; - lvol = volume; - if (!IT_IS_SURROUND_SHIFTED(pan)) lvol *= 1.0f - span; - rvol = -lvol; - if (!IT_IS_SURROUND_SHIFTED(pan)) rvol += 2.0f * volume; - 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]); - } - } - } else { - if (playing->sample->flags & IT_SAMPLE_STEREO) { - float lvol, rvol; - lvol = 0.5f * volume; - if (!IT_IS_SURROUND_SHIFTED(pan)) lvol *= 1.0f - span; - rvol = lvol; - if (!IT_IS_SURROUND_SHIFTED(pan)) rvol = volume - rvol; - 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, volume, &click); - dumb_record_click(sigrenderer->click_remover[0], pos, click); - } - size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, volume, delta); - if (store_end_sample) - dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, volume, &samples[0][pos + size_rendered]); - if (sigrenderer->click_remover) { - sample_t click; - dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, volume, &click); - dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); - } - } - } - (*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 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, float *delta, int *cutoff) -{ - { - int sample_vibrato_shift = it_sine[playing->sample_vibrato_time]; - - 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; - - *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift); - } - - if (playing->env_instrument && - (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_ON)) - { - 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(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long 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; - - 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; - float note_delta = delta * playing->delta; - int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; - - 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 (to_mix[i].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, 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, to_mix[i].volume, 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], 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]+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], 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, to_mix[i].volume, note_delta, pos, size, samples, 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(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(sigrenderer->playing[i]); - sigrenderer->playing[i] = NULL; - } - } - } -} - - - -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; - - 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->callbacks = callbacks; - sigrenderer->click_remover = cr; - - sigrenderer->sigdata = sigdata; - sigrenderer->n_channels = n_channels; - 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->note = 0; - channel->SFmacro = 0; - channel->filter_cutoff = 127; - channel->filter_resonance = 0; - channel->xm_retrig = 0; - channel->retrig_tick = 0; - channel->tremor_time = 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->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->playing = NULL; - } - - 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->pat_loop_row = -1; - sigrenderer->rowcount = 1; - sigrenderer->order = startorder; - sigrenderer->row = 0; - sigrenderer->processorder = startorder - 1; - sigrenderer->tick = 1; - - { - 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; - } - /* 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; - - return sigrenderer; -} - - - -void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->loop = callback; - sigrenderer->callbacks->loop_data = data; - } -} - - - -void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->xm_speed_zero = callback; - sigrenderer->callbacks->xm_speed_zero_data = data; - } -} - - - -void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->midi = callback; - sigrenderer->callbacks->midi_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; - 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 *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); - return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0); -} - - - -static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, long 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 >= 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 long it_sigrenderer_get_samples( - sigrenderer_t *vsigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - long pos; - int dt; - long 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 = (long)t & 65535; - sigrenderer->time_left += (long)(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 = (long)t & 65535; - sigrenderer->time_left += (long)(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, float 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) { - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) - if (sigrenderer->channel[i].playing) - free(sigrenderer->channel[i].playing); - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) - if (sigrenderer->playing[i]) - free(sigrenderer->playing[i]); - - dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover); - - if (sigrenderer->callbacks) - free(sigrenderer->callbacks); - - 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 *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos) -{ - return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos); -} - - - -DUMB_IT_SIGRENDERER *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 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 */ - float 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 = 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 dumb_it_callback_terminate(void *data) -{ - (void)data; - return 1; -} - - - -int 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 */ - - - -/* Returns the length of the module, up until it first loops. */ -long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata) -{ - 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, 0); - 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; - sigdata->checkpoint = checkpoint; - - for (;;) { - long 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; - } -} - - - -void 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)); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itrender.c - Code to render an Impulse Tracker / / \ \ + * module. | < / \_ + * | \/ /\ / + * Written - painstakingly - by entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" +#include "internal/it.h" +#include "internal/lpc.h" + +#include "internal/blip_buf.h" +#include "internal/lanczos_resampler.h" + +// #define BIT_ARRAY_BULLSHIT + +#define END_RAMPING +#define RAMP_DOWN + +static IT_PLAYING *new_playing() +{ + IT_PLAYING * r = (IT_PLAYING*) malloc(sizeof(*r)); + if (r) + { + r->resampler.blip_buffer[0] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[0] ) + { + free( r ); + return NULL; + } + r->resampler.blip_buffer[1] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[1] ) + { + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + blip_set_rates(r->resampler.blip_buffer[0], 65536, 1); + blip_set_rates(r->resampler.blip_buffer[1], 65536, 1); + r->resampler.fir_resampler_ratio = 0.0; + r->resampler.fir_resampler[0] = lanczos_resampler_create(); + if ( !r->resampler.fir_resampler[0] ) { + free( r->resampler.blip_buffer[1] ); + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + r->resampler.fir_resampler[1] = lanczos_resampler_create(); + if ( !r->resampler.fir_resampler[1] ) { + lanczos_resampler_delete( r->resampler.fir_resampler[0] ); + free( r->resampler.blip_buffer[1] ); + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + } + return r; +} + +static void free_playing(IT_PLAYING * r) +{ + lanczos_resampler_delete( r->resampler.fir_resampler[1] ); + lanczos_resampler_delete( r->resampler.fir_resampler[0] ); + blip_delete( r->resampler.blip_buffer[1] ); + blip_delete( r->resampler.blip_buffer[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; + +#ifdef END_RAMPING + dst->declick_stage = src->declick_stage; + dst->declick_volume = src->declick_volume; +#endif + + 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.blip_buffer[0] = blip_dup( src->resampler.blip_buffer[0] ); + if ( !dst->resampler.blip_buffer[0] ) + { + free( dst ); + return NULL; + } + dst->resampler.blip_buffer[1] = blip_dup( src->resampler.blip_buffer[1] ); + if ( !dst->resampler.blip_buffer[1] ) + { + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } + dst->resampler.fir_resampler_ratio = src->resampler.fir_resampler_ratio; + dst->resampler.fir_resampler[0] = lanczos_resampler_dup( src->resampler.fir_resampler[0] ); + if ( !dst->resampler.fir_resampler[0] ) { + blip_delete( dst->resampler.blip_buffer[1] ); + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } + dst->resampler.fir_resampler[1] = lanczos_resampler_dup( src->resampler.fir_resampler[1] ); + if ( !dst->resampler.fir_resampler[1] ) { + lanczos_resampler_delete( dst->resampler.fir_resampler[0] ); + blip_delete( dst->resampler.blip_buffer[1] ); + blip_delete( dst->resampler.blip_buffer[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->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->click_remover = NULL; + + dst->callbacks = callbacks; + +#ifdef BIT_ARRAY_BULLSHIT + dst->played = bit_array_dup(src->played); + + dst->looped = src->looped; + dst->time_played = src->time_played; + dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper); +#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, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) +{ + sample_t currsample = state->currsample; + sample_t prevsample = state->prevsample; + + float a, b, c; + + 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; + 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 MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32)) +#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) +#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 + +int _dumb_it_use_sse = 0; + +static void it_filter(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) +{ +#if defined(_USE_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 = &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, long 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); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } 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); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } 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); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + } +} + + +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) && 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: +#ifdef RAMP_DOWN + channel->playing->declick_stage = 2; +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + 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: +#ifdef RAMP_DOWN + playing->declick_stage = 2; +#else + free_playing(playing); + sigrenderer->playing[i] = NULL; +#endif + 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(channel->playing); + + channel->playing = new_playing(); + + if (!channel->playing) + return; + + if (!envelopes_copied && sigdata->flags & IT_USE_INSTRUMENTS && nna != NNA_NOTE_CUT) { + 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; + 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; +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + channel->playing->channel_volume = channel->channelvolume; + channel->playing->ramp_volume[0] = 31337.0; /* special */ + 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, 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; +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#endif + 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 = ((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_A_PTM) && (sample->flags & IT_SAMPLE_16BIT)) + offset >>= 1; + if (offset < end) { + it_playing_reset_resamplers(playing, offset); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } else if (sigdata->flags & IT_OLD_EFFECTS) { + it_playing_reset_resamplers(playing, end); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + } + 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); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + 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) + { +#ifdef RAMP_DOWN + playing->declick_stage = 2; +#else + free_playing(playing); + sigrenderer->playing[i] = NULL; +#endif + 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); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + + 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); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + + 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 = &arpeggio_okt_3; + break; + + case IT_OKT_ARPEGGIO_4: + channel->arpeggio_table = &arpeggio_okt_4; + break; + + case IT_OKT_ARPEGGIO_5: + channel->arpeggio_table = &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? + } + } + } + + 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; + } + 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; + } + } + channel->toneporta = v << 4; + } else { + /* 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; + } + 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; + } + } + channel->toneporta = v << 4; + } + 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) { +#ifdef RAMP_DOWN + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + + if (!channel->sample) { + if (channel->playing) + { + free_playing(channel->playing); + channel->playing = NULL; + } + } else { + if (channel->playing) { + free_playing(channel->playing); + } + channel->playing = playing; + playing = NULL; + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#else + if (!channel->sample) { + free_playing(channel->playing); + channel->playing = NULL; + } else { +#endif + 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) { +#ifdef RAMP_DOWN + int i; + if (playing) { + free_playing(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 = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(channel->playing); + channel->playing = NULL; + } +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + } + if (playing) free_playing(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(); + if (!channel->playing) { + if (playing) free_playing(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); + } +#ifdef RAMP_DOWN + 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(playing); + return; + } + playing = NULL; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + ptemp->declick_stage = 2; + ptemp->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; + sigrenderer->playing[i] = ptemp; + ptemp = NULL; + break; + } + } + if (ptemp) free_playing(ptemp); + } +#endif + + 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; +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + channel->playing->channel_volume = channel->channelvolume; + channel->playing->ramp_volume[0] = 31337.0; /* special */ + 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(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) { +#ifdef RAMP_DOWN + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(channel->playing); + channel->playing = NULL; + } +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + } + } + } 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, float 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; + + pan = apply_pan_envelope(playing); + + if ((sigrenderer->n_channels >= 2) && (sigdata->flags & IT_STEREO) && (sigrenderer->n_channels != 3 || !IT_IS_SURROUND_SHIFTED(pan))) { + span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128)); + vol = 0.5f; + if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span; + playing->float_volume[0] = vol; + vol = -vol; + if (!IT_IS_SURROUND_SHIFTED(pan)) vol += 1.0f; + playing->float_volume[1] = vol; + } 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; + + if (!sigrenderer->ramp_style || playing->ramp_volume[0] == 31337.0) { + playing->ramp_volume[0] = playing->float_volume[0]; + playing->ramp_volume[1] = playing->float_volume[1]; + playing->ramp_delta[0] = 0; + playing->ramp_delta[1] = 0; + } else { + playing->ramp_delta[0] = invt2g * (playing->float_volume[0] - playing->ramp_volume[0]); + playing->ramp_delta[1] = 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; +} + +#ifdef _MSC_VER +static float log2(float x) {return (float)log(x)/(float)log(2.0f);} +#endif + +static int delta_to_note(float delta, int base) +{ + float note; + note = log2(delta * 65536.f / (float)base)*12.0f+60.5f; + if (note > 119) note = 119; + else if (note < 0) note = 0; + return (int)note; +} + +// 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 +}; + +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); + + 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->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->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); + timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n, sigrenderer->time_played); + timekeeping_array_bump(sigrenderer->row_timekeeper, 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 + ) { +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#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; + } + } + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->looped == 0) { + timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); + } + timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); +#endif + + 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 + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + sigrenderer->gvz_time += (int)(t >> 16); + sigrenderer->gvz_sub_time = (int)t & 65535; + if (sigrenderer->gvz_time >= 65536 * 12) { +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#endif + 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 = ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + 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; + +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 +}; + +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, float 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 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. + */ +#if 0 +static long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int cr_record_which) +{ + int bits; + + long size_rendered; + + DUMB_VOLUME_RAMP_INFO lvol, rvol; + + if (playing->flags & IT_PLAYING_DEAD) + return 0; + + if (*left_to_mix <= 0) + volume = 0; + +#ifndef END_RAMPING + { + 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; + } +#endif + + 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_1(bits, &playing->resampler, NULL, size, 0, 0, delta); + else + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); + } else { + lvol.volume = playing->ramp_volume [0]; + rvol.volume = playing->ramp_volume [1]; + lvol.delta = playing->ramp_delta [0] * main_delta; + rvol.delta = playing->ramp_delta [1] * main_delta; + lvol.target = playing->float_volume [0]; + rvol.target = playing->float_volume [1]; + rvol.mix = lvol.mix = volume; + if (sigrenderer->n_channels >= 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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 ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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]); + } + } + } else { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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 ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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); + } + } + } + playing->ramp_volume [0] = lvol.volume; + playing->ramp_volume [1] = rvol.volume; + (*left_to_mix)--; + } + + if (playing->resampler.dir == 0) + playing->flags |= IT_PLAYING_DEAD; + + return size_rendered; +} +#endif + +#ifdef END_RAMPING +#if 1 +static long render_playing_part(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, DUMB_VOLUME_RAMP_INFO * lvol, DUMB_VOLUME_RAMP_INFO * rvol, int bits, float delta, long pos, long size, sample_t **samples, int store_end_sample, int cr_record_which) +{ + long size_rendered = 0; + + if (sigrenderer->n_channels == 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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 ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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]); + } + } + } else { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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 ((cr_record_which & 1) && 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 ((cr_record_which & 2) && 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); + } + } + } + return size_rendered; +} + +static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style) +{ + int bits; + + long size_rendered; + + DUMB_VOLUME_RAMP_INFO lvol, rvol; + + if (!size) return 0; + + 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; + } + + bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + size_rendered = size; + + if (volume == 0) { + if (playing->declick_stage < 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta); + else + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); + } else { + playing->declick_stage = 3; + } + } else { + lvol.volume = playing->ramp_volume [0]; + rvol.volume = playing->ramp_volume [1]; + lvol.delta = playing->ramp_delta [0] * main_delta; + rvol.delta = playing->ramp_delta [1] * main_delta; + lvol.target = playing->float_volume [0]; + rvol.target = playing->float_volume [1]; + rvol.mix = lvol.mix = volume; + if (ramp_style) { + if (playing->declick_stage == 1) { + size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3); + } else if (playing->declick_stage == 0 || playing->declick_stage == 2) { + float declick_count = ((ramp_style == 2) ? 327.68 : 49.152) / main_delta; /* 5ms / 0.75ms */ + float declick_remain = playing->declick_volume * declick_count; + float declick_dir = -1; + float declick_target; + int remain; + DUMB_VOLUME_RAMP_INFO declick_lvol, declick_rvol; + if (playing->declick_stage == 0) { + declick_remain = declick_count - declick_remain; + declick_dir = 1; + } + if (size < declick_remain) declick_remain = size; + remain = declick_remain; + if (remain > size) + declick_remain = size; + declick_target = playing->declick_volume + declick_dir / declick_count * declick_remain; + declick_lvol.volume = lvol.volume * playing->declick_volume; + declick_rvol.volume = rvol.volume * playing->declick_volume; + declick_lvol.target = lvol.volume * declick_target; + declick_rvol.target = rvol.volume * declick_target; + declick_lvol.delta = (declick_lvol.target - declick_lvol.volume) / declick_remain; + declick_rvol.delta = (declick_rvol.target - declick_rvol.volume) / declick_remain; + declick_lvol.mix = declick_rvol.mix = volume; + if (remain < size) { + size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, 0, 1); + if (size_rendered == remain) { + if (playing->declick_stage == 0) { + size_rendered += render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos + remain, size - remain, samples, store_end_sample, 2); + } else { + size_rendered = size; + } + } + playing->declick_stage++; + playing->declick_volume = 1; + } else { + size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, store_end_sample, 3); + playing->declick_volume = declick_target; + } + lvol.volume = declick_lvol.volume; + rvol.volume = declick_rvol.volume; + } else /*if (playing->declick_stage == 3)*/ { + (*left_to_mix)++; + } + } else if (playing->declick_stage < 2) { + size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3); + } else { + playing->declick_stage = 3; + (*left_to_mix)++; + } + playing->ramp_volume [0] = lvol.volume; + playing->ramp_volume [1] = rvol.volume; + (*left_to_mix)--; + } + + if (playing->resampler.dir == 0) + playing->flags |= IT_PLAYING_DEAD; + + return size_rendered; +} +#else +static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style) +{ + long rv, trv; + int l_t_m_temp, cr_which; + if (!size) return 0; + + rv = 0; + cr_which = 3; + + { + 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; + } + + if (ramp_style) + { + if (playing->declick_stage == 0) { + /* ramp up! */ + cr_which = 1; + while (playing->declick_stage == 0 && size) { + l_t_m_temp = *left_to_mix; + if (size == 1) cr_which |= 2; + trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which); + cr_which &= 2; + if (ramp_style == 2) playing->declick_volume += (1.f / 256.f) * main_delta; + else playing->declick_volume *= (float)pow(2.0f, main_delta); + if (playing->declick_volume >= 1.0f) { + playing->declick_volume = 1.0f; + playing->declick_stage = 1; + } + if (!trv) { + *left_to_mix = l_t_m_temp; + return rv; + } + rv += trv; + pos += trv; + size -= trv; + } + + if (!size) { + *left_to_mix = l_t_m_temp; + return rv; + } + + cr_which = 2; + + } +#ifdef RAMP_DOWN + else if (playing->declick_stage == 2) { + float halflife = pow(0.5, 1.0 / 64.0 * main_delta); + cr_which = 1; + while (playing->declick_stage == 2 && size) { + l_t_m_temp = *left_to_mix; + if (size == 1) cr_which |= 2; + trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which); + cr_which &= 2; + /*if (ramp_style == 2) playing->declick_volume -= (1.f / 256.f) * main_delta; + else playing->declick_volume *= (float)pow(0.5f, main_delta);*/ + playing->declick_volume *= halflife; + if (playing->declick_volume < (1.f / 256.f)) { + playing->declick_stage = 3; + } + if (!trv) { + *left_to_mix = l_t_m_temp; + return rv; + } + rv += trv; + pos += trv; + size -= trv; + } + + if (size) rv += render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, left_to_mix, 2); + else *left_to_mix = l_t_m_temp; + + return rv; + + } else if (playing->declick_stage == 3) { + return size; + } +#endif + } else if (playing->declick_stage >= 2) { + playing->declick_stage = 3; + return size; + } + + return rv + render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, cr_which); +} +#endif +#else +#define render_playing_ramp(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, ramp_style) render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, 3) +#endif + +typedef struct IT_TO_MIX +{ + IT_PLAYING *playing; + float volume; +} +IT_TO_MIX; + + + +static int 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, float *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! */ + float 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, float volume, float delta, long pos, long 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 ramp_style = sigrenderer->ramp_style; + + //int max_output = sigrenderer->max_output; + + if (ramp_style > 2) { + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2; + else ramp_style -= 3; + } + + 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; + float 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_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix, ramp_style); + } + } + + 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 ( +#ifdef RAMP_DOWN + (sigrenderer->channel[i].playing->declick_stage == 3) || +#endif + (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if ( +#ifdef RAMP_DOWN + (sigrenderer->playing[i]->declick_stage == 3) || +#endif + (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render_surround(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long 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; + + int ramp_style = sigrenderer->ramp_style; + + DUMB_CLICK_REMOVER **saved_cr = sigrenderer->click_remover; + + //int max_output = sigrenderer->max_output; + + if (ramp_style > 2) { + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2; + else ramp_style -= 3; + } + + 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; + float 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_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix, ramp_style); + } + } + + 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; + float 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_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + 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_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, &samples[1], 0, &left_to_mix, ramp_style); + } + } + + 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 ( +#ifdef RAMP_DOWN + (sigrenderer->channel[i].playing->declick_stage == 3) || +#endif + (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if ( +#ifdef RAMP_DOWN + (sigrenderer->playing[i]->declick_stage == 3) || +#endif + (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long 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; + + 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->callbacks = callbacks; + sigrenderer->click_remover = cr; + + sigrenderer->sigdata = sigdata; + sigrenderer->n_channels = n_channels; + sigrenderer->resampling_quality = dumb_resampling_quality; + sigrenderer->ramp_style = 0; + 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); + + sigrenderer->looped = 0; + sigrenderer->time_played = 0; + sigrenderer->row_timekeeper = timekeeping_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); + timekeeping_array_push(sigrenderer->row_timekeeper, order * 256 + n, 0); + timekeeping_array_bump(sigrenderer->row_timekeeper, 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; + + 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 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; + } + } + 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; + } + } + } +} + + +void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style) { + if (sigrenderer && ramp_style >= 0 && ramp_style <= 4) { + sigrenderer->ramp_style = ramp_style; + } +} + + +void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->loop = callback; + sigrenderer->callbacks->loop_data = data; + } +} + + + +void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->xm_speed_zero = callback; + sigrenderer->callbacks->xm_speed_zero_data = data; + } +} + + + +void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->midi = callback; + sigrenderer->callbacks->midi_data = data; + } +} + + + +void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*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 *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, long 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); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)sigrenderer->time_left << 16; +#endif + + 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; + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)pos << 16; +#endif + + return sigrenderer; +} + + + +static long it_sigrenderer_get_samples( + sigrenderer_t *vsigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + long pos; + int dt; + long todo; + int ret; + 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 = (long)t & 65535; + sigrenderer->time_left += (long)(t >> 16); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)todo * dt; +#endif + + ret = process_tick(sigrenderer); + + if (ret) { + sigrenderer->order = -1; + sigrenderer->row = -1; + } + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->looped == 1) { + sigrenderer->looped = -1; + size = 0; + timekeeping_array_reset(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); + sigrenderer->time_played = timekeeping_array_get_item(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); + break; + } +#endif + + if (ret) { + return pos; + } + } + + render(sigrenderer, volume, delta, pos, size, samples); + + pos += size; + + t = sigrenderer->sub_time_left - (LONG_LONG)size * dt; + sigrenderer->sub_time_left = (long)t & 65535; + sigrenderer->time_left += (long)(t >> 16); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)size * dt; +#endif + + 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, float 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) { + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) + free_playing(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(sigrenderer->playing[i]); + + 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); + + timekeeping_array_destroy(sigrenderer->row_timekeeper); +#endif + + free(vsigrenderer); + } +} + + + +#ifdef BIT_ARRAY_BULLSHIT +static long it_sigrenderer_get_position(sigrenderer_t *vsigrenderer) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + + return sigrenderer->time_played >> 16; +} +#endif + + + +DUH_SIGTYPE_DESC _dumb_sigtype_it = { + SIGTYPE_IT, + NULL, + &it_start_sigrenderer, + NULL, + &it_sigrenderer_get_samples, + &it_sigrenderer_get_current_sample, +#ifdef BIT_ARRAY_BULLSHIT + &it_sigrenderer_get_position, +#else + NULL, +#endif + &_dumb_it_end_sigrenderer, + &_dumb_it_unload_sigdata +}; + + + +DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos) +{ + return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos); +} + + + +DUMB_IT_SIGRENDERER *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 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 */ + float 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 = 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 dumb_it_callback_terminate(void *data) +{ + (void)data; + return 1; +} + + + +int 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. */ +long 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 (;;) { + long 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 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 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 dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data) +{ + int n; + long 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 (;;) { + long 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/Frameworks/Dumb/dumb/src/it/itunload.c b/Frameworks/Dumb/dumb/src/it/itunload.c index efed192a6..136fd5c5a 100644 --- a/Frameworks/Dumb/dumb/src/it/itunload.c +++ b/Frameworks/Dumb/dumb/src/it/itunload.c @@ -1,72 +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); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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/Frameworks/Dumb/dumb/src/it/load669.c b/Frameworks/Dumb/dumb/src/it/load669.c new file mode 100644 index 000000000..e5a3fc3f4 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/load6692.c b/Frameworks/Dumb/dumb/src/it/load6692.c new file mode 100644 index 000000000..2358838f4 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_669(const char *filename) +{ + DUH *duh = dumb_load_669_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadamf.c b/Frameworks/Dumb/dumb/src/it/loadamf.c new file mode 100644 index 000000000..2695125f1 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadamf2.c b/Frameworks/Dumb/dumb/src/it/loadamf2.c new file mode 100644 index 000000000..91f171de7 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_amf(const char *filename) +{ + DUH *duh = dumb_load_amf_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadany.c b/Frameworks/Dumb/dumb/src/it/loadany.c new file mode 100644 index 000000000..5d268eafa --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadany2.c b/Frameworks/Dumb/dumb/src/it/loadany2.c new file mode 100644 index 000000000..fb9439f66 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadasy.c b/Frameworks/Dumb/dumb/src/it/loadasy.c new file mode 100644 index 000000000..86c6ad8ce --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadasy2.c b/Frameworks/Dumb/dumb/src/it/loadasy2.c new file mode 100644 index 000000000..7b253320e --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_asy(const char *filename) +{ + DUH *duh = dumb_load_asy_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadmod.c b/Frameworks/Dumb/dumb/src/it/loadmod.c index 121e2881d..4e3e03706 100644 --- a/Frameworks/Dumb/dumb/src/it/loadmod.c +++ b/Frameworks/Dumb/dumb/src/it/loadmod.c @@ -1,42 +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 *dumb_load_mod_quick(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = dumb_read_mod_quick(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/loadmod2.c b/Frameworks/Dumb/dumb/src/it/loadmod2.c index ec705f81d..617897e78 100644 --- a/Frameworks/Dumb/dumb/src/it/loadmod2.c +++ b/Frameworks/Dumb/dumb/src/it/loadmod2.c @@ -1,29 +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 *dumb_load_mod(const char *filename) -{ - DUH *duh = dumb_load_mod_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/loadmtm.c b/Frameworks/Dumb/dumb/src/it/loadmtm.c new file mode 100644 index 000000000..3c974880f --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadmtm2.c b/Frameworks/Dumb/dumb/src/it/loadmtm2.c new file mode 100644 index 000000000..9fc238908 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_mtm(const char *filename) +{ + DUH *duh = dumb_load_mtm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadokt.c b/Frameworks/Dumb/dumb/src/it/loadokt.c new file mode 100644 index 000000000..2139d49e8 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadokt2.c b/Frameworks/Dumb/dumb/src/it/loadokt2.c new file mode 100644 index 000000000..aa752c582 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_okt(const char *filename) +{ + DUH *duh = dumb_load_okt_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadoldpsm.c b/Frameworks/Dumb/dumb/src/it/loadoldpsm.c new file mode 100644 index 000000000..547294b36 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadoldpsm2.c b/Frameworks/Dumb/dumb/src/it/loadoldpsm2.c new file mode 100644 index 000000000..ff2e427c4 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadpsm.c b/Frameworks/Dumb/dumb/src/it/loadpsm.c new file mode 100644 index 000000000..a6851d5b6 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadpsm2.c b/Frameworks/Dumb/dumb/src/it/loadpsm2.c new file mode 100644 index 000000000..7a12257a1 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadptm.c b/Frameworks/Dumb/dumb/src/it/loadptm.c new file mode 100644 index 000000000..ddcaaec95 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadptm2.c b/Frameworks/Dumb/dumb/src/it/loadptm2.c new file mode 100644 index 000000000..ea8982f2b --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_ptm(const char *filename) +{ + DUH *duh = dumb_load_ptm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadriff.c b/Frameworks/Dumb/dumb/src/it/loadriff.c new file mode 100644 index 000000000..07994b0fc --- /dev/null +++ b/Frameworks/Dumb/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 * 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/Frameworks/Dumb/dumb/src/it/loadriff2.c b/Frameworks/Dumb/dumb/src/it/loadriff2.c new file mode 100644 index 000000000..11c5d4974 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_riff(const char *filename) +{ + DUH *duh = dumb_load_riff_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loads3m.c b/Frameworks/Dumb/dumb/src/it/loads3m.c index 777151c68..41af114ea 100644 --- a/Frameworks/Dumb/dumb/src/it/loads3m.c +++ b/Frameworks/Dumb/dumb/src/it/loads3m.c @@ -1,42 +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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/loads3m2.c b/Frameworks/Dumb/dumb/src/it/loads3m2.c index 8b55e83dc..de81e09a2 100644 --- a/Frameworks/Dumb/dumb/src/it/loads3m2.c +++ b/Frameworks/Dumb/dumb/src/it/loads3m2.c @@ -1,29 +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 *dumb_load_s3m(const char *filename) -{ - DUH *duh = dumb_load_s3m_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_load_s3m(const char *filename) +{ + DUH *duh = dumb_load_s3m_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadstm.c b/Frameworks/Dumb/dumb/src/it/loadstm.c new file mode 100644 index 000000000..847852082 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/loadstm2.c b/Frameworks/Dumb/dumb/src/it/loadstm2.c new file mode 100644 index 000000000..4655b52d0 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_load_stm(const char *filename) +{ + DUH *duh = dumb_load_stm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadxm.c b/Frameworks/Dumb/dumb/src/it/loadxm.c index 2c8067cbc..a0eba6162 100644 --- a/Frameworks/Dumb/dumb/src/it/loadxm.c +++ b/Frameworks/Dumb/dumb/src/it/loadxm.c @@ -1,42 +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 *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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/loadxm2.c b/Frameworks/Dumb/dumb/src/it/loadxm2.c index a596542a2..242a586d4 100644 --- a/Frameworks/Dumb/dumb/src/it/loadxm2.c +++ b/Frameworks/Dumb/dumb/src/it/loadxm2.c @@ -1,29 +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 *dumb_load_xm(const char *filename) -{ - DUH *duh = dumb_load_xm_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_load_xm(const char *filename) +{ + DUH *duh = dumb_load_xm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/ptmeffect.c b/Frameworks/Dumb/dumb/src/it/ptmeffect.c new file mode 100644 index 000000000..b05a5d744 --- /dev/null +++ b/Frameworks/Dumb/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/Frameworks/Dumb/dumb/src/it/read669.c b/Frameworks/Dumb/dumb/src/it/read669.c new file mode 100644 index 000000000..4e29afbce --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/read669.c @@ -0,0 +1,447 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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) +{ + long i; + long 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) { + sigdata->channel_pan[i+0] = 48; + sigdata->channel_pan[i+1] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *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/Frameworks/Dumb/dumb/src/it/read6692.c b/Frameworks/Dumb/dumb/src/it/read6692.c new file mode 100644 index 000000000..b5f4f2044 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_read_669(DUMBFILE *f) +{ + DUH *duh = dumb_read_669_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readam.c b/Frameworks/Dumb/dumb/src/it/readam.c new file mode 100644 index 000000000..ac1cc3996 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readam.c @@ -0,0 +1,787 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; + + long 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 = 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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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; + } + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) + { + 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/Frameworks/Dumb/dumb/src/it/readamf.c b/Frameworks/Dumb/dumb/src/it/readamf.c new file mode 100644 index 000000000..400491776 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readamf.c @@ -0,0 +1,523 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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->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_start != 0 ) + 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; + } + + 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 { + for ( i = 0; i < 16; i++ ) { + sigdata->channel_pan[ i ] = ( dumbfile_getc( f ) & 1 ) ? 16 : 48; + } + } + + 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; + + 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 *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/Frameworks/Dumb/dumb/src/it/readamf2.c b/Frameworks/Dumb/dumb/src/it/readamf2.c new file mode 100644 index 000000000..c2258fc89 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_read_amf(DUMBFILE *f) +{ + DUH *duh = dumb_read_amf_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readany.c b/Frameworks/Dumb/dumb/src/it/readany.c new file mode 100644 index 000000000..637ad16d3 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/readany2.c b/Frameworks/Dumb/dumb/src/it/readany2.c new file mode 100644 index 000000000..ac881a717 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/readasy.c b/Frameworks/Dumb/dumb/src/it/readasy.c new file mode 100644 index 000000000..0732facf4 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readasy.c @@ -0,0 +1,331 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 ); + + if ( entry->mask ) ++entry; + } + pos += 4; + } + IT_SET_END_ROW( entry ); + ++entry; + } + + pattern->n_entries = 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 ) +{ + long 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) { + sigdata->channel_pan[i+0] = 16; + sigdata->channel_pan[i+1] = 48; + sigdata->channel_pan[i+2] = 48; + sigdata->channel_pan[i+3] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *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/Frameworks/Dumb/dumb/src/it/readdsmf.c b/Frameworks/Dumb/dumb/src/it/readdsmf.c new file mode 100644 index 000000000..0196f3df5 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readdsmf.c @@ -0,0 +1,382 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 = 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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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/Frameworks/Dumb/dumb/src/it/readmod.c b/Frameworks/Dumb/dumb/src/it/readmod.c index ebeb53a34..94fb7cc74 100644 --- a/Frameworks/Dumb/dumb/src/it/readmod.c +++ b/Frameworks/Dumb/dumb/src/it/readmod.c @@ -1,602 +1,631 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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(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(buffer + pos, 4*4, f); - - n_channels = 8; - } else - dumbfile_getnc(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); - - entry++; - } - pos += 4; - } - IT_SET_END_ROW(entry); - entry++; - } - - return 0; -} - - - -static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) -{ - int finetune; - -/** - 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(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? - sample->loop_start = dumbfile_mgetw(f) << 1; - sample->loop_end = sample->loop_start + (dumbfile_mgetw(f) << 1); -/** -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 = (long)(16726.0*pow(DUMB_PITCH_BASE, 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? - - return dumbfile_error(f); -} - - - -static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) -{ - long i; - long 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->data)[i] = dumbfile_getc(f); - } else - sample->flags &= ~IT_SAMPLE_EXISTS; - - /* skip truncated data */ - dumbfile_skip(f, truncated_size); - // Should we be truncating it? - - if (dumbfile_error(f)) - return -1; - - return 0; -} - - - -typedef struct BUFFERED_MOD BUFFERED_MOD; - -struct BUFFERED_MOD -{ - unsigned char *buffered; - long ptr, len; - DUMBFILE *remaining; -}; - - - -static int buffer_mod_skip(void *f, long n) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - bm->ptr += n; - if (bm->ptr >= bm->len) { - free(bm->buffered); - bm->buffered = NULL; - return dumbfile_skip(bm->remaining, bm->ptr - bm->len); - } - return 0; - } - return dumbfile_skip(bm->remaining, n); -} - - - -static int buffer_mod_getc(void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - int rv = bm->buffered[bm->ptr++]; - if (bm->ptr >= bm->len) { - free(bm->buffered); - bm->buffered = NULL; - } - return rv; - } - return dumbfile_getc(bm->remaining); -} - - - -static long buffer_mod_getnc(char *ptr, long n, void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - int left = bm->len - bm->ptr; - if (n >= left) { - int rv; - memcpy(ptr, bm->buffered + bm->ptr, left); - free(bm->buffered); - bm->buffered = NULL; - rv = dumbfile_getnc(ptr + left, n - left, bm->remaining); - return left + MAX(rv, 0); - } - memcpy(ptr, bm->buffered + bm->ptr, n); - bm->ptr += n; - return n; - } - return dumbfile_getnc(ptr, n, bm->remaining); -} - - - -static void buffer_mod_close(void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) free(bm->buffered); - /* Do NOT close bm->remaining */ - free(f); -} - - - -DUMBFILE_SYSTEM buffer_mod_dfs = { - NULL, - &buffer_mod_skip, - &buffer_mod_getc, - &buffer_mod_getnc, - &buffer_mod_close -}; - - - -#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128) - -static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, unsigned long *fft) -{ - BUFFERED_MOD *bm = malloc(sizeof(*bm)); - if (!bm) return NULL; - - bm->buffered = malloc(MOD_FFT_OFFSET + 4); - if (!bm->buffered) { - free(bm); - return NULL; - } - - bm->len = dumbfile_getnc(bm->buffered, MOD_FFT_OFFSET + 4, f); - - if (bm->len > 0) { - if (bm->len >= MOD_FFT_OFFSET + 4) - *fft = (unsigned long)bm->buffered[MOD_FFT_OFFSET ] << 24 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+1] << 16 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+2] << 8 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+3]; - else - *fft = 0; - bm->ptr = 0; - } else { - free(bm->buffered); - bm->buffered = NULL; - } - - bm->remaining = f; - - return dumbfile_open_ex(bm, &buffer_mod_dfs); -} - - - -static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - int n_channels; - int i; - unsigned long fft; - - f = dumbfile_buffer_mod(f, &fft); - if (!f) - return NULL; - - sigdata = malloc(sizeof(*sigdata)); - if (!sigdata) { - dumbfile_close(f); - 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(sigdata->name, 20, f) < 20) { - free(sigdata); - dumbfile_close(f); - 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('F','L','T','4'): - 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; - } - } - - sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); - if (!sigdata->sample) { - free(sigdata); - dumbfile_close(f); - 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)) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - 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); - dumbfile_close(f); - 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); - dumbfile_close(f); - return NULL; - } - if (dumbfile_getnc(sigdata->order, 128, f) < 128) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - - /* "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); - - /* Work out how many patterns there are. */ - sigdata->n_patterns = -1; - for (i = 0; i < 128; i++) - if (sigdata->n_patterns < sigdata->order[i]) - sigdata->n_patterns = sigdata->order[i]; - sigdata->n_patterns++; - - /* 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); - dumbfile_close(f); - return NULL; - } - for (i = 0; i < sigdata->n_patterns; i++) - sigdata->pattern[i].entry = NULL; - - /* Read in the patterns */ - { - unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */ - if (!buffer) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - 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); - dumbfile_close(f); - 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)) { - continue; - } - } - - dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */ - /* The DUMBFILE originally passed to our function is intact. */ - - /* 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) { - sigdata->channel_pan[i+0] = 16; - sigdata->channel_pan[i+1] = 48; - sigdata->channel_pan[i+2] = 48; - sigdata->channel_pan[i+3] = 16; - } - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -DUH *dumb_read_mod_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_mod_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, unsigned long fft) +{ + long i; + long 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; + unsigned long 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) { + sigdata->channel_pan[i+0] = 16; + sigdata->channel_pan[i+1] = 48; + sigdata->channel_pan[i+2] = 48; + sigdata->channel_pan[i+3] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *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/Frameworks/Dumb/dumb/src/it/readmod2.c b/Frameworks/Dumb/dumb/src/it/readmod2.c index a031b47e8..fa5eacc50 100644 --- a/Frameworks/Dumb/dumb/src/it/readmod2.c +++ b/Frameworks/Dumb/dumb/src/it/readmod2.c @@ -1,29 +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 *dumb_read_mod(DUMBFILE *f) -{ - DUH *duh = dumb_read_mod_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *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/Frameworks/Dumb/dumb/src/it/readmtm.c b/Frameworks/Dumb/dumb/src/it/readmtm.c new file mode 100644 index 000000000..77f9d9e16 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readmtm.c @@ -0,0 +1,412 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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" + +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 = 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) +{ + long i; + long 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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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) { + + int l, 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 = 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 *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/Frameworks/Dumb/dumb/src/it/readokt.c b/Frameworks/Dumb/dumb/src/it/readokt.c new file mode 100644 index 000000000..75a38e1c9 --- /dev/null +++ b/Frameworks/Dumb/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 < 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; i < 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; i < 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 > j) sigdata->n_patterns = 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; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + for (i = 0; i < 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; i < 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 (; i < 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 *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/Frameworks/Dumb/dumb/src/it/readokt2.c b/Frameworks/Dumb/dumb/src/it/readokt2.c new file mode 100644 index 000000000..c3fc5ed57 --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_read_okt(DUMBFILE *f) +{ + DUH *duh = dumb_read_okt_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readoldpsm.c b/Frameworks/Dumb/dumb/src/it/readoldpsm.c new file mode 100644 index 000000000..d4faec147 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readoldpsm.c @@ -0,0 +1,688 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 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; + long 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 = 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; + long offset; +} +PSM_COMPONENT; + +static int 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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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 *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/Frameworks/Dumb/dumb/src/it/readpsm.c b/Frameworks/Dumb/dumb/src/it/readpsm.c new file mode 100644 index 000000000..bdece48bb --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readpsm.c @@ -0,0 +1,1286 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; + int length; + int loopstart; + int loopend; + int panpos; + int defvol; + int samplerate; + + 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 = 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 = 0; + 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(sizeof(sigdata->name) - 1, (unsigned)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) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + 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; + + 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 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 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 dumb_get_psm_subsong_count(DUMBFILE *f) { + int length, subsongs; + long 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, na, nb; + char * p; + + na = nb = 0; + + i = memcmp( a, b, l ); + if ( !i ) return i; + + /* 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 ( (const unsigned char *)p == 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 ( (const unsigned char *)p == b + j ) return -1; + } + + if ( i < j ) return -1; + else if ( j > i ) return 1; + + i = memcmp( a, b, j ); + if ( i ) return i; + + return na - nb; +} + + + +DUH *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"; + snprintf( version, 15, "%u", ver ); + version[15] = 0; + 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/Frameworks/Dumb/dumb/src/it/readptm.c b/Frameworks/Dumb/dumb/src/it/readptm.c new file mode 100644 index 000000000..59f2c0d14 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readptm.c @@ -0,0 +1,551 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, long *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) +{ + long 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; + long offset; +} +PTM_COMPONENT; + + + +static int 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)) { + _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)) { + _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)) { + _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 *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/Frameworks/Dumb/dumb/src/it/readriff.c b/Frameworks/Dumb/dumb/src/it/readriff.c new file mode 100644 index 000000000..1dd852d76 --- /dev/null +++ b/Frameworks/Dumb/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 *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/Frameworks/Dumb/dumb/src/it/reads3m.c b/Frameworks/Dumb/dumb/src/it/reads3m.c index 97d09d7d3..2ee647a4a 100644 --- a/Frameworks/Dumb/dumb/src/it/reads3m.c +++ b/Frameworks/Dumb/dumb/src/it/reads3m.c @@ -1,670 +1,765 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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" - - - -/** WARNING: this is duplicated in itread.c */ -static int it_seek(DUMBFILE *f, long offset) -{ - long pos = dumbfile_pos(f); - - if (pos > offset) - return -1; - - if (pos < offset) - if (dumbfile_skip(f, offset - pos)) - return -1; - - return 0; -} - - - -static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f) -{ - unsigned char type; - int flags; - - type = dumbfile_getc(f); - - if (type > 1) { - /** WARNING: no adlib support */ - } - - dumbfile_getnc(sample->filename, 13, f); - sample->filename[13] = 0; - - *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); - - if (dumbfile_getc(f) != 0) - /* Sample is packed apparently (or error reading from file). We don't - * know how to read packed samples. - */ - return -1; - - 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(sample->name, 28, f); - sample->name[28] = 0; - - if (type == 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; - if (flags & 2) sample->flags |= IT_SAMPLE_STEREO; - if (flags & 4) sample->flags |= IT_SAMPLE_16BIT; - - 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 - /* 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; - - return dumbfile_error(f); -} - - - -static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, DUMBFILE *f) -{ - long n; - - long 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_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 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. - */ - - /* Discard the length. */ - dumbfile_skip(f, 2); - - if (dumbfile_error(f)) - 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; - dumbfile_getnc(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, 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; - } - - 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++]; - if (entry->effect != 255) { - entry->mask |= IT_ENTRY_EFFECT; - if (entry->effect == IT_BREAK_TO_ROW) - entry->effectvalue -= (entry->effectvalue >> 4) * 6; - } - /** 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 IT_COMPONENT_INSTRUMENT 1 -#define IT_COMPONENT_PATTERN 2 -#define IT_COMPONENT_SAMPLE 3 - -typedef struct IT_COMPONENT -{ - unsigned char type; - unsigned char n; - long 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 it_component_compare(const void *e1, const void *e2) -{ - return ((const IT_COMPONENT *)e1)->offset - - ((const IT_COMPONENT *)e2)->offset; -} - - - -static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - - int flags, cwtv, ffi; - int default_pan_present; - - IT_COMPONENT *component; - int n_components = 0; - - int n; - - unsigned char *buffer; - - sigdata = malloc(sizeof(*sigdata)); - if (!sigdata) return NULL; - - dumbfile_getnc(sigdata->name, 28, f); - sigdata->name[28] = 0; - - if (dumbfile_getc(f) != 0x1A || 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_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; - - if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - sigdata->global_volume = dumbfile_getc(f) << 1; - 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 - //what do we do with master_volume? it's not the same as mixing volume... - sigdata->mixing_volume = 48; - - /* 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); - - /* Channel settings for 32 channels, 255=unused, +128=disabled */ - { - int i; - for (i = 0; i < 32; i++) { - int c = dumbfile_getc(f); - if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */ - sigdata->channel_volume[i] = 64; - sigdata->channel_pan[i] = c & 8 ? 12 : 3; - /** 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(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 = IT_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++) { - long offset = dumbfile_igetw(f) << 4; - 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 { - /** 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(IT_COMPONENT), &it_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++) { - long offset; - int m; - - if (it_seek(f, component[n].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - switch (component[n].type) { - - case IT_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 IT_COMPONENT_SAMPLE: - if (it_s3m_read_sample_header(&sigdata->sample[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 (it_seek(f, component[m].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, 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; -} - - - -DUH *dumb_read_s3m_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_s3m_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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, long *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) +{ + long n; + + long 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; + long 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 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; + 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 ? 12 : 3; + /** 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++) { + long 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++) { + long 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 *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/Frameworks/Dumb/dumb/src/it/reads3m2.c b/Frameworks/Dumb/dumb/src/it/reads3m2.c index c9447c260..0499eecde 100644 --- a/Frameworks/Dumb/dumb/src/it/reads3m2.c +++ b/Frameworks/Dumb/dumb/src/it/reads3m2.c @@ -1,29 +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 *dumb_read_s3m(DUMBFILE *f) -{ - DUH *duh = dumb_read_s3m_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_read_s3m(DUMBFILE *f) +{ + DUH *duh = dumb_read_s3m_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readstm.c b/Frameworks/Dumb/dumb/src/it/readstm.c new file mode 100644 index 000000000..2cbe715d6 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readstm.c @@ -0,0 +1,395 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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; + 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 = 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 ); + sigdata->channel_pan[ 0 ] = 48; + sigdata->channel_pan[ 1 ] = 16; + sigdata->channel_pan[ 2 ] = 48; + sigdata->channel_pan[ 3 ] = 16; + + 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 *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/Frameworks/Dumb/dumb/src/it/readstm2.c b/Frameworks/Dumb/dumb/src/it/readstm2.c new file mode 100644 index 000000000..c336b2a3d --- /dev/null +++ b/Frameworks/Dumb/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 *dumb_read_stm(DUMBFILE *f) +{ + DUH *duh = dumb_read_stm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readxm.c b/Frameworks/Dumb/dumb/src/it/readxm.c index 53f80d50e..03a73e6b0 100644 --- a/Frameworks/Dumb/dumb/src/it/readxm.c +++ b/Frameworks/Dumb/dumb/src/it/readxm.c @@ -1,1007 +1,1417 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/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" - - - -/** TODO: - - * XM_TREMOLO doesn't sound quite right... - * XM_E_SET_FINETUNE done but not tested yet. - * 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 :) */ -static const char xm_convert_vibrato[] = { - IT_VIBRATO_SINE, - IT_VIBRATO_SQUARE, - IT_VIBRATO_SAWTOOTH, - IT_VIBRATO_SAWTOOTH -}; - - - -#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 */ -} -XM_INSTRUMENT_EXTRA; - - - -/* 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 size; - int pos; - int channel; - int row; - int effect, effectvalue; - IT_ENTRY *entry; - - /* pattern header size */ - if (dumbfile_igetl(f) != 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; - } - - 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(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; - } - - 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); - - entry++; - } - - channel++; - if (channel >= n_channels) { - channel = 0; - 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; - - if (envelope->n_nodes > 12) { - TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); - envelope->n_nodes = 0; - return -1; - } - - pos = 0; - for (i = 0; i < envelope->n_nodes; i++) { - envelope->node_t[i] = data[pos++]; - if (data[pos] > 64) { - TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]); - envelope->n_nodes = 0; - return -1; - } - envelope->node_y[i] = (signed char)(data[pos++] + y_offset); - } - - return 0; -} - - - -static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f) -{ - unsigned long size, bytes_read; - unsigned short vol_points[24]; - unsigned short pan_points[24]; - int i, type; - - /* 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. - */ - size = dumbfile_igetl(f); - - dumbfile_getnc(instrument->name, 22, f); - instrument->name[22] = 0; - 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 */ - if (dumbfile_igetl(f) != 0x28) { - TRACE("XM error: unexpected sample header size\n"); - return -1; - } - - /* 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) - 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 (dumbfile_skip(f, size - bytes_read)) - 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; - - 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); - - dumbfile_skip(f, 1); /* reserved */ - - dumbfile_getnc(sample->name, 22, f); - sample->name[22] = 0; - - sample->filename[0] = 0; - - if (dumbfile_error(f)) - return -1; - - sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number)*pow(DUMB_PITCH_BASE, finetune*2)); - - sample->flags = IT_SAMPLE_EXISTS; - - roguebytes = (int)sample->length; - roguebytesmask = 3; - - if (type & XM_SAMPLE_16BIT) { - sample->flags |= IT_SAMPLE_16BIT; - sample->length >>= 1; - sample->loop_start >>= 1; - sample->loop_end >>= 1; - } else - roguebytesmask >>= 1; - - if (type & XM_SAMPLE_STEREO) { - sample->flags |= IT_SAMPLE_STEREO; - sample->length >>= 1; - sample->loop_start >>= 1; - sample->loop_end >>= 1; - } 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; - 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; - - return roguebytes; -} - - - -static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f) -{ - int old; - long i; - long truncated_size; - int n_channels; - long datasize; - - if (!(sample->flags & IT_SAMPLE_EXISTS)) - return dumbfile_skip(f, roguebytes); - - /* 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; - } - - n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; - datasize = sample->length * n_channels; - - sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); - if (!sample->data) - return -1; - - /* sample data is stored as signed delta values */ - old = 0; - if (sample->flags & IT_SAMPLE_16BIT) - for (i = 0; i < sample->length; i++) - ((short *)sample->data)[i*n_channels] = old += dumbfile_igetw(f); - else - for (i = 0; i < sample->length; i++) - ((signed char *)sample->data)[i*n_channels] = old += dumbfile_getc(f); - - /* skip truncated data */ - dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); - - if (sample->flags & IT_SAMPLE_STEREO) { - old = 0; - if (sample->flags & IT_SAMPLE_16BIT) - for (i = 1; i < datasize; i += 2) - ((short *)sample->data)[i] = old += dumbfile_igetw(f); - else - for (i = 1; i < datasize; i += 2) - ((signed char *)sample->data)[i] = old += dumbfile_getc(f); - - /* skip truncated data */ - dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); - } - - dumbfile_skip(f, roguebytes); - - if (dumbfile_error(f)) - return -1; - - 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) -{ - DUMB_IT_SIGDATA *sigdata; - char id_text[18]; - - 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(sigdata->name, 20, f) < 20) { - free(sigdata); - return NULL; - } - sigdata->name[20] = 0; - - 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 */ - if (dumbfile_igetw(f) != 0x0104) { - TRACE("XM error: wrong format version\n"); - free(sigdata); - return NULL; - } - - /* - ------------------ - --- Header --- - ------------------ - */ - - /* header size */ - if (dumbfile_igetl(f) != 0x0114) { - 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_patterns = dumbfile_igetw(f); - sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */ - 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); - - /* sanity checks */ - if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 128 || 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(sigdata->order, sigdata->n_orders, f); - dumbfile_skip(f, 256 - sigdata->n_orders); - - if (dumbfile_error(f)) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - /* - -------------------- - --- 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) != 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; - - if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) { - TRACE("XM error: instrument %d\n", i+1); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - 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) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (j = total_samples; j < total_samples+extra.n_samples; j++) - sigdata->sample[j].data = NULL; - - /* read instrument's samples */ - for (j = 0; j < extra.n_samples; j++) { - IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; - int b = it_xm_read_sample_header(sample, f); - if (b < 0) { - _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]; - } - for (j = 0; j < extra.n_samples; j++) { - if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - } - total_samples += extra.n_samples; - } - } - - sigdata->n_samples = total_samples; - - 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. - */ -long 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; - long 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 */ - - - -DUH *dumb_read_xm_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_xm_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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" + + + +/** 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 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 limit_xm_skip_end(void *f, long n) +{ + DUMBFILE *df = f; + LIMITED_XM *lx = df->file; + return dumbfile_skip( lx->remaining, n ); +} + +static int limit_xm_skip(void *f, long n) +{ + LIMITED_XM *lx = f; + lx->ptr += n; + return 0; +} + + + +static int limit_xm_getc(void *f) +{ + LIMITED_XM *lx = f; + if (lx->ptr >= lx->allocated) { + return 0; + } + return lx->buffered[lx->ptr++]; +} + + + +static long limit_xm_getnc(char *ptr, long 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 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 limit_xm_seek(void *f, long n) +{ + (void)f; + (void)n; + return 1; +} + + + +static long 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) +{ + unsigned long 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 = (long)(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; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } else + roguebytesmask >>= 1; + + if (type & XM_SAMPLE_STEREO) { + sample->flags |= IT_SAMPLE_STEREO; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } 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; + 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; + + return roguebytes; +} + + + +static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f) +{ + int old; + long i; + long truncated_size; + int n_channels; + long datasize; + + if (!(sample->flags & IT_SAMPLE_EXISTS)) + return dumbfile_skip(f, roguebytes); + + /* 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; + } + + n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + datasize = sample->length * n_channels; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (roguebytes == 4) + { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + roguebytes = 0; + } + else + { + /* sample data is stored as signed delta values */ + old = 0; + if (sample->flags & IT_SAMPLE_16BIT) + for (i = 0; i < sample->length; i++) + ((short *)sample->data)[i*n_channels] = old += dumbfile_igetw(f); + else + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i*n_channels] = old += dumbfile_getc(f); + } + + /* skip truncated data */ + dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); + + if (sample->flags & IT_SAMPLE_STEREO) { + old = 0; + if (sample->flags & IT_SAMPLE_16BIT) + for (i = 1; i < datasize; i += 2) + ((short *)sample->data)[i] = old += dumbfile_igetw(f); + else + for (i = 1; i < datasize; i += 2) + ((signed char *)sample->data)[i] = old += dumbfile_getc(f); + + /* skip truncated data */ + dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); + } + + dumbfile_skip(f, roguebytes); + + if (dumbfile_error(f)) + return -1; + + 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); + + /* 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) { + _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. + */ +long 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; + long 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 *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/Frameworks/Dumb/dumb/src/it/readxm2.c b/Frameworks/Dumb/dumb/src/it/readxm2.c index b678bd2d1..c79f753fd 100644 --- a/Frameworks/Dumb/dumb/src/it/readxm2.c +++ b/Frameworks/Dumb/dumb/src/it/readxm2.c @@ -1,29 +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 *dumb_read_xm(DUMBFILE *f) -{ - DUH *duh = dumb_read_xm_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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 *dumb_read_xm(DUMBFILE *f) +{ + DUH *duh = dumb_read_xm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/xmeffect.c b/Frameworks/Dumb/dumb/src/it/xmeffect.c index cd39749a8..1aa489222 100644 --- a/Frameworks/Dumb/dumb/src/it/xmeffect.c +++ b/Frameworks/Dumb/dumb/src/it/xmeffect.c @@ -1,242 +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) -{ -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; /** TODO: glissando control */ - 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); - 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: - effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); - break; - - case XM_SET_GLOBAL_VOLUME: - effect = IT_SET_GLOBAL_VOLUME; - value *= 2; - 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; /** TODO */ - 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_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_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; /** TODO: value&4 -> don't retrig wave */ - break; - - case EBASE + XM_E_SET_TREMOLO_CONTROL: - effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; - value &= ~4; /** TODO: value&4 -> don't retrig wave */ - 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; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/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/Frameworks/Dumb/dumb/vc6/dumb/.gitignore b/Frameworks/Dumb/dumb/vc6/dumb/.gitignore new file mode 100644 index 000000000..32cff875e --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/.gitignore @@ -0,0 +1,3 @@ +*.user +Debug +Release \ No newline at end of file diff --git a/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj new file mode 100644 index 000000000..525bc56fc --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj @@ -0,0 +1,2007 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj new file mode 100644 index 000000000..a6441671a --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {612D360C-A51B-4B34-8F49-33F42A2957F5} + dumb + + + + + + + + + + + + StaticLibrary + true + v110_xp + + + StaticLibrary + v110_xp + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ../../include;%(AdditionalIncludeDirectories) + _USE_SSE;_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;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/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters new file mode 100644 index 000000000..c8d1360f3 --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters @@ -0,0 +1,341 @@ + + + + + {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\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\helpers + + + 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 + + + src\helpers + + + + + include + + + include\internal + + + include\internal + + + 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/Frameworks/FLAC/flac.xcodeproj/project.pbxproj b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj index f9170e2ea..86a46f3bc 100644 --- a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj +++ b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj @@ -420,9 +420,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "flac" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* flac */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -503,6 +509,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_NAME = FLAC; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = ""; USE_SEPERATE_HEADERMAPS = YES; WRAPPER_EXTENSION = framework; @@ -543,6 +550,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_NAME = FLAC; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = ""; USE_SEPERATE_HEADERMAPS = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index a43cbe1b1..7fabe9042 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 17C8F1F40CBED286008D969D /* Ay_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */; }; 17C8F1F50CBED286008D969D /* Ay_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F18C0CBED286008D969D /* Ay_Apu.h */; }; 17C8F1F60CBED286008D969D /* Ay_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */; }; - 17C8F1F70CBED286008D969D /* Ay_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F18E0CBED286008D969D /* Ay_Cpu.h */; }; 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */; }; 17C8F1F90CBED286008D969D /* Ay_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1900CBED286008D969D /* Ay_Emu.h */; }; 17C8F1FA0CBED286008D969D /* blargg_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1910CBED286008D969D /* blargg_common.h */; }; @@ -31,7 +30,6 @@ 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A00CBED286008D969D /* Fir_Resampler.h */; }; 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */; }; 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A20CBED286008D969D /* Gb_Apu.h */; }; - 17C8F20C0CBED286008D969D /* gb_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A30CBED286008D969D /* gb_cpu_io.h */; }; 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */; }; 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A50CBED286008D969D /* Gb_Cpu.h */; }; 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */; }; @@ -46,13 +44,11 @@ 17C8F2190CBED286008D969D /* Gym_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B00CBED286008D969D /* Gym_Emu.h */; }; 17C8F21A0CBED286008D969D /* Hes_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */; }; 17C8F21B0CBED286008D969D /* Hes_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B20CBED286008D969D /* Hes_Apu.h */; }; - 17C8F21C0CBED286008D969D /* hes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B30CBED286008D969D /* hes_cpu_io.h */; }; 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */; }; 17C8F21E0CBED286008D969D /* Hes_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B50CBED286008D969D /* Hes_Cpu.h */; }; 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */; }; 17C8F2200CBED286008D969D /* Hes_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B70CBED286008D969D /* Hes_Emu.h */; }; 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */; }; - 17C8F2220CBED286008D969D /* Kss_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B90CBED286008D969D /* Kss_Cpu.h */; }; 17C8F2230CBED286008D969D /* Kss_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */; }; 17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1BB0CBED286008D969D /* Kss_Emu.h */; }; 17C8F2250CBED286008D969D /* Kss_Scc_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */; }; @@ -65,7 +61,6 @@ 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C40CBED286008D969D /* Music_Emu.h */; }; 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */; }; 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C60CBED286008D969D /* Nes_Apu.h */; }; - 17C8F2300CBED286008D969D /* nes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C70CBED286008D969D /* nes_cpu_io.h */; }; 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */; }; 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C90CBED286008D969D /* Nes_Cpu.h */; }; 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */; }; @@ -82,14 +77,11 @@ 17C8F23E0CBED286008D969D /* Nsfe_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */; }; 17C8F23F0CBED286008D969D /* Sap_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */; }; 17C8F2400CBED286008D969D /* Sap_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D70CBED286008D969D /* Sap_Apu.h */; }; - 17C8F2410CBED286008D969D /* sap_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D80CBED286008D969D /* sap_cpu_io.h */; }; 17C8F2420CBED286008D969D /* Sap_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */; }; - 17C8F2430CBED286008D969D /* Sap_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */; }; 17C8F2440CBED286008D969D /* Sap_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */; }; 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DC0CBED286008D969D /* Sap_Emu.h */; }; 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */; }; 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DE0CBED286008D969D /* Sms_Apu.h */; }; - 17C8F2480CBED286008D969D /* Sms_Oscs.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */; }; 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */; }; 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E10CBED286008D969D /* Snes_Spc.h */; }; 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */; }; @@ -98,14 +90,171 @@ 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E50CBED286008D969D /* Spc_Dsp.h */; }; 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */; }; 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E70CBED286008D969D /* Spc_Emu.h */; }; - 17C8F2510CBED286008D969D /* Vgm_Emu_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */; }; - 17C8F2520CBED286008D969D /* Vgm_Emu_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */; }; 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */; }; 17C8F2540CBED286008D969D /* Vgm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */; }; 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */; }; 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */; }; 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */; }; 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */; }; + 8370B72F17F615FE001A4D7A /* adlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68C17F615FD001A4D7A /* adlib.h */; }; + 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */; }; + 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68E17F615FD001A4D7A /* Ay_Core.h */; }; + 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68F17F615FD001A4D7A /* blargg_common.cpp */; }; + 8370B73317F615FE001A4D7A /* blargg_errors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69017F615FD001A4D7A /* blargg_errors.cpp */; }; + 8370B73417F615FE001A4D7A /* blargg_errors.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69117F615FD001A4D7A /* blargg_errors.h */; }; + 8370B73517F615FE001A4D7A /* Blip_Buffer_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */; }; + 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */; }; + 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */; }; + 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69517F615FD001A4D7A /* Bml_Parser.h */; }; + 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69617F615FD001A4D7A /* C140_Emu.cpp */; }; + 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69717F615FD001A4D7A /* C140_Emu.h */; }; + 8370B73B17F615FE001A4D7A /* c140.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69817F615FD001A4D7A /* c140.c */; }; + 8370B73C17F615FE001A4D7A /* c140.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69917F615FD001A4D7A /* c140.h */; }; + 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */; }; + 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69B17F615FD001A4D7A /* dac_control.c */; }; + 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69C17F615FD001A4D7A /* dac_control.h */; }; + 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69D17F615FD001A4D7A /* dbopl.cpp */; }; + 8370B74117F615FE001A4D7A /* dbopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69E17F615FD001A4D7A /* dbopl.h */; }; + 8370B74217F615FE001A4D7A /* divfix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69F17F615FD001A4D7A /* divfix.h */; }; + 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A017F615FD001A4D7A /* Downsampler.cpp */; }; + 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A117F615FD001A4D7A /* Downsampler.h */; }; + 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A217F615FD001A4D7A /* emuconfig.h */; }; + 8370B74617F615FE001A4D7A /* fm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A317F615FD001A4D7A /* fm.c */; }; + 8370B74717F615FE001A4D7A /* fm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A417F615FD001A4D7A /* fm.h */; }; + 8370B74817F615FE001A4D7A /* fm2612.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A517F615FD001A4D7A /* fm2612.c */; }; + 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A617F615FD001A4D7A /* fmopl.cpp */; }; + 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A717F615FD001A4D7A /* fmopl.h */; }; + 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */; }; + 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */; }; + 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */; }; + 8370B74E17F615FE001A4D7A /* Gbs_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */; }; + 8370B75117F615FE001A4D7A /* Gme_Loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */; }; + 8370B75217F615FE001A4D7A /* Gme_Loader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */; }; + 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */; }; + 8370B75417F615FE001A4D7A /* Hes_Apu_Adpcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */; }; + 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */; }; + 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B317F615FD001A4D7A /* Hes_Core.h */; }; + 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */; }; + 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B517F615FD001A4D7A /* i_fmpac.h */; }; + 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B617F615FD001A4D7A /* i_fmunit.h */; }; + 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B717F615FD001A4D7A /* i_vrc7.h */; }; + 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */; }; + 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B917F615FD001A4D7A /* K051649_Emu.h */; }; + 8370B75D17F615FE001A4D7A /* k051649.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BA17F615FD001A4D7A /* k051649.c */; }; + 8370B75E17F615FE001A4D7A /* k051649.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BB17F615FD001A4D7A /* k051649.h */; }; + 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */; }; + 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */; }; + 8370B76117F615FE001A4D7A /* k053260.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BE17F615FD001A4D7A /* k053260.c */; }; + 8370B76217F615FE001A4D7A /* k053260.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BF17F615FD001A4D7A /* k053260.h */; }; + 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */; }; + 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C117F615FD001A4D7A /* K054539_Emu.h */; }; + 8370B76517F615FE001A4D7A /* k054539.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C217F615FD001A4D7A /* k054539.c */; }; + 8370B76617F615FE001A4D7A /* k054539.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C317F615FD001A4D7A /* k054539.h */; }; + 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C417F615FD001A4D7A /* kmsnddev.h */; }; + 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */; }; + 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C617F615FD001A4D7A /* Kss_Core.h */; }; + 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C717F615FD001A4D7A /* mamedef.h */; }; + 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C817F615FD001A4D7A /* mathdefs.h */; }; + 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */; }; + 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */; }; + 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */; }; + 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */; }; + 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */; }; + 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */; }; + 8370B77217F615FE001A4D7A /* nestypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CF17F615FD001A4D7A /* nestypes.h */; }; + 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */; }; + 8370B77417F615FE001A4D7A /* Nsf_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D117F615FD001A4D7A /* Nsf_Core.h */; }; + 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */; }; + 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */; }; + 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */; }; + 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */; }; + 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */; }; + 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D717F615FD001A4D7A /* okim6258.c */; }; + 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D817F615FD001A4D7A /* okim6258.h */; }; + 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */; }; + 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */; }; + 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DB17F615FD001A4D7A /* okim6295.c */; }; + 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DC17F615FD001A4D7A /* okim6295.h */; }; + 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */; }; + 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */; }; + 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */; }; + 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */; }; + 8370B78417F615FE001A4D7A /* pwm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E117F615FE001A4D7A /* pwm.c */; }; + 8370B78517F615FE001A4D7A /* pwm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E217F615FE001A4D7A /* pwm.h */; }; + 8370B78617F615FE001A4D7A /* qmix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E317F615FE001A4D7A /* qmix.c */; }; + 8370B78717F615FE001A4D7A /* qmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E417F615FE001A4D7A /* qmix.h */; }; + 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */; }; + 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */; }; + 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E717F615FE001A4D7A /* Resampler.cpp */; }; + 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E817F615FE001A4D7A /* Resampler.h */; }; + 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */; }; + 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */; }; + 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EB17F615FE001A4D7A /* rf5c68.c */; }; + 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EC17F615FE001A4D7A /* rf5c68.h */; }; + 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */; }; + 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */; }; + 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */; }; + 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F017F615FE001A4D7A /* Rom_Data.h */; }; + 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F117F615FE001A4D7A /* s_deltat.c */; }; + 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F217F615FE001A4D7A /* s_deltat.h */; }; + 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F317F615FE001A4D7A /* s_logtbl.c */; }; + 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F417F615FE001A4D7A /* s_logtbl.h */; }; + 8370B79817F615FE001A4D7A /* s_opl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F517F615FE001A4D7A /* s_opl.c */; }; + 8370B79917F615FE001A4D7A /* s_opl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F617F615FE001A4D7A /* s_opl.h */; }; + 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F717F615FE001A4D7A /* s_opltbl.c */; }; + 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F817F615FE001A4D7A /* s_opltbl.h */; }; + 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */; }; + 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FA17F615FE001A4D7A /* Sap_Core.h */; }; + 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FB17F615FE001A4D7A /* scd_pcm.c */; }; + 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FC17F615FE001A4D7A /* scd_pcm.h */; }; + 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */; }; + 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */; }; + 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FF17F615FE001A4D7A /* segapcm.c */; }; + 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70017F615FE001A4D7A /* segapcm.h */; }; + 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */; }; + 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70217F615FE001A4D7A /* Sgc_Core.h */; }; + 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */; }; + 8370B7A717F615FE001A4D7A /* Sgc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */; }; + 8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70517F615FE001A4D7A /* Sgc_Emu.h */; }; + 8370B7A917F615FE001A4D7A /* Sgc_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */; }; + 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70717F615FE001A4D7A /* Sgc_Impl.h */; }; + 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */; }; + 8370B7AC17F615FE001A4D7A /* Sms_Fm_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */; }; + 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */; }; + 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70B17F615FE001A4D7A /* Spc_Filter.h */; }; + 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */; }; + 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */; }; + 8370B7B117F615FE001A4D7A /* Track_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */; }; + 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70F17F615FE001A4D7A /* Track_Filter.h */; }; + 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71017F615FE001A4D7A /* Upsampler.cpp */; }; + 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71117F615FE001A4D7A /* Upsampler.h */; }; + 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */; }; + 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71317F615FE001A4D7A /* Vgm_Core.h */; }; + 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */; }; + 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */; }; + 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71617F615FE001A4D7A /* ym2151.c */; }; + 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71717F615FE001A4D7A /* ym2151.h */; }; + 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */; }; + 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */; }; + 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71A17F615FE001A4D7A /* ym2413.c */; }; + 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71B17F615FE001A4D7A /* ym2413.h */; }; + 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */; }; + 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */; }; + 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */; }; + 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */; }; + 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */; }; + 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */; }; + 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72417F615FE001A4D7A /* ymdeltat.cpp */; }; + 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72517F615FE001A4D7A /* ymdeltat.h */; }; + 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */; }; + 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */; }; + 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */; }; + 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */; }; + 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72A17F615FE001A4D7A /* ymz280b.c */; }; + 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72B17F615FE001A4D7A /* ymz280b.h */; }; + 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */; }; + 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */; }; + 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -118,7 +267,6 @@ 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Apu.cpp; path = gme/Ay_Apu.cpp; sourceTree = ""; }; 17C8F18C0CBED286008D969D /* Ay_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Apu.h; path = gme/Ay_Apu.h; sourceTree = ""; }; 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Cpu.cpp; path = gme/Ay_Cpu.cpp; sourceTree = ""; }; - 17C8F18E0CBED286008D969D /* Ay_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Cpu.h; path = gme/Ay_Cpu.h; sourceTree = ""; }; 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Emu.cpp; path = gme/Ay_Emu.cpp; sourceTree = ""; }; 17C8F1900CBED286008D969D /* Ay_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Emu.h; path = gme/Ay_Emu.h; sourceTree = ""; }; 17C8F1910CBED286008D969D /* blargg_common.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = blargg_common.h; path = gme/blargg_common.h; sourceTree = ""; }; @@ -139,7 +287,6 @@ 17C8F1A00CBED286008D969D /* Fir_Resampler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Fir_Resampler.h; path = gme/Fir_Resampler.h; sourceTree = ""; }; 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Apu.cpp; path = gme/Gb_Apu.cpp; sourceTree = ""; }; 17C8F1A20CBED286008D969D /* Gb_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gb_Apu.h; path = gme/Gb_Apu.h; sourceTree = ""; }; - 17C8F1A30CBED286008D969D /* gb_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = gb_cpu_io.h; path = gme/gb_cpu_io.h; sourceTree = ""; }; 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Cpu.cpp; path = gme/Gb_Cpu.cpp; sourceTree = ""; }; 17C8F1A50CBED286008D969D /* Gb_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gb_Cpu.h; path = gme/Gb_Cpu.h; sourceTree = ""; }; 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Oscs.cpp; path = gme/Gb_Oscs.cpp; sourceTree = ""; }; @@ -154,13 +301,11 @@ 17C8F1B00CBED286008D969D /* Gym_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gym_Emu.h; path = gme/Gym_Emu.h; sourceTree = ""; }; 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Apu.cpp; path = gme/Hes_Apu.cpp; sourceTree = ""; }; 17C8F1B20CBED286008D969D /* Hes_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Apu.h; path = gme/Hes_Apu.h; sourceTree = ""; }; - 17C8F1B30CBED286008D969D /* hes_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = hes_cpu_io.h; path = gme/hes_cpu_io.h; sourceTree = ""; }; 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Cpu.cpp; path = gme/Hes_Cpu.cpp; sourceTree = ""; }; 17C8F1B50CBED286008D969D /* Hes_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Cpu.h; path = gme/Hes_Cpu.h; sourceTree = ""; }; 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Emu.cpp; path = gme/Hes_Emu.cpp; sourceTree = ""; }; 17C8F1B70CBED286008D969D /* Hes_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Emu.h; path = gme/Hes_Emu.h; sourceTree = ""; }; 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Cpu.cpp; path = gme/Kss_Cpu.cpp; sourceTree = ""; }; - 17C8F1B90CBED286008D969D /* Kss_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Kss_Cpu.h; path = gme/Kss_Cpu.h; sourceTree = ""; }; 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Emu.cpp; path = gme/Kss_Emu.cpp; sourceTree = ""; }; 17C8F1BB0CBED286008D969D /* Kss_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Kss_Emu.h; path = gme/Kss_Emu.h; sourceTree = ""; }; 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Scc_Apu.cpp; path = gme/Kss_Scc_Apu.cpp; sourceTree = ""; }; @@ -173,7 +318,6 @@ 17C8F1C40CBED286008D969D /* Music_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Music_Emu.h; path = gme/Music_Emu.h; sourceTree = ""; }; 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Apu.cpp; path = gme/Nes_Apu.cpp; sourceTree = ""; }; 17C8F1C60CBED286008D969D /* Nes_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nes_Apu.h; path = gme/Nes_Apu.h; sourceTree = ""; }; - 17C8F1C70CBED286008D969D /* nes_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = nes_cpu_io.h; path = gme/nes_cpu_io.h; sourceTree = ""; }; 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Cpu.cpp; path = gme/Nes_Cpu.cpp; sourceTree = ""; }; 17C8F1C90CBED286008D969D /* Nes_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nes_Cpu.h; path = gme/Nes_Cpu.h; sourceTree = ""; }; 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Fme7_Apu.cpp; path = gme/Nes_Fme7_Apu.cpp; sourceTree = ""; }; @@ -190,14 +334,11 @@ 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nsfe_Emu.h; path = gme/Nsfe_Emu.h; sourceTree = ""; }; 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Apu.cpp; path = gme/Sap_Apu.cpp; sourceTree = ""; }; 17C8F1D70CBED286008D969D /* Sap_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Apu.h; path = gme/Sap_Apu.h; sourceTree = ""; }; - 17C8F1D80CBED286008D969D /* sap_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = sap_cpu_io.h; path = gme/sap_cpu_io.h; sourceTree = ""; }; 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Cpu.cpp; path = gme/Sap_Cpu.cpp; sourceTree = ""; }; - 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Cpu.h; path = gme/Sap_Cpu.h; sourceTree = ""; }; 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Emu.cpp; path = gme/Sap_Emu.cpp; sourceTree = ""; }; 17C8F1DC0CBED286008D969D /* Sap_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Emu.h; path = gme/Sap_Emu.h; sourceTree = ""; }; 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Apu.cpp; path = gme/Sms_Apu.cpp; sourceTree = ""; }; 17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = ""; }; - 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Oscs.h; path = gme/Sms_Oscs.h; sourceTree = ""; }; 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Snes_Spc.cpp; path = gme/Snes_Spc.cpp; sourceTree = ""; }; 17C8F1E10CBED286008D969D /* Snes_Spc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Snes_Spc.h; path = gme/Snes_Spc.h; sourceTree = ""; }; 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Cpu.cpp; path = gme/Spc_Cpu.cpp; sourceTree = ""; }; @@ -206,14 +347,173 @@ 17C8F1E50CBED286008D969D /* Spc_Dsp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Dsp.h; path = gme/Spc_Dsp.h; sourceTree = ""; }; 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Emu.cpp; path = gme/Spc_Emu.cpp; sourceTree = ""; }; 17C8F1E70CBED286008D969D /* Spc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Emu.h; path = gme/Spc_Emu.h; sourceTree = ""; }; - 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu_Impl.cpp; path = gme/Vgm_Emu_Impl.cpp; sourceTree = ""; }; - 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Vgm_Emu_Impl.h; path = gme/Vgm_Emu_Impl.h; sourceTree = ""; }; 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu.cpp; path = gme/Vgm_Emu.cpp; sourceTree = ""; }; 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Vgm_Emu.h; path = gme/Vgm_Emu.h; sourceTree = ""; }; 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2413_Emu.cpp; path = gme/Ym2413_Emu.cpp; sourceTree = ""; }; 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2413_Emu.h; path = gme/Ym2413_Emu.h; sourceTree = ""; }; 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu.cpp; path = gme/Ym2612_Emu.cpp; sourceTree = ""; }; 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2612_Emu.h; path = gme/Ym2612_Emu.h; sourceTree = ""; }; + 8370B68C17F615FD001A4D7A /* adlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adlib.h; path = gme/adlib.h; sourceTree = ""; }; + 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Core.cpp; path = gme/Ay_Core.cpp; sourceTree = ""; }; + 8370B68E17F615FD001A4D7A /* Ay_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ay_Core.h; path = gme/Ay_Core.h; sourceTree = ""; }; + 8370B68F17F615FD001A4D7A /* blargg_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = blargg_common.cpp; path = gme/blargg_common.cpp; sourceTree = ""; }; + 8370B69017F615FD001A4D7A /* blargg_errors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = blargg_errors.cpp; path = gme/blargg_errors.cpp; sourceTree = ""; }; + 8370B69117F615FD001A4D7A /* blargg_errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blargg_errors.h; path = gme/blargg_errors.h; sourceTree = ""; }; + 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blip_Buffer_impl.h; path = gme/Blip_Buffer_impl.h; sourceTree = ""; }; + 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blip_Buffer_impl2.h; path = gme/Blip_Buffer_impl2.h; sourceTree = ""; }; + 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Bml_Parser.cpp; path = gme/Bml_Parser.cpp; sourceTree = ""; }; + 8370B69517F615FD001A4D7A /* Bml_Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bml_Parser.h; path = gme/Bml_Parser.h; sourceTree = ""; }; + 8370B69617F615FD001A4D7A /* C140_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C140_Emu.cpp; path = gme/C140_Emu.cpp; sourceTree = ""; }; + 8370B69717F615FD001A4D7A /* C140_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = C140_Emu.h; path = gme/C140_Emu.h; sourceTree = ""; }; + 8370B69817F615FD001A4D7A /* c140.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = c140.c; path = gme/c140.c; sourceTree = ""; }; + 8370B69917F615FD001A4D7A /* c140.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = c140.h; path = gme/c140.h; sourceTree = ""; }; + 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Chip_Resampler.h; path = gme/Chip_Resampler.h; sourceTree = ""; }; + 8370B69B17F615FD001A4D7A /* dac_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dac_control.c; path = gme/dac_control.c; sourceTree = ""; }; + 8370B69C17F615FD001A4D7A /* dac_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dac_control.h; path = gme/dac_control.h; sourceTree = ""; }; + 8370B69D17F615FD001A4D7A /* dbopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dbopl.cpp; path = gme/dbopl.cpp; sourceTree = ""; }; + 8370B69E17F615FD001A4D7A /* dbopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dbopl.h; path = gme/dbopl.h; sourceTree = ""; }; + 8370B69F17F615FD001A4D7A /* divfix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = divfix.h; path = gme/divfix.h; sourceTree = ""; }; + 8370B6A017F615FD001A4D7A /* Downsampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Downsampler.cpp; path = gme/Downsampler.cpp; sourceTree = ""; }; + 8370B6A117F615FD001A4D7A /* Downsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Downsampler.h; path = gme/Downsampler.h; sourceTree = ""; }; + 8370B6A217F615FD001A4D7A /* emuconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = emuconfig.h; path = gme/emuconfig.h; sourceTree = ""; }; + 8370B6A317F615FD001A4D7A /* fm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm.c; path = gme/fm.c; sourceTree = ""; }; + 8370B6A417F615FD001A4D7A /* fm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fm.h; path = gme/fm.h; sourceTree = ""; }; + 8370B6A517F615FD001A4D7A /* fm2612.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm2612.c; path = gme/fm2612.c; sourceTree = ""; }; + 8370B6A617F615FD001A4D7A /* fmopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fmopl.cpp; path = gme/fmopl.cpp; sourceTree = ""; }; + 8370B6A717F615FD001A4D7A /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmopl.h; path = gme/fmopl.h; sourceTree = ""; }; + 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gb_Cpu_run.h; path = gme/Gb_Cpu_run.h; sourceTree = ""; }; + 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gbs_Core.cpp; path = gme/Gbs_Core.cpp; sourceTree = ""; }; + 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gbs_Core.h; path = gme/Gbs_Core.h; sourceTree = ""; }; + 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gbs_Cpu.cpp; path = gme/Gbs_Cpu.cpp; sourceTree = ""; }; + 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gme_Loader.cpp; path = gme/Gme_Loader.cpp; sourceTree = ""; }; + 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gme_Loader.h; path = gme/Gme_Loader.h; sourceTree = ""; }; + 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Apu_Adpcm.cpp; path = gme/Hes_Apu_Adpcm.cpp; sourceTree = ""; }; + 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Apu_Adpcm.h; path = gme/Hes_Apu_Adpcm.h; sourceTree = ""; }; + 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Core.cpp; path = gme/Hes_Core.cpp; sourceTree = ""; }; + 8370B6B317F615FD001A4D7A /* Hes_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Core.h; path = gme/Hes_Core.h; sourceTree = ""; }; + 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Cpu_run.h; path = gme/Hes_Cpu_run.h; sourceTree = ""; }; + 8370B6B517F615FD001A4D7A /* i_fmpac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmpac.h; path = gme/i_fmpac.h; sourceTree = ""; }; + 8370B6B617F615FD001A4D7A /* i_fmunit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmunit.h; path = gme/i_fmunit.h; sourceTree = ""; }; + 8370B6B717F615FD001A4D7A /* i_vrc7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_vrc7.h; path = gme/i_vrc7.h; sourceTree = ""; }; + 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K051649_Emu.cpp; path = gme/K051649_Emu.cpp; sourceTree = ""; }; + 8370B6B917F615FD001A4D7A /* K051649_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K051649_Emu.h; path = gme/K051649_Emu.h; sourceTree = ""; }; + 8370B6BA17F615FD001A4D7A /* k051649.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k051649.c; path = gme/k051649.c; sourceTree = ""; }; + 8370B6BB17F615FD001A4D7A /* k051649.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k051649.h; path = gme/k051649.h; sourceTree = ""; }; + 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K053260_Emu.cpp; path = gme/K053260_Emu.cpp; sourceTree = ""; }; + 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K053260_Emu.h; path = gme/K053260_Emu.h; sourceTree = ""; }; + 8370B6BE17F615FD001A4D7A /* k053260.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k053260.c; path = gme/k053260.c; sourceTree = ""; }; + 8370B6BF17F615FD001A4D7A /* k053260.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k053260.h; path = gme/k053260.h; sourceTree = ""; }; + 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K054539_Emu.cpp; path = gme/K054539_Emu.cpp; sourceTree = ""; }; + 8370B6C117F615FD001A4D7A /* K054539_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K054539_Emu.h; path = gme/K054539_Emu.h; sourceTree = ""; }; + 8370B6C217F615FD001A4D7A /* k054539.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k054539.c; path = gme/k054539.c; sourceTree = ""; }; + 8370B6C317F615FD001A4D7A /* k054539.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k054539.h; path = gme/k054539.h; sourceTree = ""; }; + 8370B6C417F615FD001A4D7A /* kmsnddev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kmsnddev.h; path = gme/kmsnddev.h; sourceTree = ""; }; + 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Core.cpp; path = gme/Kss_Core.cpp; sourceTree = ""; }; + 8370B6C617F615FD001A4D7A /* Kss_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Kss_Core.h; path = gme/Kss_Core.h; sourceTree = ""; }; + 8370B6C717F615FD001A4D7A /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mamedef.h; path = gme/mamedef.h; sourceTree = ""; }; + 8370B6C817F615FD001A4D7A /* mathdefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mathdefs.h; path = gme/mathdefs.h; sourceTree = ""; }; + 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Cpu_run.h; path = gme/Nes_Cpu_run.h; sourceTree = ""; }; + 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Fds_Apu.cpp; path = gme/Nes_Fds_Apu.cpp; sourceTree = ""; }; + 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Fds_Apu.h; path = gme/Nes_Fds_Apu.h; sourceTree = ""; }; + 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Mmc5_Apu.h; path = gme/Nes_Mmc5_Apu.h; sourceTree = ""; }; + 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Vrc7_Apu.cpp; path = gme/Nes_Vrc7_Apu.cpp; sourceTree = ""; }; + 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Vrc7_Apu.h; path = gme/Nes_Vrc7_Apu.h; sourceTree = ""; }; + 8370B6CF17F615FD001A4D7A /* nestypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nestypes.h; path = gme/nestypes.h; sourceTree = ""; }; + 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Core.cpp; path = gme/Nsf_Core.cpp; sourceTree = ""; }; + 8370B6D117F615FD001A4D7A /* Nsf_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Core.h; path = gme/Nsf_Core.h; sourceTree = ""; }; + 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Cpu.cpp; path = gme/Nsf_Cpu.cpp; sourceTree = ""; }; + 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Impl.cpp; path = gme/Nsf_Impl.cpp; sourceTree = ""; }; + 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Impl.h; path = gme/Nsf_Impl.h; sourceTree = ""; }; + 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6258_Emu.cpp; path = gme/Okim6258_Emu.cpp; sourceTree = ""; }; + 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6258_Emu.h; path = gme/Okim6258_Emu.h; sourceTree = ""; }; + 8370B6D717F615FD001A4D7A /* okim6258.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6258.c; path = gme/okim6258.c; sourceTree = ""; }; + 8370B6D817F615FD001A4D7A /* okim6258.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6258.h; path = gme/okim6258.h; sourceTree = ""; }; + 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6295_Emu.cpp; path = gme/Okim6295_Emu.cpp; sourceTree = ""; }; + 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6295_Emu.h; path = gme/Okim6295_Emu.h; sourceTree = ""; }; + 8370B6DB17F615FD001A4D7A /* okim6295.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6295.c; path = gme/okim6295.c; sourceTree = ""; }; + 8370B6DC17F615FD001A4D7A /* okim6295.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6295.h; path = gme/okim6295.h; sourceTree = ""; }; + 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Opl_Apu.cpp; path = gme/Opl_Apu.cpp; sourceTree = ""; }; + 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opl_Apu.h; path = gme/Opl_Apu.h; sourceTree = ""; }; + 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Pwm_Emu.cpp; path = gme/Pwm_Emu.cpp; sourceTree = ""; }; + 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Pwm_Emu.h; path = gme/Pwm_Emu.h; sourceTree = ""; }; + 8370B6E117F615FE001A4D7A /* pwm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pwm.c; path = gme/pwm.c; sourceTree = ""; }; + 8370B6E217F615FE001A4D7A /* pwm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pwm.h; path = gme/pwm.h; sourceTree = ""; }; + 8370B6E317F615FE001A4D7A /* qmix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qmix.c; path = gme/qmix.c; sourceTree = ""; }; + 8370B6E417F615FE001A4D7A /* qmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qmix.h; path = gme/qmix.h; sourceTree = ""; }; + 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Qsound_Apu.cpp; path = gme/Qsound_Apu.cpp; sourceTree = ""; }; + 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Qsound_Apu.h; path = gme/Qsound_Apu.h; sourceTree = ""; }; + 8370B6E717F615FE001A4D7A /* Resampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resampler.cpp; path = gme/Resampler.cpp; sourceTree = ""; }; + 8370B6E817F615FE001A4D7A /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resampler.h; path = gme/Resampler.h; sourceTree = ""; }; + 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C68_Emu.cpp; path = gme/Rf5C68_Emu.cpp; sourceTree = ""; }; + 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C68_Emu.h; path = gme/Rf5C68_Emu.h; sourceTree = ""; }; + 8370B6EB17F615FE001A4D7A /* rf5c68.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rf5c68.c; path = gme/rf5c68.c; sourceTree = ""; }; + 8370B6EC17F615FE001A4D7A /* rf5c68.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rf5c68.h; path = gme/rf5c68.h; sourceTree = ""; }; + 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C164_Emu.cpp; path = gme/Rf5C164_Emu.cpp; sourceTree = ""; }; + 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C164_Emu.h; path = gme/Rf5C164_Emu.h; sourceTree = ""; }; + 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rom_Data.cpp; path = gme/Rom_Data.cpp; sourceTree = ""; }; + 8370B6F017F615FE001A4D7A /* Rom_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rom_Data.h; path = gme/Rom_Data.h; sourceTree = ""; }; + 8370B6F117F615FE001A4D7A /* s_deltat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_deltat.c; path = gme/s_deltat.c; sourceTree = ""; }; + 8370B6F217F615FE001A4D7A /* s_deltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_deltat.h; path = gme/s_deltat.h; sourceTree = ""; }; + 8370B6F317F615FE001A4D7A /* s_logtbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_logtbl.c; path = gme/s_logtbl.c; sourceTree = ""; }; + 8370B6F417F615FE001A4D7A /* s_logtbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_logtbl.h; path = gme/s_logtbl.h; sourceTree = ""; }; + 8370B6F517F615FE001A4D7A /* s_opl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opl.c; path = gme/s_opl.c; sourceTree = ""; }; + 8370B6F617F615FE001A4D7A /* s_opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opl.h; path = gme/s_opl.h; sourceTree = ""; }; + 8370B6F717F615FE001A4D7A /* s_opltbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opltbl.c; path = gme/s_opltbl.c; sourceTree = ""; }; + 8370B6F817F615FE001A4D7A /* s_opltbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opltbl.h; path = gme/s_opltbl.h; sourceTree = ""; }; + 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Core.cpp; path = gme/Sap_Core.cpp; sourceTree = ""; }; + 8370B6FA17F615FE001A4D7A /* Sap_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sap_Core.h; path = gme/Sap_Core.h; sourceTree = ""; }; + 8370B6FB17F615FE001A4D7A /* scd_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scd_pcm.c; path = gme/scd_pcm.c; sourceTree = ""; }; + 8370B6FC17F615FE001A4D7A /* scd_pcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scd_pcm.h; path = gme/scd_pcm.h; sourceTree = ""; }; + 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SegaPcm_Emu.cpp; path = gme/SegaPcm_Emu.cpp; sourceTree = ""; }; + 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SegaPcm_Emu.h; path = gme/SegaPcm_Emu.h; sourceTree = ""; }; + 8370B6FF17F615FE001A4D7A /* segapcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = segapcm.c; path = gme/segapcm.c; sourceTree = ""; }; + 8370B70017F615FE001A4D7A /* segapcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = segapcm.h; path = gme/segapcm.h; sourceTree = ""; }; + 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Core.cpp; path = gme/Sgc_Core.cpp; sourceTree = ""; }; + 8370B70217F615FE001A4D7A /* Sgc_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Core.h; path = gme/Sgc_Core.h; sourceTree = ""; }; + 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Cpu.cpp; path = gme/Sgc_Cpu.cpp; sourceTree = ""; }; + 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Emu.cpp; path = gme/Sgc_Emu.cpp; sourceTree = ""; }; + 8370B70517F615FE001A4D7A /* Sgc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Emu.h; path = gme/Sgc_Emu.h; sourceTree = ""; }; + 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Impl.cpp; path = gme/Sgc_Impl.cpp; sourceTree = ""; }; + 8370B70717F615FE001A4D7A /* Sgc_Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Impl.h; path = gme/Sgc_Impl.h; sourceTree = ""; }; + 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Fm_Apu.cpp; path = gme/Sms_Fm_Apu.cpp; sourceTree = ""; }; + 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sms_Fm_Apu.h; path = gme/Sms_Fm_Apu.h; sourceTree = ""; }; + 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Filter.cpp; path = gme/Spc_Filter.cpp; sourceTree = ""; }; + 8370B70B17F615FE001A4D7A /* Spc_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Filter.h; path = gme/Spc_Filter.h; sourceTree = ""; }; + 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Sfm.cpp; path = gme/Spc_Sfm.cpp; sourceTree = ""; }; + 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Sfm.h; path = gme/Spc_Sfm.h; sourceTree = ""; }; + 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Track_Filter.cpp; path = gme/Track_Filter.cpp; sourceTree = ""; }; + 8370B70F17F615FE001A4D7A /* Track_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Track_Filter.h; path = gme/Track_Filter.h; sourceTree = ""; }; + 8370B71017F615FE001A4D7A /* Upsampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Upsampler.cpp; path = gme/Upsampler.cpp; sourceTree = ""; }; + 8370B71117F615FE001A4D7A /* Upsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Upsampler.h; path = gme/Upsampler.h; sourceTree = ""; }; + 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Core.cpp; path = gme/Vgm_Core.cpp; sourceTree = ""; }; + 8370B71317F615FE001A4D7A /* Vgm_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Vgm_Core.h; path = gme/Vgm_Core.h; sourceTree = ""; }; + 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2151_Emu.cpp; path = gme/Ym2151_Emu.cpp; sourceTree = ""; }; + 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2151_Emu.h; path = gme/Ym2151_Emu.h; sourceTree = ""; }; + 8370B71617F615FE001A4D7A /* ym2151.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2151.c; path = gme/ym2151.c; sourceTree = ""; }; + 8370B71717F615FE001A4D7A /* ym2151.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2151.h; path = gme/ym2151.h; sourceTree = ""; }; + 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2203_Emu.cpp; path = gme/Ym2203_Emu.cpp; sourceTree = ""; }; + 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2203_Emu.h; path = gme/Ym2203_Emu.h; sourceTree = ""; }; + 8370B71A17F615FE001A4D7A /* ym2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2413.c; path = gme/ym2413.c; sourceTree = ""; }; + 8370B71B17F615FE001A4D7A /* ym2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2413.h; path = gme/ym2413.h; sourceTree = ""; }; + 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2608_Emu.cpp; path = gme/Ym2608_Emu.cpp; sourceTree = ""; }; + 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2608_Emu.h; path = gme/Ym2608_Emu.h; sourceTree = ""; }; + 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2610b_Emu.cpp; path = gme/Ym2610b_Emu.cpp; sourceTree = ""; }; + 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2610b_Emu.h; path = gme/Ym2610b_Emu.h; sourceTree = ""; }; + 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_Gens.cpp; path = gme/Ym2612_Emu_Gens.cpp; sourceTree = ""; }; + 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_MAME.cpp; path = gme/Ym2612_Emu_MAME.cpp; sourceTree = ""; }; + 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym3812_Emu.cpp; path = gme/Ym3812_Emu.cpp; sourceTree = ""; }; + 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym3812_Emu.h; path = gme/Ym3812_Emu.h; sourceTree = ""; }; + 8370B72417F615FE001A4D7A /* ymdeltat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ymdeltat.cpp; path = gme/ymdeltat.cpp; sourceTree = ""; }; + 8370B72517F615FE001A4D7A /* ymdeltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymdeltat.h; path = gme/ymdeltat.h; sourceTree = ""; }; + 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymf262_Emu.cpp; path = gme/Ymf262_Emu.cpp; sourceTree = ""; }; + 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymf262_Emu.h; path = gme/Ymf262_Emu.h; sourceTree = ""; }; + 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymz280b_Emu.cpp; path = gme/Ymz280b_Emu.cpp; sourceTree = ""; }; + 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymz280b_Emu.h; path = gme/Ymz280b_Emu.h; sourceTree = ""; }; + 8370B72A17F615FE001A4D7A /* ymz280b.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ymz280b.c; path = gme/ymz280b.c; sourceTree = ""; }; + 8370B72B17F615FE001A4D7A /* ymz280b.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymz280b.h; path = gme/ymz280b.h; sourceTree = ""; }; + 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu_run.h; path = gme/Z80_Cpu_run.h; sourceTree = ""; }; + 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80_Cpu.cpp; path = gme/Z80_Cpu.cpp; sourceTree = ""; }; + 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu.h; path = gme/Z80_Cpu.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* GME.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GME.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -298,10 +598,170 @@ 17C8F1860CBED26C008D969D /* Source */ = { isa = PBXGroup; children = ( + 8370B68C17F615FD001A4D7A /* adlib.h */, + 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */, + 8370B68E17F615FD001A4D7A /* Ay_Core.h */, + 8370B68F17F615FD001A4D7A /* blargg_common.cpp */, + 8370B69017F615FD001A4D7A /* blargg_errors.cpp */, + 8370B69117F615FD001A4D7A /* blargg_errors.h */, + 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */, + 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */, + 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */, + 8370B69517F615FD001A4D7A /* Bml_Parser.h */, + 8370B69617F615FD001A4D7A /* C140_Emu.cpp */, + 8370B69717F615FD001A4D7A /* C140_Emu.h */, + 8370B69817F615FD001A4D7A /* c140.c */, + 8370B69917F615FD001A4D7A /* c140.h */, + 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */, + 8370B69B17F615FD001A4D7A /* dac_control.c */, + 8370B69C17F615FD001A4D7A /* dac_control.h */, + 8370B69D17F615FD001A4D7A /* dbopl.cpp */, + 8370B69E17F615FD001A4D7A /* dbopl.h */, + 8370B69F17F615FD001A4D7A /* divfix.h */, + 8370B6A017F615FD001A4D7A /* Downsampler.cpp */, + 8370B6A117F615FD001A4D7A /* Downsampler.h */, + 8370B6A217F615FD001A4D7A /* emuconfig.h */, + 8370B6A317F615FD001A4D7A /* fm.c */, + 8370B6A417F615FD001A4D7A /* fm.h */, + 8370B6A517F615FD001A4D7A /* fm2612.c */, + 8370B6A617F615FD001A4D7A /* fmopl.cpp */, + 8370B6A717F615FD001A4D7A /* fmopl.h */, + 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */, + 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */, + 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */, + 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */, + 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */, + 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */, + 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */, + 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */, + 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */, + 8370B6B317F615FD001A4D7A /* Hes_Core.h */, + 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */, + 8370B6B517F615FD001A4D7A /* i_fmpac.h */, + 8370B6B617F615FD001A4D7A /* i_fmunit.h */, + 8370B6B717F615FD001A4D7A /* i_vrc7.h */, + 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */, + 8370B6B917F615FD001A4D7A /* K051649_Emu.h */, + 8370B6BA17F615FD001A4D7A /* k051649.c */, + 8370B6BB17F615FD001A4D7A /* k051649.h */, + 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */, + 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */, + 8370B6BE17F615FD001A4D7A /* k053260.c */, + 8370B6BF17F615FD001A4D7A /* k053260.h */, + 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */, + 8370B6C117F615FD001A4D7A /* K054539_Emu.h */, + 8370B6C217F615FD001A4D7A /* k054539.c */, + 8370B6C317F615FD001A4D7A /* k054539.h */, + 8370B6C417F615FD001A4D7A /* kmsnddev.h */, + 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */, + 8370B6C617F615FD001A4D7A /* Kss_Core.h */, + 8370B6C717F615FD001A4D7A /* mamedef.h */, + 8370B6C817F615FD001A4D7A /* mathdefs.h */, + 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */, + 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */, + 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */, + 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */, + 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */, + 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */, + 8370B6CF17F615FD001A4D7A /* nestypes.h */, + 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */, + 8370B6D117F615FD001A4D7A /* Nsf_Core.h */, + 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */, + 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */, + 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */, + 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */, + 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */, + 8370B6D717F615FD001A4D7A /* okim6258.c */, + 8370B6D817F615FD001A4D7A /* okim6258.h */, + 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */, + 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */, + 8370B6DB17F615FD001A4D7A /* okim6295.c */, + 8370B6DC17F615FD001A4D7A /* okim6295.h */, + 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */, + 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */, + 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */, + 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */, + 8370B6E117F615FE001A4D7A /* pwm.c */, + 8370B6E217F615FE001A4D7A /* pwm.h */, + 8370B6E317F615FE001A4D7A /* qmix.c */, + 8370B6E417F615FE001A4D7A /* qmix.h */, + 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */, + 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */, + 8370B6E717F615FE001A4D7A /* Resampler.cpp */, + 8370B6E817F615FE001A4D7A /* Resampler.h */, + 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */, + 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */, + 8370B6EB17F615FE001A4D7A /* rf5c68.c */, + 8370B6EC17F615FE001A4D7A /* rf5c68.h */, + 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */, + 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */, + 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */, + 8370B6F017F615FE001A4D7A /* Rom_Data.h */, + 8370B6F117F615FE001A4D7A /* s_deltat.c */, + 8370B6F217F615FE001A4D7A /* s_deltat.h */, + 8370B6F317F615FE001A4D7A /* s_logtbl.c */, + 8370B6F417F615FE001A4D7A /* s_logtbl.h */, + 8370B6F517F615FE001A4D7A /* s_opl.c */, + 8370B6F617F615FE001A4D7A /* s_opl.h */, + 8370B6F717F615FE001A4D7A /* s_opltbl.c */, + 8370B6F817F615FE001A4D7A /* s_opltbl.h */, + 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */, + 8370B6FA17F615FE001A4D7A /* Sap_Core.h */, + 8370B6FB17F615FE001A4D7A /* scd_pcm.c */, + 8370B6FC17F615FE001A4D7A /* scd_pcm.h */, + 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */, + 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */, + 8370B6FF17F615FE001A4D7A /* segapcm.c */, + 8370B70017F615FE001A4D7A /* segapcm.h */, + 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */, + 8370B70217F615FE001A4D7A /* Sgc_Core.h */, + 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */, + 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */, + 8370B70517F615FE001A4D7A /* Sgc_Emu.h */, + 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */, + 8370B70717F615FE001A4D7A /* Sgc_Impl.h */, + 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */, + 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */, + 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */, + 8370B70B17F615FE001A4D7A /* Spc_Filter.h */, + 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */, + 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */, + 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */, + 8370B70F17F615FE001A4D7A /* Track_Filter.h */, + 8370B71017F615FE001A4D7A /* Upsampler.cpp */, + 8370B71117F615FE001A4D7A /* Upsampler.h */, + 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */, + 8370B71317F615FE001A4D7A /* Vgm_Core.h */, + 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */, + 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */, + 8370B71617F615FE001A4D7A /* ym2151.c */, + 8370B71717F615FE001A4D7A /* ym2151.h */, + 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */, + 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */, + 8370B71A17F615FE001A4D7A /* ym2413.c */, + 8370B71B17F615FE001A4D7A /* ym2413.h */, + 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */, + 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */, + 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */, + 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */, + 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */, + 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */, + 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */, + 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */, + 8370B72417F615FE001A4D7A /* ymdeltat.cpp */, + 8370B72517F615FE001A4D7A /* ymdeltat.h */, + 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */, + 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */, + 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */, + 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */, + 8370B72A17F615FE001A4D7A /* ymz280b.c */, + 8370B72B17F615FE001A4D7A /* ymz280b.h */, + 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */, + 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */, + 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */, 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */, 17C8F18C0CBED286008D969D /* Ay_Apu.h */, 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */, - 17C8F18E0CBED286008D969D /* Ay_Cpu.h */, 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */, 17C8F1900CBED286008D969D /* Ay_Emu.h */, 17C8F1910CBED286008D969D /* blargg_common.h */, @@ -322,7 +782,6 @@ 17C8F1A00CBED286008D969D /* Fir_Resampler.h */, 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */, 17C8F1A20CBED286008D969D /* Gb_Apu.h */, - 17C8F1A30CBED286008D969D /* gb_cpu_io.h */, 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */, 17C8F1A50CBED286008D969D /* Gb_Cpu.h */, 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */, @@ -336,13 +795,11 @@ 17C8F1B00CBED286008D969D /* Gym_Emu.h */, 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */, 17C8F1B20CBED286008D969D /* Hes_Apu.h */, - 17C8F1B30CBED286008D969D /* hes_cpu_io.h */, 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */, 17C8F1B50CBED286008D969D /* Hes_Cpu.h */, 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */, 17C8F1B70CBED286008D969D /* Hes_Emu.h */, 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */, - 17C8F1B90CBED286008D969D /* Kss_Cpu.h */, 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */, 17C8F1BB0CBED286008D969D /* Kss_Emu.h */, 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */, @@ -355,7 +812,6 @@ 17C8F1C40CBED286008D969D /* Music_Emu.h */, 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */, 17C8F1C60CBED286008D969D /* Nes_Apu.h */, - 17C8F1C70CBED286008D969D /* nes_cpu_io.h */, 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */, 17C8F1C90CBED286008D969D /* Nes_Cpu.h */, 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */, @@ -372,14 +828,11 @@ 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */, 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */, 17C8F1D70CBED286008D969D /* Sap_Apu.h */, - 17C8F1D80CBED286008D969D /* sap_cpu_io.h */, 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */, - 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */, 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */, 17C8F1DC0CBED286008D969D /* Sap_Emu.h */, 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */, 17C8F1DE0CBED286008D969D /* Sms_Apu.h */, - 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */, 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */, 17C8F1E10CBED286008D969D /* Snes_Spc.h */, 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */, @@ -388,8 +841,6 @@ 17C8F1E50CBED286008D969D /* Spc_Dsp.h */, 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */, 17C8F1E70CBED286008D969D /* Spc_Emu.h */, - 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */, - 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */, 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */, 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */, 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */, @@ -407,59 +858,136 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */, + 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */, + 8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */, + 8370B74217F615FE001A4D7A /* divfix.h in Headers */, + 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */, + 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */, + 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */, + 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */, + 8370B74717F615FE001A4D7A /* fm.h in Headers */, 17C8F1F50CBED286008D969D /* Ay_Apu.h in Headers */, - 17C8F1F70CBED286008D969D /* Ay_Cpu.h in Headers */, 17C8F1F90CBED286008D969D /* Ay_Emu.h in Headers */, + 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */, + 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */, 17C8F1FA0CBED286008D969D /* blargg_common.h in Headers */, + 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */, 17C8F1FB0CBED286008D969D /* blargg_config.h in Headers */, + 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */, 17C8F1FC0CBED286008D969D /* blargg_endian.h in Headers */, 17C8F1FD0CBED286008D969D /* blargg_source.h in Headers */, + 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */, + 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */, + 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */, + 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */, + 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */, + 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */, + 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */, + 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */, + 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */, + 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */, + 8370B75417F615FE001A4D7A /* Hes_Apu_Adpcm.h in Headers */, 17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */, + 8370B77417F615FE001A4D7A /* Nsf_Core.h in Headers */, 17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */, 17C8F2030CBED286008D969D /* Data_Reader.h in Headers */, 17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */, + 8370B76617F615FE001A4D7A /* k054539.h in Headers */, + 8370B78717F615FE001A4D7A /* qmix.h in Headers */, + 8370B73517F615FE001A4D7A /* Blip_Buffer_impl.h in Headers */, + 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */, 17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */, 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */, + 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */, + 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */, + 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, - 17C8F20C0CBED286008D969D /* gb_cpu_io.h in Headers */, + 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */, + 8370B77217F615FE001A4D7A /* nestypes.h in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, + 8370B75217F615FE001A4D7A /* Gme_Loader.h in Headers */, + 8370B72F17F615FE001A4D7A /* adlib.h in Headers */, + 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */, + 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, 17C8F2140CBED286008D969D /* Gme_File.h in Headers */, + 8370B76217F615FE001A4D7A /* k053260.h in Headers */, + 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */, + 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */, + 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */, 17C8F2160CBED286008D969D /* gme.h in Headers */, 17C8F2190CBED286008D969D /* Gym_Emu.h in Headers */, 17C8F21B0CBED286008D969D /* Hes_Apu.h in Headers */, - 17C8F21C0CBED286008D969D /* hes_cpu_io.h in Headers */, 17C8F21E0CBED286008D969D /* Hes_Cpu.h in Headers */, + 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */, + 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */, 17C8F2200CBED286008D969D /* Hes_Emu.h in Headers */, - 17C8F2220CBED286008D969D /* Kss_Cpu.h in Headers */, + 8370B78517F615FE001A4D7A /* pwm.h in Headers */, + 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */, + 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */, + 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */, + 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */, 17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */, 17C8F2260CBED286008D969D /* Kss_Scc_Apu.h in Headers */, 17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */, 17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */, + 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */, + 8370B74117F615FE001A4D7A /* dbopl.h in Headers */, + 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */, 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */, + 8370B7AC17F615FE001A4D7A /* Sms_Fm_Apu.h in Headers */, + 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */, 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */, - 17C8F2300CBED286008D969D /* nes_cpu_io.h in Headers */, 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */, + 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */, + 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */, 17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */, + 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */, + 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */, + 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */, 17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */, + 8370B73417F615FE001A4D7A /* blargg_errors.h in Headers */, 17C8F2380CBED286008D969D /* Nes_Oscs.h in Headers */, + 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */, 17C8F23A0CBED286008D969D /* Nes_Vrc6_Apu.h in Headers */, 17C8F23C0CBED286008D969D /* Nsf_Emu.h in Headers */, + 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */, 17C8F23E0CBED286008D969D /* Nsfe_Emu.h in Headers */, + 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */, + 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */, + 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */, + 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */, 17C8F2400CBED286008D969D /* Sap_Apu.h in Headers */, - 17C8F2410CBED286008D969D /* sap_cpu_io.h in Headers */, - 17C8F2430CBED286008D969D /* Sap_Cpu.h in Headers */, 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */, + 8370B73C17F615FE001A4D7A /* c140.h in Headers */, + 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */, 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */, - 17C8F2480CBED286008D969D /* Sms_Oscs.h in Headers */, + 8370B79917F615FE001A4D7A /* s_opl.h in Headers */, + 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */, 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */, + 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, + 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */, 17C8F24C0CBED286008D969D /* Spc_Cpu.h in Headers */, + 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */, + 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */, + 8370B75E17F615FE001A4D7A /* k051649.h in Headers */, + 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */, + 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */, + 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */, 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */, + 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */, + 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */, + 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */, + 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */, + 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */, 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */, - 17C8F2520CBED286008D969D /* Vgm_Emu_Impl.h in Headers */, + 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */, 17C8F2540CBED286008D969D /* Vgm_Emu.h in Headers */, 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */, + 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */, + 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */, 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -491,9 +1019,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "GME" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* GME */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -520,50 +1054,122 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */, + 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */, + 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */, + 8370B7B117F615FE001A4D7A /* Track_Filter.cpp in Sources */, + 8370B74617F615FE001A4D7A /* fm.c in Sources */, + 8370B76517F615FE001A4D7A /* k054539.c in Sources */, + 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */, 17C8F1F40CBED286008D969D /* Ay_Apu.cpp in Sources */, + 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */, 17C8F1F60CBED286008D969D /* Ay_Cpu.cpp in Sources */, 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */, + 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */, + 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */, 17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */, + 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */, 17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */, + 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */, + 8370B79817F615FE001A4D7A /* s_opl.c in Sources */, + 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */, + 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */, + 8370B74E17F615FE001A4D7A /* Gbs_Cpu.cpp in Sources */, 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, + 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */, + 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, + 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */, + 8370B74817F615FE001A4D7A /* fm2612.c in Sources */, + 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */, + 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */, 17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */, 17C8F2080CBED286008D969D /* Fir_Resampler.cpp in Sources */, + 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */, + 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */, + 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */, 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */, + 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */, + 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */, 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */, + 8370B7A917F615FE001A4D7A /* Sgc_Impl.cpp in Sources */, 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */, + 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */, + 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */, 17C8F2110CBED286008D969D /* Gbs_Emu.cpp in Sources */, + 8370B73317F615FE001A4D7A /* blargg_errors.cpp in Sources */, + 8370B73B17F615FE001A4D7A /* c140.c in Sources */, + 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */, 17C8F2130CBED286008D969D /* Gme_File.cpp in Sources */, 17C8F2150CBED286008D969D /* gme.cpp in Sources */, 17C8F2180CBED286008D969D /* Gym_Emu.cpp in Sources */, + 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */, + 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */, 17C8F21A0CBED286008D969D /* Hes_Apu.cpp in Sources */, 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */, 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */, + 8370B78617F615FE001A4D7A /* qmix.c in Sources */, 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */, + 8370B76117F615FE001A4D7A /* k053260.c in Sources */, + 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */, 17C8F2230CBED286008D969D /* Kss_Emu.cpp in Sources */, 17C8F2250CBED286008D969D /* Kss_Scc_Apu.cpp in Sources */, + 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */, + 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */, 17C8F2280CBED286008D969D /* M3u_Playlist.cpp in Sources */, 17C8F22A0CBED286008D969D /* Multi_Buffer.cpp in Sources */, + 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */, 17C8F22C0CBED286008D969D /* Music_Emu.cpp in Sources */, + 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */, 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */, 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */, 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */, + 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */, + 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */, + 8370B78417F615FE001A4D7A /* pwm.c in Sources */, 17C8F2350CBED286008D969D /* Nes_Namco_Apu.cpp in Sources */, + 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */, + 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */, 17C8F2370CBED286008D969D /* Nes_Oscs.cpp in Sources */, 17C8F2390CBED286008D969D /* Nes_Vrc6_Apu.cpp in Sources */, + 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */, + 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */, 17C8F23B0CBED286008D969D /* Nsf_Emu.cpp in Sources */, + 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */, + 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */, + 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */, + 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */, + 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */, + 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */, 17C8F23D0CBED286008D969D /* Nsfe_Emu.cpp in Sources */, + 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */, + 8370B7A717F615FE001A4D7A /* Sgc_Emu.cpp in Sources */, 17C8F23F0CBED286008D969D /* Sap_Apu.cpp in Sources */, + 8370B75117F615FE001A4D7A /* Gme_Loader.cpp in Sources */, + 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */, + 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */, 17C8F2420CBED286008D969D /* Sap_Cpu.cpp in Sources */, + 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */, 17C8F2440CBED286008D969D /* Sap_Emu.cpp in Sources */, + 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */, + 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */, 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */, 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */, + 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */, + 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */, + 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */, 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */, + 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */, + 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */, + 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */, + 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */, 17C8F24D0CBED286008D969D /* Spc_Dsp.cpp in Sources */, 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */, - 17C8F2510CBED286008D969D /* Vgm_Emu_Impl.cpp in Sources */, 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */, 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */, + 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */, + 8370B75D17F615FE001A4D7A /* k051649.c in Sources */, + 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */, 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -595,10 +1201,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = HAVE_STDINT_H; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; @@ -620,10 +1228,12 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = "HAVE_STDINT_H;NDEBUG"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/GME/gme/Ay_Apu.cpp b/Frameworks/GME/gme/Ay_Apu.cpp old mode 100755 new mode 100644 index 9dc5bb28e..79c2c05f3 --- a/Frameworks/GME/gme/Ay_Apu.cpp +++ b/Frameworks/GME/gme/Ay_Apu.cpp @@ -1,8 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Ay_Apu.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 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 @@ -22,7 +22,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // 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 inaudible_freq = 16384; int const period_factor = 16; @@ -67,12 +67,18 @@ static byte const modes [8] = MODE( 0,1, 0,0, 0,0 ), }; +void Ay_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + Ay_Apu::Ay_Apu() { // build full table of the upper 8 envelope waveforms for ( int m = 8; m--; ) { - byte* out = env.modes [m]; + byte* out = env_modes [m]; int flags = modes [m]; for ( int x = 3; --x >= 0; ) { @@ -89,27 +95,27 @@ Ay_Apu::Ay_Apu() } } - output( 0 ); + type_ = Ay8910; + set_output( NULL ); volume( 1.0 ); reset(); } void Ay_Apu::reset() { + addr_ = 0; last_time = 0; - noise.delay = 0; - noise.lfsr = 1; - - osc_t* osc = &oscs [osc_count]; - do + noise_delay = 0; + noise_lfsr = 1; + + for ( osc_t* osc = &oscs [osc_count]; osc != oscs; ) { osc--; osc->period = period_factor; - osc->delay = 0; + osc->delay = 0; osc->last_amp = 0; osc->phase = 0; } - while ( osc != oscs ); for ( int i = sizeof regs; --i >= 0; ) regs [i] = 0; @@ -117,25 +123,31 @@ void Ay_Apu::reset() write_data_( 13, 0 ); } +int Ay_Apu::read() +{ + static byte const masks [reg_count] = { + 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F, + 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00 + }; + if (!(type_ & 0x10)) return regs [addr_] & masks [addr_]; + else return regs [addr_]; +} + void Ay_Apu::write_data_( int addr, int data ) { assert( (unsigned) addr < reg_count ); if ( (unsigned) addr >= 14 ) - { - #ifdef dprintf - dprintf( "Wrote to I/O port %02X\n", (int) addr ); - #endif - } + dprintf( "Wrote to I/O port %02X\n", (int) addr ); // 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() + env_wave = env_modes [data - 7]; + env_pos = -48; + env_delay = 0; // will get set to envelope period in run_until() } regs [addr] = data; @@ -143,7 +155,7 @@ void Ay_Apu::write_data_( int addr, int data ) int i = addr >> 1; if ( i < osc_count ) { - blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100L * period_factor) + + blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) + regs [i * 2] * period_factor; if ( !period ) period = period_factor; @@ -170,22 +182,23 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) 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; + blip_time_t const old_noise_delay = noise_delay; + unsigned 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; + int env_step_scale = ((type_ & 0xF0) == 0x00) ? 1 : 0; + blip_time_t const env_period_factor = period_factor << env_step_scale; // verified + blip_time_t env_period = (regs [12] * 0x100 + 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; + 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; + int osc_mode = regs [7] >> index; // output Blip_Buffer* const osc_output = osc->output; @@ -195,8 +208,8 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // period int half_vol = 0; - blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() + - inaudible_freq) / (inaudible_freq * 2); + blip_time_t inaudible_period = (unsigned) (osc_output->clock_rate() + + inaudible_freq) / (unsigned) (inaudible_freq * 2); if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) { half_vol = 1; // Actually around 60%, but 50% is close enough @@ -206,16 +219,18 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // 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 ) + int const vol_mode = regs [0x08 + index]; + int const vol_mode_mask = type_ == Ay8914 ? 0x30 : 0x10; + int volume = amp_table [vol_mode & 0x0F] >> half_vol + env_step_scale; + int osc_env_pos = env_pos; + if ( vol_mode & vol_mode_mask ) { - volume = env.wave [osc_env_pos] >> half_vol; + volume = env_wave [osc_env_pos] >> half_vol + env_step_scale; + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); // 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; + end_time = start_time + env_delay; if ( end_time >= final_end_time ) end_time = final_end_time; @@ -237,14 +252,14 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) 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; + int 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; + unsigned noise_lfsr = 1; if ( !(osc_mode & noise_off) ) { ntime = start_time + old_noise_delay; @@ -311,8 +326,8 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) else { // 20 or more noise periods on average for some music - blargg_long remain = end - ntime; - blargg_long count = remain / noise_period; + int remain = end - ntime; + int count = remain / noise_period; if ( remain >= 0 ) ntime += noise_period + count * noise_period; } @@ -327,11 +342,12 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) delta = -delta; synth_.offset( time, delta, osc_output ); time += period; + + // alternate (less-efficient) implementation //phase ^= 1; } - //assert( phase == (delta > 0) ); phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); - // (delta > 0) + check( phase == (delta > 0) ); } else { @@ -358,10 +374,11 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // next envelope step if ( ++osc_env_pos >= 0 ) osc_env_pos -= 32; - volume = env.wave [osc_env_pos] >> half_vol; + volume = env_wave [osc_env_pos] >> half_vol + env_step_scale; + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); start_time = end_time; - end_time += env_period; + end_time += env_period; if ( end_time > final_end_time ) end_time = final_end_time; } @@ -369,27 +386,27 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) if ( !(osc_mode & noise_off) ) { - noise.delay = ntime - final_end_time; - noise.lfsr = noise_lfsr; + noise_delay = ntime - final_end_time; + this->noise_lfsr = noise_lfsr; } } // TODO: optimized saw wave envelope? // maintain envelope phase - blip_time_t remain = final_end_time - last_time - env.delay; + 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; + int 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 ); + env_delay = -remain; + assert( env_delay > 0 ); + assert( env_pos < 0 ); last_time = final_end_time; } diff --git a/Frameworks/GME/gme/Ay_Apu.h b/Frameworks/GME/gme/Ay_Apu.h old mode 100755 new mode 100644 index 31956939e..314772f31 --- a/Frameworks/GME/gme/Ay_Apu.h +++ b/Frameworks/GME/gme/Ay_Apu.h @@ -1,6 +1,6 @@ // AY-3-8910 sound chip emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef AY_APU_H #define AY_APU_H @@ -9,90 +9,106 @@ class Ay_Apu { public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); +// Basics + enum Ay_Apu_Type + { + Ay8910 = 0, + Ay8912, + Ay8913, + Ay8914, + Ym2149 = 0x10, + Ym3439, + Ymz284, + Ymz294, + Ym2203 = 0x20, + Ym2608, + Ym2610, + Ym2610b + }; + + void set_type( Ay_Apu_Type type ) { type_ = type; } + + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); - // Reset sound chip + // Writes to address register + void write_addr( int data ) { addr_ = data & 0x0F; } + + // Emulates to time t, then writes to current data register + void write_data( blip_time_t t, int data ) { run_until( t ); write_data_( addr_, data ); } + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Reads from current data register + int read(); + + // Resets sound chip void reset(); - // Write to register at specified time + // Number of registers 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. + // Same as set_output(), but for a particular channel enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int chan, Blip_Buffer* ); - // Set overall volume (default is 1.0) - void volume( double ); + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth_.volume( 0.7/osc_count/amp_range * v ); } - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } +private: + // noncopyable + Ay_Apu( const Ay_Apu& ); + Ay_Apu& operator = ( const Ay_Apu& ); + +// Implementation public: Ay_Apu(); - typedef unsigned char byte; + BLARGG_DISABLE_NOTHROW + typedef BOOST::uint8_t byte; + private: struct osc_t { - blip_time_t period; - blip_time_t delay; - short last_amp; - short phase; + blip_time_t period; + blip_time_t delay; + short last_amp; + short phase; Blip_Buffer* output; } oscs [osc_count]; + + Ay_Apu_Type type_; + blip_time_t last_time; - byte latch; - byte regs [reg_count]; + byte addr_; + byte regs [reg_count]; - struct { - blip_time_t delay; - blargg_ulong lfsr; - } noise; + blip_time_t noise_delay; + unsigned noise_lfsr; - struct { - blip_time_t delay; - byte const* wave; - int pos; - byte modes [8] [48]; // values already passed through volume table - } env; + blip_time_t env_delay; + byte const* env_wave; + int env_pos; + byte env_modes [8] [48]; // values already passed through volume table - void run_until( blip_time_t ); void write_data_( int addr, int data ); + void run_until( blip_time_t ); + public: enum { amp_range = 255 }; - Blip_Synth synth_; + Blip_Synth_Norm synth_; // used by Ay_Core for beeper sound }; -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 ) +inline void Ay_Apu::set_output( int i, Blip_Buffer* out ) { 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 ); + oscs [i].output = out; } inline void Ay_Apu::end_frame( blip_time_t time ) @@ -100,8 +116,8 @@ inline void Ay_Apu::end_frame( blip_time_t time ) if ( time > last_time ) run_until( time ); - assert( last_time >= time ); last_time -= time; + assert( last_time >= 0 ); } #endif diff --git a/Frameworks/GME/gme/Ay_Cpu.cpp b/Frameworks/GME/gme/Ay_Cpu.cpp old mode 100755 new mode 100644 index 6ff7156ba..40fbff6f6 --- a/Frameworks/GME/gme/Ay_Cpu.cpp +++ b/Frameworks/GME/gme/Ay_Cpu.cpp @@ -1,19 +1,11 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ -/* -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 "Ay_Core.h" #include "blargg_endian.h" -#include - //#include "z80_cpu_log.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 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 @@ -24,1642 +16,44 @@ 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() +void Ay_Core::cpu_out( time_t time, addr_t addr, int data ) { - state = &state_; - for ( int i = 0x100; --i >= 0; ) + if ( (addr & 0xFF) == 0xFE ) { - 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; + check( !cpc_mode ); + spectrum_mode = !cpc_mode; + + // beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output) + if ( (data &= beeper_mask) != last_beeper ) + { + last_beeper = data; + int delta = -beeper_delta; + beeper_delta = delta; + Blip_Buffer* bb = beeper_output; + bb->set_modified(); + apu_.synth_.offset( time, delta, bb ); + } + } + else + { + cpu_out_( time, addr, data ); } - 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 OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) +#define IN_PORT( addr ) cpu_in( addr ) +#define FLAT_MEM mem +#define CPU cpu -#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, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -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; +#define CPU_BEGIN \ +bool Ay_Core::run_cpu( time_t end_time ) \ +{\ + cpu.set_end_time( end_time );\ + byte* const mem = mem_.ram; // cache - typedef BOOST::int8_t int8_t; - - 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 - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 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 ); - - fuint8 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 - }; - - fuint16 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) - fuint16 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 = (BOOST::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: - fuint16 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) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 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 ); - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 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 ) {\ - fuint16 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 ) {\ - fuint16 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 ) {\ - fuint8 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 ) {\ - fuint8 result = read;\ - fuint8 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 ) {\ - fuint8 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 ) {\ - fuint8 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 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 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 - fuint8 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; - fuint16 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; - fuint16 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; - fuint16 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; - - fuint16 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 ); - dprintf( "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(); - dprintf( "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: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 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 - fuint16 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) - fuint16 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) - fuint8 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: - dprintf( "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 - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "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_; + #include "Z80_Cpu_run.h" return warning; } diff --git a/Frameworks/GME/gme/Ay_Emu.cpp b/Frameworks/GME/gme/Ay_Emu.cpp old mode 100755 new mode 100644 index 93582f88f..002346ef5 --- a/Frameworks/GME/gme/Ay_Emu.cpp +++ b/Frameworks/GME/gme/Ay_Emu.cpp @@ -1,404 +1,357 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 = (BOOST::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; } - -gme_type_t_ const gme_ay_type [1] = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; - -// 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; - } - //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); - if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data - dprintf( "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 ); - //dprintf( "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; - } - } - - dprintf( "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 - - dprintf( "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, 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; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Ay_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 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: probably don't need detailed errors as to why file is corrupt + +int const spectrum_clock = 3546900; // 128K Spectrum +int const spectrum_period = 70908; + +//int const spectrum_clock = 3500000; // 48K Spectrum +//int const spectrum_period = 69888; + +int const cpc_clock = 2000000; + +Ay_Emu::Ay_Emu() +{ + core.set_cpc_callback( enable_cpc_, this ); + set_type( gme_ay_type ); + set_silence_lookahead( 6 ); +} + +Ay_Emu::~Ay_Emu() { } + +// Track info + +// Given pointer to 2-byte offset of data, returns pointer to data, or NULL if +// offset is 0 or there is less than min_size bytes of data available. +static byte const* get_data( Ay_Emu::file_t const& file, byte const ptr [], int min_size ) +{ + int offset = (BOOST::int16_t) get_be16( ptr ); + int pos = ptr - (byte const*) file.header; + int size = file.end - (byte const*) file.header; + assert( (unsigned) pos <= (unsigned) size - 2 ); + int limit = size - min_size; + if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit ) + return NULL; + return ptr + offset; +} + +static blargg_err_t parse_header( byte const in [], int size, Ay_Emu::file_t* out ) +{ + typedef Ay_Emu::header_t header_t; + if ( size < header_t::size ) + return blargg_err_file_type; + + out->header = (header_t const*) in; + out->end = in + size; + header_t const& h = *(header_t const*) in; + if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) + return blargg_err_file_type; + + out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); + if ( !out->tracks ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "missing track data" ); + + return blargg_ok; +} + +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 ) * (1000 / 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 ) ); +} + +static void hash_ay_file( Ay_Emu::file_t const& file, Gme_Info_::Hash_Function& out ) +{ + out.hash_( &file.header->vers, sizeof(file.header->vers) ); + out.hash_( &file.header->player, sizeof(file.header->player) ); + out.hash_( &file.header->unused[0], sizeof(file.header->unused) ); + out.hash_( &file.header->max_track, sizeof(file.header->max_track) ); + out.hash_( &file.header->first_track, sizeof(file.header->first_track) ); + + for ( unsigned i = 0; i <= file.header->max_track; i++ ) + { + byte const* track_info = get_data( file, file.tracks + i * 4 + 2, 14 ); + if ( track_info ) + { + out.hash_( track_info + 8, 2 ); + byte const* points = get_data( file, track_info + 10, 6 ); + if ( points ) out.hash_( points, 6 ); + + byte const* blocks = get_data( file, track_info + 12, 8 ); + if ( blocks ) + { + int addr = get_be16( blocks ); + + while ( addr ) + { + out.hash_( blocks, 4 ); + + int len = get_be16( blocks + 2 ); + + byte const* block = get_data( file, blocks + 4, len ); + if ( block ) out.hash_( block, len ); + + blocks += 6; + addr = get_be16( blocks ); + } + } + } + } +} + +blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_ay_fields( file, out, track ); + return blargg_ok; +} + +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 [], int size ) + { + RETURN_ERR( parse_header( begin, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + copy_ay_fields( file, out, track ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_ay_file( file, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_ay_emu () +{ + return BLARGG_NEW Ay_Emu; +} + +static Music_Emu* new_ay_file() +{ + return BLARGG_NEW Ay_File; +} + +gme_type_t_ const gme_ay_type [1] = {{ + "ZX Spectrum", + 0, + &new_ay_emu, + &new_ay_file, + "AY", + 1 +}}; + +// Setup + +blargg_err_t Ay_Emu::load_mem_( byte const in [], int size ) +{ + assert( offsetof (header_t,track_info [2]) == header_t::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" ); + + int const osc_count = Ay_Apu::osc_count + 1; // +1 for beeper + + set_voice_count( osc_count ); + core.apu().volume( gain() ); + + 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+1 + }; + set_voice_types( types ); + + return setup_buffer( spectrum_clock ); +} + +void Ay_Emu::update_eq( blip_eq_t const& eq ) +{ + core.apu().treble_eq( eq ); +} + +void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i >= Ay_Apu::osc_count ) + core.set_beeper_output( center ); + else + core.apu().set_output( i, center ); +} + +void Ay_Emu::set_tempo_( double t ) +{ + int p = spectrum_period; + if ( clock_rate() != spectrum_clock ) + p = clock_rate() / 50; + + core.set_play_period( blip_time_t (p / t) ); +} + +blargg_err_t Ay_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + byte* const mem = core.mem(); + + memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET + memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 ); + memset( mem + core.ram_addr, 0x00, core.mem_size - core.ram_addr ); + + // locate data blocks + byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); + if ( !data ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + byte const* const more_data = get_data( file, data + 10, 6 ); + if ( !more_data ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + byte const* blocks = get_data( file, data + 12, 8 ); + if ( !blocks ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + // initial addresses + unsigned addr = get_be16( blocks ); + if ( !addr ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "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 > core.mem_size ) + { + set_warning( "Bad data block size" ); + len = core.mem_size - addr; + } + check( len ); + byte const* in = get_data( file, blocks, 0 ); blocks += 2; + if ( len > (unsigned) (file.end - in) ) + { + set_warning( "File data missing" ); + len = file.end - in; + } + //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); + if ( addr < core.ram_addr && addr >= 0x400 ) // several tracks use low data + dprintf( "Block addr in ROM\n" ); + memcpy( mem + addr, in, len ); + + if ( file.end - blocks < 8 ) + { + set_warning( "File data missing" ); + 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, passive, sizeof passive ); + int const play_addr = get_be16( more_data + 4 ); + if ( play_addr ) + { + memcpy( mem, active, sizeof active ); + mem [ 9] = play_addr; + mem [10] = play_addr >> 8; + } + mem [2] = init; + mem [3] = init >> 8; + + mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) + + // start at spectrum speed + change_clock_rate( spectrum_clock ); + set_tempo( tempo() ); + + Ay_Core::registers_t r = { }; + 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; + + core.start_track( r, play_addr ); + + return blargg_ok; +} + +blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) +{ + core.end_frame( &duration ); + return blargg_ok; +} + +inline void Ay_Emu::enable_cpc() +{ + change_clock_rate( cpc_clock ); + set_tempo( tempo() ); +} + +void Ay_Emu::enable_cpc_( void* data ) +{ + STATIC_CAST(Ay_Emu*,data)->enable_cpc(); +} + +blargg_err_t Ay_Emu::hash_( Hash_Function& out ) const +{ + hash_ay_file( file, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Ay_Emu.h b/Frameworks/GME/gme/Ay_Emu.h old mode 100755 new mode 100644 index ba8445d31..7da69a57e --- a/Frameworks/GME/gme/Ay_Emu.h +++ b/Frameworks/GME/gme/Ay_Emu.h @@ -1,70 +1,60 @@ // Sinclair Spectrum AY music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef AY_EMU_H #define AY_EMU_H #include "Classic_Emu.h" -#include "Ay_Apu.h" -#include "Ay_Cpu.h" +#include "Ay_Core.h" -class Ay_Emu : private Ay_Cpu, public Classic_Emu { - typedef Ay_Cpu cpu; +class Ay_Emu : public Classic_Emu { public: // AY file header - enum { header_size = 0x14 }; struct header_t { - byte tag [8]; + enum { size = 0x14 }; + + byte tag [8]; byte vers; byte player; - byte unused [2]; - byte author [2]; - byte comment [2]; + 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; } + +// Implementation public: Ay_Emu(); ~Ay_Emu(); + struct file_t { header_t const* header; - byte const* end; byte const* tracks; + byte const* end; // end of file data }; + + blargg_err_t hash_( Hash_Function& out ) const; + 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& ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + private: file_t file; + Ay_Core core; - unsigned play_addr; - 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 ); + void enable_cpc(); + static void enable_cpc_( void* data ); }; #endif diff --git a/Frameworks/GME/gme/Blip_Buffer.cpp b/Frameworks/GME/gme/Blip_Buffer.cpp old mode 100755 new mode 100644 index 6304cedb4..41cebfcea --- a/Frameworks/GME/gme/Blip_Buffer.cpp +++ b/Frameworks/GME/gme/Blip_Buffer.cpp @@ -1,446 +1,509 @@ -// 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_ = LONG_MAX; - 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 = (ULONG_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 c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); - double cos_nc_angle = cos( maxh * cutoff * angle ); - double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); - double cos_angle = cos( angle ); - - c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; - double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); - double b = 2.0 - cos_angle - cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d - } -} - -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; -} - +// Blip_Buffer $vers. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include + +/* Copyright (C) 2003-2008 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" + +//// Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = UINT_MAX/2 + 1; + buffer_ = NULL; + buffer_center_ = NULL; + buffer_size_ = 0; + sample_rate_ = 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 + int i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting truncates and sign-extends + i = 0x18000; + assert( (BOOST::int16_t) i == -0x8000 ); + #endif + + clear(); +} + +Blip_Buffer::~Blip_Buffer() +{ + free( buffer_ ); +} + +void Blip_Buffer::clear() +{ + bool const entire_buffer = true; + + offset_ = 0; + reader_accum_ = 0; + modified_ = false; + + if ( buffer_ ) + { + int count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) ); + } +} + +blargg_err_t Blip_Buffer::set_sample_rate( int new_rate, int msec ) +{ + // Limit to maximum size that resampled time can represent + int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) - + blip_buffer_extra_ - 64; // TODO: -64 isn't needed + int new_size = (new_rate * (msec + 1) + 999) / 1000; + if ( new_size > max_size ) + new_size = max_size; + + // Resize buffer + if ( buffer_size_ != new_size ) + { + //dprintf( "%d \n", (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + CHECK_ALLOC( p ); + buffer_ = (delta_t*) p; + buffer_center_ = buffer_ + BLIP_MAX_QUALITY/2; + buffer_size_ = new_size; + } + + // Update sample_rate and things that depend on it + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return blargg_ok; +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( int rate ) const +{ + double ratio = (double) sample_rate_ / rate; + int factor = (int) floor( ratio * (1 << 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 && sample_rate_ ) + { + shift = 13; + int f = (freq << 16) / sample_rate_; + while ( (f >>= 1) != 0 && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (int) buffer_size_ ); // fails if time is past end of buffer +} + +int Blip_Buffer::count_samples( blip_time_t t ) const +{ + blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (int) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( int count ) const +{ + 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( int count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + int remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +int Blip_Buffer::read_samples( blip_sample_t out_ [], int max_samples, bool stereo ) +{ + int count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = highpass_shift(); + delta_t const* reader = read_pos() + count; + int reader_sum = integrator(); + + blip_sample_t* BLARGG_RESTRICT out = out_ + count; + if ( stereo ) + out += count; + int offset = -count; + + if ( !stereo ) + { + do + { + int s = reader_sum >> delta_bits; + + reader_sum -= reader_sum >> bass; + reader_sum += reader [offset]; + + BLIP_CLAMP( s, s ); + out [offset] = (blip_sample_t) s; + } + while ( ++offset ); + } + else + { + do + { + int s = reader_sum >> delta_bits; + + reader_sum -= reader_sum >> bass; + reader_sum += reader [offset]; + + BLIP_CLAMP( s, s ); + out [offset * 2] = (blip_sample_t) s; + } + while ( ++offset ); + } + + set_integrator( reader_sum ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const in [], int count ) +{ + delta_t* out = buffer_center_ + (offset_ >> BLIP_BUFFER_ACCURACY); + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( --count >= 0 ) + { + int s = *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + +void Blip_Buffer::save_state( blip_buffer_state_t* out ) +{ + assert( samples_avail() == 0 ); + out->offset_ = offset_; + out->reader_accum_ = reader_accum_; + memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf ); +} + +void Blip_Buffer::load_state( blip_buffer_state_t const& in ) +{ + clear(); + + offset_ = in.offset_; + reader_accum_ = in.reader_accum_; + memcpy( buffer_, in.buf, sizeof in.buf ); +} + + +//// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = NULL; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1 << blip_sample_bits) + 0.5); +} + +#if BLIP_BUFFER_FAST + +void blip_eq_t::generate( float* out, int count ) const { } + +#else + +Blip_Synth_::Blip_Synth_( short p [], int w ) : + phases( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = NULL; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +// Generates right half of sinc kernel (including center point) with cutoff at +// sample rate / 2 / oversample. Frequency response at cutoff frequency is +// treble dB (-6=0.5,-12=0.25). Mid controls frequency that rolloff begins at, +// cut * sample rate / 2. +static void gen_sinc( float out [], int out_size, double oversample, + double treble, double mid ) +{ + if ( mid > 0.9999 ) mid = 0.9999; + if ( treble < -300.0 ) treble = -300.0; + if ( treble > 5.0 ) treble = 5.0; + + double const maxh = 4096.0; + double rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - mid) ); + double const pow_a_n = pow( rolloff, maxh - maxh * mid ); + double const to_angle = PI / maxh / oversample; + for ( int i = 1; i < out_size; i++ ) + { + double angle = i * to_angle; + double c = rolloff * cos( angle * maxh - angle ) - + cos( angle * maxh ); + double cos_nc_angle = cos( angle * maxh * mid ); + double cos_nc1_angle = cos( angle * maxh * mid - angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } + + // Approximate center by looking at two points to right. Much simpler + // and more reliable than trying to calculate it properly. + out [0] = out [1] + 0.5 * (out [1] - out [2]); +} + +// Gain is 1-2800 for beta of 0-10, instead of 1.0 as it should be, but +// this is corrected by normalization in treble_eq(). +static void kaiser_window( float io [], int count, float beta ) +{ + int const accuracy = 10; + + float const beta2 = beta * beta; + float const step = (float) 0.5 / count; + float pos = (float) 0.5; + for ( float* const end = io + count; io < end; ++io ) + { + float x = (pos - pos*pos) * beta2; + float u = x; + float k = 1; + float n = 2; + + // Keep refining until adjustment becomes small + do + { + u *= x / (n * n); + n += 1; + k += u; + } + while ( k <= u * (1 << accuracy) ); + + pos += step; + *io *= k; + } +} + +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 cutoff_adj = blip_res * 2.25 / count + 0.85; + if ( cutoff_adj < 1.02 ) + cutoff_adj = 1.02; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + cutoff_adj = half_rate / cutoff_freq; + double cutoff = rolloff_freq * cutoff_adj / half_rate; + + gen_sinc( out, count, oversample * cutoff_adj, treble, cutoff ); + + kaiser_window( out, count, kaiser ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + // Generate right half of kernel + int const half_size = blip_eq_t::calc_count( width ); + float fimpulse [blip_res / 2 * (BLIP_MAX_QUALITY - 1) + 1]; + eq.generate( fimpulse, half_size ); + + int i; + + // Find rescale factor. Summing from small to large (right to left) + // reduces error. + double total = 0.0; + for ( i = half_size; --i > 0; ) + total += fimpulse [i]; + total = total * 2.0 + fimpulse [0]; + + //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 / total; + kernel_unit = (int) base_unit; + + // Integrate, first difference, rescale, convert to int + double sum = 0; + double next = 0; + int const size = impulses_size(); + for ( i = 0; i < size; i++ ) + { + int j = (half_size - 1) - i; + + if ( i >= blip_res ) + sum += fimpulse [j + blip_res]; + + // goes slightly past center, so it needs a little mirroring + next += fimpulse [j < 0 ? -j : j]; + + // calculate unintereleved index + int x = (~i & (blip_res - 1)) * (width >> 1) + (i >> BLIP_PHASE_BITS); + assert( (unsigned) x < (unsigned) size ); + + // flooring separately virtually eliminates error + phases [x] = (short) (int) + (floor( sum * rescale + 0.5 ) - floor( next * rescale + 0.5 )); + //phases [x] = (short) (int) + // floor( sum * rescale - next * rescale + 0.5 ); + } + + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::adjust_impulse() +{ + int const size = impulses_size(); + int const half_width = width / 2; + + // Sum each phase as would be done when synthesizing, and correct + // any that don't add up to exactly kernel_half. + for ( int phase = blip_res / 2; --phase >= 0; ) + { + int const fwd = phase * half_width; + int const rev = size - half_width - fwd; + + int error = kernel_unit; + for ( int i = half_width; --i >= 0; ) + { + error += phases [fwd + i]; + error += phases [rev + i]; + } + phases [fwd + half_width - 1] -= (short) error; + + // Error shouldn't occur now with improved calculation + //if ( error ) printf( "error: %ld\n", error ); + } + + #if 0 + for ( int i = 0; i < blip_res; i++, printf( "\n" ) ) + for ( int j = 0; j < width / 2; j++ ) + printf( "%5d,", (int) -phases [j + width/2 * i] ); + #endif +} + +void Blip_Synth_::rescale_kernel( int shift ) +{ + // Keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values. + int const keep_positive = 0x8000 + (1 << (shift - 1)); + + int const half_width = width / 2; + for ( int phase = blip_res; --phase >= 0; ) + { + int const fwd = phase * half_width; + + // Integrate, rescale, then differentiate again. + // If differences are rescaled directly, more error results. + int sum = keep_positive; + for ( int i = 0; i < half_width; i++ ) + { + int prev = sum; + sum += phases [fwd + i]; + phases [fwd + i] = (sum >> shift) - (prev >> shift); + } + } + + adjust_impulse(); +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( volume_unit_ != new_unit ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + // Factor that kernel must be multiplied by + volume_unit_ = new_unit; + double factor = new_unit * (1 << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + // If factor is low, reduce amplitude of kernel itself + int shift = 0; + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + rescale_kernel( shift ); + } + } + + delta_factor = -(int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif diff --git a/Frameworks/GME/gme/Blip_Buffer.h b/Frameworks/GME/gme/Blip_Buffer.h old mode 100755 new mode 100644 index 9467584f4..503fc9114 --- a/Frameworks/GME/gme/Blip_Buffer.h +++ b/Frameworks/GME/gme/Blip_Buffer.h @@ -1,489 +1,198 @@ -// Band-limited sound synthesis buffer - -// Blip_Buffer 0.4.1 -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - - // internal - #include - #if INT_MAX >= 0x7FFFFFFF - typedef int blip_long; - typedef unsigned blip_ulong; - #else - typedef long blip_long; - typedef unsigned long blip_ulong; - #endif - -// 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(); - - // 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 +// Band-limited sound synthesis buffer + +// Blip_Buffer $vers +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer_impl.h" + +typedef int blip_time_t; // Source clocks in current time frame +typedef BOOST::int16_t blip_sample_t; // 16-bit signed output sample +int const blip_default_length = 1000 / 4; // Default Blip_Buffer length (1/4 second) + + +//// Sample buffer for band-limited synthesis + +class Blip_Buffer : public Blip_Buffer_ { +public: + + // Sets output sample rate and resizes and clears sample buffer + blargg_err_t set_sample_rate( int samples_per_sec, int msec_length = blip_default_length ); + + // Sets number of source time units per second + void clock_rate( int clocks_per_sec ); + + // Clears buffer and removes all samples + void clear(); + + // Use Blip_Synth to add waveform to buffer + + // Resamples to time t, then subtracts t from current time. Appends result of resampling + // to buffer for reading. + void end_frame( blip_time_t t ); + + // Number of samples available for reading with read_samples() + int samples_avail() const; + + // Reads at most n samples to out [0 to n-1] and returns number actually read. If stereo + // is true, writes to out [0], out [2], out [4] etc. instead. + int read_samples( blip_sample_t out [], int n, bool stereo = false ); + +// More features + + // Sets flag that tells some Multi_Buffer types that sound was added to buffer, + // so they know that it needs to be mixed in. Only needs to be called once + // per time frame that sound was added. Not needed if not using Multi_Buffer. + void set_modified() { modified_ = true; } + + // Sets high-pass filter frequency, from 0 to 20000 Hz, where higher values reduce bass more + void bass_freq( int frequency ); + + int length() const; // Length of buffer in milliseconds + int sample_rate() const; // Current output sample rate + int clock_rate() const; // Number of source time units per second + int output_latency() const; // Number of samples delay from offset() to read_samples() + +// Low-level features + + // Removes the first n samples + void remove_samples( int n ); + + // Returns number of clocks needed until n samples will be available. + // If buffer cannot even hold n samples, returns number of clocks + // until buffer becomes full. + blip_time_t count_clocks( int n ) const; + + // Number of samples that should be mixed before calling end_frame( t ) + int count_samples( blip_time_t t ) const; + + // Mixes n samples into buffer + void mix_samples( const blip_sample_t in [], int n ); + +// Resampled time (sorry, poor documentation right now) + + // Resampled time is fixed-point, in terms of output samples. + + // Converts clock count to resampled time + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + + // Converts clock time since beginning of current time frame to resampled time + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + + // Returns factor that converts clock rate to resampled time + blip_resampled_time_t clock_rate_factor( int clock_rate ) const; + +// State save/load + + // Saves state, including high-pass filter and tails of last deltas. + // All samples must have been read from buffer before calling this + // (that is, samples_avail() must return 0). + void save_state( blip_buffer_state_t* out ); + + // Loads state. State must have been saved from Blip_Buffer with same + // settings during same run of program; states can NOT be stored on disk. + // Clears buffer before loading state. + void load_state( const blip_buffer_state_t& in ); + +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + Blip_Buffer(); + ~Blip_Buffer(); + void remove_silence( int n ); +}; + + +//// Adds amplitude changes to Blip_Buffer + +template class Blip_Synth; + +typedef Blip_Synth<8, 1> Blip_Synth_Fast; // faster, but less equalizer control +typedef Blip_Synth<12,1> Blip_Synth_Norm; // good for most things +typedef Blip_Synth<16,1> Blip_Synth_Good; // sharper filter cutoff + +template +class Blip_Synth { +public: + + // Sets volume of amplitude delta unit + void volume( double v ) { impl.volume_unit( 1.0 / range * v ); } + + // Configures low-pass filter + void treble_eq( const blip_eq_t& eq ) { impl.treble_eq( eq ); } + + // Gets/sets default Blip_Buffer + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Extends waveform to time t at current amplitude, then changes its amplitude to a + // Using this requires a separate Blip_Synth for each waveform. + void update( blip_time_t t, int a ); + +// Low-level interface + + // If no Blip_Buffer* is specified, uses one set by output() above + + // Adds amplitude transition at time t. Delta can be positive or negative. + // The actual change in amplitude is delta * volume. + void offset( blip_time_t t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // 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( buf->to_fixed( t ), delta, buf ); } + void offset_inline( blip_time_t t, int delta ) const { offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Use resampled time functions in Blip_Buffer + // to convert clock counts to resampled time. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; + typedef char coeff_t; +#else + Blip_Synth_ impl; + typedef short coeff_t; + // Left halves of first difference of step response for each possible phase + coeff_t phases [quality / 2 * blip_res]; +public: + Blip_Synth() : impl( phases, quality ) { } +#endif +}; + + +//// Low-pass equalization parameters + +class blip_eq_t { + double treble, kaiser; + int rolloff_freq, sample_rate, cutoff_freq; +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, int rolloff_freq, int sample_rate, int cutoff_freq = 0, + double kaiser = 5.2 ); + + // Generate center point and right half of impulse response + virtual void generate( float out [], int count ) const; + virtual ~blip_eq_t() { } + + enum { oversample = blip_res }; + static int calc_count( int quality ) { return (quality - 1) * (oversample / 2) + 1; } +}; + +#include "Blip_Buffer_impl2.h" + +#endif diff --git a/Frameworks/GME/gme/Classic_Emu.cpp b/Frameworks/GME/gme/Classic_Emu.cpp old mode 100755 new mode 100644 index 063444fe2..f02d79b2a --- a/Frameworks/GME/gme/Classic_Emu.cpp +++ b/Frameworks/GME/gme/Classic_Emu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Classic_Emu.h" #include "Multi_Buffer.h" -#include -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -20,9 +19,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ Classic_Emu::Classic_Emu() { - buf = 0; - stereo_buffer = 0; - voice_types = 0; + buf = NULL; + stereo_buffer = NULL; + voice_types = NULL; // avoid inconsistency in our duplicated constants assert( (int) wave_type == (int) Multi_Buffer::wave_type ); @@ -33,6 +32,8 @@ Classic_Emu::Classic_Emu() Classic_Emu::~Classic_Emu() { delete stereo_buffer; + delete effects_buffer_; + effects_buffer_ = NULL; } void Classic_Emu::set_equalizer_( equalizer_t const& eq ) @@ -40,10 +41,10 @@ void Classic_Emu::set_equalizer_( equalizer_t const& eq ) Music_Emu::set_equalizer_( eq ); update_eq( eq.treble ); if ( buf ) - buf->bass_freq( equalizer().bass ); + buf->bass_freq( (int) equalizer().bass ); } -blargg_err_t Classic_Emu::set_sample_rate_( long rate ) +blargg_err_t Classic_Emu::set_sample_rate_( int rate ) { if ( !buf ) { @@ -61,11 +62,11 @@ void Classic_Emu::mute_voices_( int mask ) { if ( mask & (1 << i) ) { - set_voice( i, 0, 0, 0 ); + set_voice( i, NULL, NULL, NULL ); } else { - Multi_Buffer::channel_t ch = buf->channel( i, (voice_types ? voice_types [i] : 0) ); + Multi_Buffer::channel_t ch = buf->channel( i ); 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 ); @@ -73,33 +74,35 @@ void Classic_Emu::mute_voices_( int mask ) } } -void Classic_Emu::change_clock_rate( long rate ) +void Classic_Emu::change_clock_rate( int rate ) { clock_rate_ = rate; buf->clock_rate( rate ); } -blargg_err_t Classic_Emu::setup_buffer( long rate ) +blargg_err_t Classic_Emu::setup_buffer( int rate ) { change_clock_rate( rate ); - RETURN_ERR( buf->set_channel_count( voice_count() ) ); + RETURN_ERR( buf->set_channel_count( voice_count(), voice_types ) ); set_equalizer( equalizer() ); buf_changed_count = buf->channels_changed_count(); - return 0; + return blargg_ok; } blargg_err_t Classic_Emu::start_track_( int track ) { RETURN_ERR( Music_Emu::start_track_( track ) ); buf->clear(); - return 0; + return blargg_ok; } -blargg_err_t Classic_Emu::play_( long count, sample_t* out ) +blargg_err_t Classic_Emu::play_( int count, sample_t out [] ) { - long remain = count; + // read from buffer, then refill buffer and repeat if necessary + int remain = count; while ( remain ) { + buf->disable_immediate_removal(); remain -= buf->read_samples( &out [count - remain], remain ); if ( remain ) { @@ -108,77 +111,14 @@ blargg_err_t Classic_Emu::play_( long count, sample_t* out ) buf_changed_count = buf->channels_changed_count(); remute_voices(); } + + // TODO: use more accurate length calculation int msec = buf->length(); - blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; + blip_time_t clocks_emulated = msec * clock_rate_ / 1000 - 100; 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 ) - { - dprintf( "addr: %X\n", addr ); - dprintf( "file_size: %d\n", file_size_ ); - dprintf( "rounded: %d\n", rounded ); - dprintf( "mask: $%X\n", mask ); - } + return blargg_ok; } diff --git a/Frameworks/GME/gme/Classic_Emu.h b/Frameworks/GME/gme/Classic_Emu.h old mode 100755 new mode 100644 index 8cd822ca2..4495400f1 --- a/Frameworks/GME/gme/Classic_Emu.h +++ b/Frameworks/GME/gme/Classic_Emu.h @@ -1,6 +1,6 @@ // Common aspects of emulators which use Blip_Buffer for sound output -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef CLASSIC_EMU_H #define CLASSIC_EMU_H @@ -9,33 +9,57 @@ #include "Music_Emu.h" class Classic_Emu : public Music_Emu { +protected: +// Derived interface + + // Advertises type of sound on each voice, so Effects_Buffer can better choose + // what effect to apply (pan, echo, surround). Constant can have value added so + // that voices of the same type can be spread around the stereo sound space. + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + void set_voice_types( int const types [] ) { voice_types = types; } + + // Sets up Blip_Buffers after loading file + blargg_err_t setup_buffer( int clock_rate ); + + // Clock rate of Blip_buffers + int clock_rate() const { return clock_rate_; } + + // Changes clock rate of Blip_Buffers (experimental) + void change_clock_rate( int ); + +// Overrides should do the indicated task + + // Set Blip_Buffer(s) voice outputs to, or mute voice if pointer is NULL + virtual void set_voice( int index, Blip_Buffer* center, + Blip_Buffer* left, Blip_Buffer* right ) BLARGG_PURE( ; ) + + // Update equalization + virtual void update_eq( blip_eq_t const& ) BLARGG_PURE( ; ) + + // Start track + virtual blargg_err_t start_track_( int track ) BLARGG_PURE( ; ) + + // Run for at most msec or time_io clocks, then set time_io to number of clocks + // actually run for. After returning, Blip_Buffers have time frame of time_io clocks + // ended. + virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) BLARGG_PURE( ; ) + +// Internal public: Classic_Emu(); ~Classic_Emu(); - void set_buffer( Multi_Buffer* ); + virtual void set_buffer( Multi_Buffer* ); + 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* ); + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual void mute_voices_( int ); + virtual void set_equalizer_( equalizer_t const& ); + virtual blargg_err_t play_( int, sample_t [] ); + private: Multi_Buffer* buf; Multi_Buffer* stereo_buffer; // NULL if using custom buffer - long clock_rate_; + int clock_rate_; unsigned buf_changed_count; int const* voice_types; }; @@ -46,82 +70,10 @@ inline void Classic_Emu::set_buffer( Multi_Buffer* 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). +inline void Classic_Emu::set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ) { } -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 ); -}; +inline void Classic_Emu::update_eq( blip_eq_t const& ) { } -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 +inline blargg_err_t Classic_Emu::run_clocks( blip_time_t&, int ) { return blargg_ok; } #endif diff --git a/Frameworks/GME/gme/Dual_Resampler.cpp b/Frameworks/GME/gme/Dual_Resampler.cpp old mode 100755 new mode 100644 index 8644517ca..e19dfff87 --- a/Frameworks/GME/gme/Dual_Resampler.cpp +++ b/Frameworks/GME/gme/Dual_Resampler.cpp @@ -1,131 +1,317 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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() { } - -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 ); - - long count = resampler.read( sample_buf.begin(), sample_buf_size ); - assert( count == (long) sample_buf_size ); - - 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 ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - sn.next( bass ); - blargg_long r = (blargg_long) in [1] * 2 + s; - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - in += 2; - out [0] = l; - out [1] = r; - out += 2; - } - - sn.end( blip_buf ); -} - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Dual_Resampler.h" + +/* Copyright (C) 2003-2008 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: fix this. hack since resampler holds back some output. +int const resampler_extra = 34; + +int const stereo = 2; + +Dual_Resampler::Dual_Resampler() { } + +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_ERR( resampler.resize_buffer( resampler_size ) ); + resampler.clear(); + return blargg_ok; +} + +void Dual_Resampler::resize( int pairs ) +{ + int new_sample_buf_size = pairs * 2; + //new_sample_buf_size = new_sample_buf_size / 4 * 4; // TODO: needed only for 3:2 downsampler + 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.rate()) * 2 + 2; + clear(); + } +} + +void Dual_Resampler::clear() +{ + buf_pos = buffered = 0; + resampler.clear(); +} + + +int Dual_Resampler::play_frame_( Stereo_Buffer& stereo_buf, dsample_t out [], Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + int pair_count = sample_buf_size >> 1; + blip_time_t blip_time = stereo_buf.center()->count_clocks( pair_count ); + int sample_count = oversamples_per_frame - resampler.written() + resampler_extra; + + int new_count = set_callback.f( set_callback.data, blip_time, sample_count, resampler.buffer() ); + assert( new_count < resampler_size ); + + stereo_buf.end_frame( blip_time ); + assert( stereo_buf.samples_avail() == pair_count * 2 ); + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + blip_time_t blip_time_2 = second_buf->center()->count_clocks( pair_count ); + second_buf->end_frame( blip_time_2 ); + assert( second_buf->samples_avail() == pair_count * 2 ); + } + } + + resampler.write( new_count ); + + int count = resampler.read( sample_buf.begin(), sample_buf_size ); + + mix_samples( stereo_buf, out, count, secondary_buf_set, secondary_buf_set_count ); + + pair_count = count >> 1; + stereo_buf.left()->remove_samples( pair_count ); + stereo_buf.right()->remove_samples( pair_count ); + stereo_buf.center()->remove_samples( pair_count ); + + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + second_buf->left()->remove_samples( pair_count ); + second_buf->right()->remove_samples( pair_count ); + second_buf->center()->remove_samples( pair_count ); + } + } + + return count; +} + +void Dual_Resampler::dual_play( int count, dsample_t out [], Stereo_Buffer& stereo_buf, Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + // empty extra buffer + int remain = buffered - 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 >= sample_buf_size ) + { + buf_pos = buffered = play_frame_( stereo_buf, out, secondary_buf_set, secondary_buf_set_count ); + out += buffered; + count -= buffered; + } + + while (count > 0) + { + buffered = play_frame_( stereo_buf, sample_buf.begin(), secondary_buf_set, secondary_buf_set_count ); + if ( buffered >= count ) + { + buf_pos = count; + memcpy( out, sample_buf.begin(), count * sizeof *out ); + out += count; + count = 0; + } + else + { + memcpy( out, sample_buf.begin(), buffered * sizeof *out ); + out += buffered; + count -= buffered; + } + } +} + +void Dual_Resampler::mix_samples( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count, Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + // lol hax + if ( ((Tracked_Blip_Buffer*)stereo_buf.left())->non_silent() | ((Tracked_Blip_Buffer*)stereo_buf.right())->non_silent() ) + mix_stereo( stereo_buf, out_, count ); + else + mix_mono( stereo_buf, out_, count ); + + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + if ( ((Tracked_Blip_Buffer*)second_buf->left())->non_silent() | ((Tracked_Blip_Buffer*)second_buf->right())->non_silent() ) + mix_extra_stereo( *second_buf, out_, count ); + else + mix_extra_mono( *second_buf, out_, count ); + } + } +} + +void Dual_Resampler::mix_mono( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( sn, *stereo_buf.center() ); + + count >>= 1; + BLIP_READER_ADJ_( sn, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + stereo_dsample_t const* BLARGG_RESTRICT in = + (stereo_dsample_t const*) sample_buf.begin() + count; + int offset = -count; + int const gain = gain_; + do + { + int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( sn, bass, offset ); + + int l = (in [offset] [0] * gain >> gain_bits) + s; + int r = (in [offset] [1] * gain >> gain_bits) + s; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( sn, *stereo_buf.center() ); +} + +void Dual_Resampler::mix_stereo( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( snc, *stereo_buf.center() ); + BLIP_READER_BEGIN( snl, *stereo_buf.left() ); + BLIP_READER_BEGIN( snr, *stereo_buf.right() ); + + count >>= 1; + BLIP_READER_ADJ_( snc, count ); + BLIP_READER_ADJ_( snl, count ); + BLIP_READER_ADJ_( snr, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + stereo_dsample_t const* BLARGG_RESTRICT in = + (stereo_dsample_t const*) sample_buf.begin() + count; + int offset = -count; + int const gain = gain_; + do + { + int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16); + int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16); + int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( snc, bass, offset ); + BLIP_READER_NEXT_IDX_( snl, bass, offset ); + BLIP_READER_NEXT_IDX_( snr, bass, offset ); + + int l = (in [offset] [0] * gain >> gain_bits) + sl + sc; + int r = (in [offset] [1] * gain >> gain_bits) + sr + sc; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( snc, *stereo_buf.center() ); + BLIP_READER_END( snl, *stereo_buf.left() ); + BLIP_READER_END( snr, *stereo_buf.right() ); +} + +void Dual_Resampler::mix_extra_mono( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( sn, *stereo_buf.center() ); + + count >>= 1; + BLIP_READER_ADJ_( sn, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + int offset = -count; + int const gain = gain_; + do + { + int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( sn, bass, offset ); + + int l = out [offset] [0] + s; + int r = out [offset] [1] + s; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( sn, *stereo_buf.center() ); +} + +void Dual_Resampler::mix_extra_stereo( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( snc, *stereo_buf.center() ); + BLIP_READER_BEGIN( snl, *stereo_buf.left() ); + BLIP_READER_BEGIN( snr, *stereo_buf.right() ); + + count >>= 1; + BLIP_READER_ADJ_( snc, count ); + BLIP_READER_ADJ_( snl, count ); + BLIP_READER_ADJ_( snr, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + int offset = -count; + int const gain = gain_; + do + { + int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16); + int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16); + int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( snc, bass, offset ); + BLIP_READER_NEXT_IDX_( snl, bass, offset ); + BLIP_READER_NEXT_IDX_( snr, bass, offset ); + + int l = out [offset] [0] + sl + sc; + int r = out [offset] [1] + sr + sc; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( snc, *stereo_buf.center() ); + BLIP_READER_END( snl, *stereo_buf.left() ); + BLIP_READER_END( snr, *stereo_buf.right() ); +} diff --git a/Frameworks/GME/gme/Dual_Resampler.h b/Frameworks/GME/gme/Dual_Resampler.h old mode 100755 new mode 100644 index 61beb8a08..2bdfb4afa --- a/Frameworks/GME/gme/Dual_Resampler.h +++ b/Frameworks/GME/gme/Dual_Resampler.h @@ -1,50 +1,61 @@ -// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. +// Combination of Fir_Resampler and Stereo_Buffer mixing. Used by Sega FM emulators. -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef DUAL_RESAMPLER_H #define DUAL_RESAMPLER_H -#include "Fir_Resampler.h" -#include "Blip_Buffer.h" +#include "Multi_Buffer.h" + +#if GME_VGM_FAST_RESAMPLER + #include "Downsampler.h" + typedef Downsampler Dual_Resampler_Downsampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler_Norm Dual_Resampler_Downsampler; +#endif class Dual_Resampler { public: - Dual_Resampler(); - virtual ~Dual_Resampler(); - typedef short dsample_t; - double setup( double oversample, double rolloff, double gain ); + blargg_err_t setup( double oversample, double rolloff, double gain ); + double rate() const { return resampler.rate(); } 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& ); + void dual_play( int count, dsample_t out [], Stereo_Buffer&, Stereo_Buffer** secondary_buf_set = NULL, int secondary_buf_set_count = 0 ); -protected: - virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; + blargg_callback set_callback; + +// Implementation +public: + Dual_Resampler(); + ~Dual_Resampler(); + private: - + enum { gain_bits = 14 }; blargg_vector sample_buf; int sample_buf_size; int oversamples_per_frame; int buf_pos; + int buffered; int resampler_size; + int gain_; - Fir_Resampler<12> resampler; - void mix_samples( Blip_Buffer&, dsample_t* ); - void play_frame_( Blip_Buffer&, dsample_t* ); + Dual_Resampler_Downsampler resampler; + void mix_samples( Stereo_Buffer&, dsample_t [], int, Stereo_Buffer**, int ); + void mix_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_stereo( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_stereo( Stereo_Buffer&, dsample_t [], int ); + int play_frame_( Stereo_Buffer&, dsample_t [], Stereo_Buffer**, int ); }; -inline double Dual_Resampler::setup( double oversample, double rolloff, double gain ) +inline blargg_err_t 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(); + gain_ = (int) ((1 << gain_bits) * gain); + return resampler.set_rate( oversample ); } #endif diff --git a/Frameworks/GME/gme/Effects_Buffer.cpp b/Frameworks/GME/gme/Effects_Buffer.cpp old mode 100755 new mode 100644 index 730f8e94c..496c00bb0 --- a/Frameworks/GME/gme/Effects_Buffer.cpp +++ b/Frameworks/GME/gme/Effects_Buffer.cpp @@ -1,529 +1,640 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +/* Copyright (C) 2006-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 -#include "Effects_Buffer.h" +int const fixed_shift = 12; +#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) +#define FROM_FIXED( f ) ((f) >> fixed_shift) -#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( bool center_only ) : Multi_Buffer( 2 ) -{ - buf_count = center_only ? max_buf_count - 4 : max_buf_count; - - echo_pos = 0; - reverb_pos = 0; - - stereo_remain = 0; - effect_remain = 0; - effects_enabled = false; - set_depth( 0 ); -} - -Effects_Buffer::~Effects_Buffer() { } - -blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) -{ - if ( !echo_buf.size() ) - RETURN_ERR( echo_buf.resize( echo_size ) ); - - if ( !reverb_buf.size() ) - RETURN_ERR( reverb_buf.resize( reverb_size ) ); - - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - - config( config_ ); - clear(); - - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Effects_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Effects_Buffer::bass_freq( int freq ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( freq ); -} - -void Effects_Buffer::clear() -{ - stereo_remain = 0; - effect_remain = 0; - if ( echo_buf.size() ) - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - - if ( reverb_buf.size() ) - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -inline int pin_range( int n, int max, int min = 0 ) -{ - if ( n < min ) - return min; - if ( n > max ) - return max; - return n; -} - -void Effects_Buffer::config( const config_t& cfg ) -{ - channels_changed(); - - // clear echo and reverb buffers - if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) - { - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - } - - config_ = cfg; - - if ( config_.effects_enabled ) - { - // convert to internal format - - chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); - chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; - - chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); - chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; - - chans.reverb_level = TO_FIXED( config_.reverb_level ); - chans.echo_level = TO_FIXED( config_.echo_level ); - - int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); - - int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); - chans.reverb_delay_l = pin_range( reverb_size - - (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); - chans.reverb_delay_r = pin_range( reverb_size + 1 - - (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); - - int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); - chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), - echo_size - 1 ); - chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), - echo_size - 1 ); - - chan_types [0].center = &bufs [0]; - chan_types [0].left = &bufs [3]; - chan_types [0].right = &bufs [4]; - - chan_types [1].center = &bufs [1]; - chan_types [1].left = &bufs [3]; - chan_types [1].right = &bufs [4]; - - chan_types [2].center = &bufs [2]; - chan_types [2].left = &bufs [5]; - chan_types [2].right = &bufs [6]; - assert( 2 < chan_types_count ); - } - else - { - // set up outputs - for ( unsigned i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.center = &bufs [0]; - c.left = &bufs [1]; - c.right = &bufs [2]; - } - } - - if ( buf_count < max_buf_count ) - { - for ( int i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.left = c.center; - c.right = c.center; - } - } -} - -Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) -{ - int out = 2; - if ( !type ) - { - out = i % 5; - if ( out > 2 ) - out = 2; - } - else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) - { - out = type & 1; - } - return chan_types [out]; -} - -void Effects_Buffer::end_frame( blip_time_t clock_count ) -{ - int bufs_used = 0; - for ( int i = 0; i < buf_count; i++ ) - { - bufs_used |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } - - int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); - if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) - stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - if ( effects_enabled || config_.effects_enabled ) - effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - 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 ) -{ - require( total_samples % 2 == 0 ); // count must be even - - long remain = bufs [0].samples_avail(); - if ( remain > (total_samples >> 1) ) - remain = (total_samples >> 1); - total_samples = remain; - while ( remain ) - { - int active_bufs = buf_count; - 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 * 2; - remain -= count; - - stereo_remain -= count; - if ( stereo_remain < 0 ) - stereo_remain = 0; - - effect_remain -= count; - if ( effect_remain < 0 ) - effect_remain = 0; - - for ( int i = 0; i < buf_count; i++ ) - { - if ( i < active_bufs ) - bufs [i].remove_samples( count ); - else - bufs [i].remove_silence( count ); // keep time synchronized - } - } - - return total_samples * 2; -} - -void Effects_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( c, bufs [0] ); - - // unrolled loop - for ( blargg_long n = count >> 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 ( (BOOST::int16_t) cs0 != cs0 ) - cs0 = 0x7FFF - (cs0 >> 24); - ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); - - if ( (BOOST::int16_t) cs1 != cs1 ) - cs1 = 0x7FFF - (cs1 >> 24); - ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); - out += 4; - } - - if ( count & 1 ) - { - int s = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - out [0] = s; - out [1] = s; - if ( (BOOST::int16_t) s != s ) - { - s = 0x7FFF - (s >> 24); - out [0] = s; - out [1] = s; - } - } - - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_stereo( 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( c, bufs [0] ); - BLIP_READER_BEGIN( l, bufs [1] ); - BLIP_READER_BEGIN( r, bufs [2] ); - - while ( count-- ) - { - int cs = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - int left = cs + BLIP_READER_READ( l ); - int right = cs + BLIP_READER_READ( r ); - BLIP_READER_NEXT( l, bass ); - BLIP_READER_NEXT( r, bass ); - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - - BLIP_READER_END( r, bufs [2] ); - BLIP_READER_END( l, bufs [1] ); - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - 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 ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} - -void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( l1, bufs [3] ); - BLIP_READER_BEGIN( r1, bufs [4] ); - BLIP_READER_BEGIN( l2, bufs [5] ); - BLIP_READER_BEGIN( r2, bufs [6] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - 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 ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( l1, bufs [3] ); - BLIP_READER_END( r1, bufs [4] ); - BLIP_READER_END( l2, bufs [5] ); - BLIP_READER_END( r2, bufs [6] ); - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} +int const max_read = 2560; // determines minimum delay +Effects_Buffer::Effects_Buffer( int max_bufs, int echo_size_ ) : Multi_Buffer( stereo ) +{ + echo_size = max( max_read * (int) stereo, echo_size_ & ~1 ); + clock_rate_ = 0; + bass_freq_ = 90; + bufs = NULL; + bufs_size = 0; + bufs_max = max( max_bufs, (int) extra_chans ); + no_echo = true; + no_effects = true; + + // defaults + config_.enabled = false; + config_.delay [0] = 120; + config_.delay [1] = 122; + config_.feedback = 0.2f; + config_.treble = 0.4f; + + static float const sep = 0.8f; + config_.side_chans [0].pan = -sep; + config_.side_chans [1].pan = +sep; + config_.side_chans [0].vol = 1.0f; + config_.side_chans [1].vol = 1.0f; + + memset( &s, 0, sizeof s ); + clear(); +} + +Effects_Buffer::~Effects_Buffer() +{ + delete_bufs(); +} + +// avoid using new [] +blargg_err_t Effects_Buffer::new_bufs( int size ) +{ + bufs = (buf_t*) malloc( size * sizeof *bufs ); + CHECK_ALLOC( bufs ); + for ( int i = 0; i < size; i++ ) + new (bufs + i) buf_t; + bufs_size = size; + return blargg_ok; +} + +void Effects_Buffer::delete_bufs() +{ + if ( bufs ) + { + for ( int i = bufs_size; --i >= 0; ) + bufs [i].~buf_t(); + free( bufs ); + bufs = NULL; + } + bufs_size = 0; +} + +blargg_err_t Effects_Buffer::set_sample_rate( int rate, int msec ) +{ + // extra to allow farther past-the-end pointers + mixer.samples_read = 0; + RETURN_ERR( echo.resize( echo_size + stereo ) ); + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +void Effects_Buffer::clock_rate( int rate ) +{ + clock_rate_ = rate; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( clock_rate_ ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass_freq_ ); +} + +blargg_err_t Effects_Buffer::set_channel_count( int count, int const types [] ) +{ + RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) ); + + delete_bufs(); + + mixer.samples_read = 0; + + RETURN_ERR( chans.resize( count + extra_chans ) ); + + RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) ); + + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) ); + + for ( int i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.cfg.vol = 1.0f; + ch.cfg.pan = 0.0f; + ch.cfg.surround = false; + ch.cfg.echo = false; + } + // side channels with echo + chans [2].cfg.echo = true; + chans [3].cfg.echo = true; + + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + apply_config(); + clear(); + + return blargg_ok; +} + +void Effects_Buffer::clear_echo() +{ + if ( echo.size() ) + memset( echo.begin(), 0, echo.size() * sizeof echo [0] ); +} + +void Effects_Buffer::clear() +{ + echo_pos = 0; + s.low_pass [0] = 0; + s.low_pass [1] = 0; + mixer.samples_read = 0; + + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); + clear_echo(); +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i ) +{ + i += extra_chans; + require( extra_chans <= i && i < (int) chans.size() ); + return chans [i].channel; +} + + +// Configuration + +// 3 wave positions with/without surround, 2 multi (one with same config as wave) +int const simple_bufs = 3 * 2 + 2 - 1; + +Simple_Effects_Buffer::Simple_Effects_Buffer() : + Effects_Buffer( extra_chans + simple_bufs, 18 * 1024 ) +{ + config_.echo = 0.20f; + config_.stereo = 0.20f; + config_.surround = true; + config_.enabled = false; +} + +void Simple_Effects_Buffer::apply_config() +{ + Effects_Buffer::config_t& c = Effects_Buffer::config(); + + c.enabled = config_.enabled; + if ( c.enabled ) + { + c.delay [0] = 120; + c.delay [1] = 122; + c.feedback = config_.echo * 0.7f; + c.treble = 0.6f - 0.3f * config_.echo; + + float sep = config_.stereo + 0.80f; + if ( sep > 1.0f ) + sep = 1.0f; + + c.side_chans [0].pan = -sep; + c.side_chans [1].pan = +sep; + + for ( int i = channel_count(); --i >= 0; ) + { + chan_config_t& ch = Effects_Buffer::chan_config( i ); + + ch.pan = 0.0f; + ch.surround = config_.surround; + ch.echo = false; + + int const type = (channel_types() ? channel_types() [i] : 0); + if ( !(type & noise_type) ) + { + int index = (type & type_index_mask) % 6 - 3; + if ( index < 0 ) + { + index += 3; + ch.surround = false; + ch.echo = true; + } + if ( index >= 1 ) + { + ch.pan = config_.stereo; + if ( index == 1 ) + ch.pan = -ch.pan; + } + } + else if ( type & 1 ) + { + ch.surround = false; + } + } + } + + Effects_Buffer::apply_config(); +} + +int Effects_Buffer::min_delay() const +{ + require( sample_rate() ); + return max_read * 1000 / sample_rate(); +} + +int Effects_Buffer::max_delay() const +{ + require( sample_rate() ); + return (echo_size / stereo - max_read) * 1000 / sample_rate(); +} + +void Effects_Buffer::apply_config() +{ + int i; + + if ( !bufs_size ) + return; + + s.treble = TO_FIXED( config_.treble ); + + bool echo_dirty = false; + + fixed_t old_feedback = s.feedback; + s.feedback = TO_FIXED( config_.feedback ); + if ( !old_feedback && s.feedback ) + echo_dirty = true; + + // delays + for ( i = stereo; --i >= 0; ) + { + int delay = config_.delay [i] * sample_rate() / 1000 * stereo; + delay = max( delay, (int) (max_read * stereo) ); + delay = min( delay, (int) (echo_size - max_read * stereo) ); + if ( s.delay [i] != delay ) + { + s.delay [i] = delay; + echo_dirty = true; + } + } + + // side channels + for ( i = 2; --i >= 0; ) + { + chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f; + chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan; + } + + // convert volumes + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan ); + ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan ); + if ( ch.cfg.surround ) + ch.vol [0] = -ch.vol [0]; + } + + assign_buffers(); + + // set side channels + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.left = chans [ch.cfg.echo*2 ].channel.center; + ch.channel.right = chans [ch.cfg.echo*2+1].channel.center; + } + + bool old_echo = !no_echo && !no_effects; + + // determine whether effects and echo are needed at all + no_effects = true; + no_echo = true; + for ( i = chans.size(); --i >= extra_chans; ) + { + chan_t& ch = chans [i]; + if ( ch.cfg.echo && s.feedback ) + no_echo = false; + + if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + } + if ( !no_echo ) + no_effects = false; + + if ( chans [0].vol [0] != TO_FIXED( 1 ) || + chans [0].vol [1] != TO_FIXED( 0 ) || + chans [1].vol [0] != TO_FIXED( 0 ) || + chans [1].vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + + if ( !config_.enabled ) + no_effects = true; + + if ( no_effects ) + { + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.center = &bufs [2]; + ch.channel.left = &bufs [0]; + ch.channel.right = &bufs [1]; + } + } + + mixer.bufs [0] = &bufs [0]; + mixer.bufs [1] = &bufs [1]; + mixer.bufs [2] = &bufs [2]; + + if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) ) + clear_echo(); + + channels_changed(); +} + +void Effects_Buffer::assign_buffers() +{ + // assign channels to buffers + int buf_count = 0; + for ( int i = 0; i < (int) chans.size(); i++ ) + { + // put second two side channels at end to give priority to main channels + // in case closest matching is necessary + int x = i; + if ( i > 1 ) + x += 2; + if ( x >= (int) chans.size() ) + x -= (chans.size() - 2); + chan_t& ch = chans [x]; + + int b = 0; + for ( ; b < buf_count; b++ ) + { + if ( ch.vol [0] == bufs [b].vol [0] && + ch.vol [1] == bufs [b].vol [1] && + (ch.cfg.echo == bufs [b].echo || !s.feedback) ) + break; + } + + if ( b >= buf_count ) + { + if ( buf_count < bufs_max ) + { + bufs [b].vol [0] = ch.vol [0]; + bufs [b].vol [1] = ch.vol [1]; + bufs [b].echo = ch.cfg.echo; + buf_count++; + } + else + { + // TODO: this is a mess, needs refinement + dprintf( "Effects_Buffer ran out of buffers; using closest match\n" ); + b = 0; + fixed_t best_dist = TO_FIXED( 8 ); + for ( int h = buf_count; --h >= 0; ) + { + #define CALC_LEVELS( vols, sum, diff, surround ) \ + fixed_t sum, diff;\ + bool surround = false;\ + {\ + fixed_t vol_0 = vols [0];\ + if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\ + fixed_t vol_1 = vols [1];\ + if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\ + sum = vol_0 + vol_1;\ + diff = vol_0 - vol_1;\ + } + CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround ); + CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround ); + + fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff ); + + if ( ch_surround != buf_surround ) + dist += TO_FIXED( 1 ) / 2; + + if ( s.feedback && ch.cfg.echo != bufs [h].echo ) + dist += TO_FIXED( 1 ) / 2; + + if ( best_dist > dist ) + { + best_dist = dist; + b = h; + } + } + } + } + + //dprintf( "ch %d->buf %d\n", x, b ); + ch.channel.center = &bufs [b]; + } +} + + +// Mixing + +void Effects_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +int Effects_Buffer::read_samples( blip_sample_t out [], int out_size ) +{ + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + require( pair_count * stereo == out_size ); // must read an even number of samples + if ( pair_count ) + { + if ( no_effects ) + { + mixer.read_pairs( out, pair_count ); + } + else + { + int pairs_remain = pair_count; + do + { + // mix at most max_read pairs at a time + int count = max_read; + if ( count > pairs_remain ) + count = pairs_remain; + + if ( no_echo ) + { + // optimization: clear echo here to keep mix_effects() a leaf function + echo_pos = 0; + memset( echo.begin(), 0, count * stereo * sizeof echo [0] ); + } + mix_effects( out, count ); + + int new_echo_pos = echo_pos + count * stereo; + if ( new_echo_pos >= echo_size ) + new_echo_pos -= echo_size; + echo_pos = new_echo_pos; + assert( echo_pos < echo_size ); + + out += count * stereo; + mixer.samples_read += count; + pairs_remain -= count; + } + while ( pairs_remain ); + } + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( b.non_silent() ) + b.remove_samples( mixer.samples_read ); + else + b.remove_silence( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + +void Effects_Buffer::mix_effects( blip_sample_t out_ [], int pair_count ) +{ + typedef fixed_t stereo_fixed_t [stereo]; + + // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output + int echo_phase = 1; + do + { + // mix any modified buffers + { + buf_t* buf = bufs; + int bufs_remain = bufs_size; + do + { + if ( buf->non_silent() && buf->echo == echo_phase ) + { + stereo_fixed_t* BLARGG_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos]; + int const bass = BLIP_READER_BASS( *buf ); + BLIP_READER_BEGIN( in, *buf ); + BLIP_READER_ADJ_( in, mixer.samples_read ); + fixed_t const vol_0 = buf->vol [0]; + fixed_t const vol_1 = buf->vol [1]; + + int count = (unsigned) (echo_size - echo_pos) / stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + BLIP_READER_ADJ_( in, count ); + + out += count; + int offset = -count; + do + { + fixed_t s = BLIP_READER_READ( in ); + BLIP_READER_NEXT_IDX_( in, bass, offset ); + + out [offset] [0] += s * vol_0; + out [offset] [1] += s * vol_1; + } + while ( ++offset ); + + out = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + + BLIP_READER_END( in, *buf ); + } + buf++; + } + while ( --bufs_remain ); + } + + // add echo + if ( echo_phase && !no_echo ) + { + fixed_t const feedback = s.feedback; + fixed_t const treble = s.treble; + + int i = 1; + do + { + fixed_t low_pass = s.low_pass [i]; + + fixed_t* echo_end = &echo [echo_size + i]; + fixed_t const* BLARGG_RESTRICT in_pos = &echo [echo_pos + i]; + int out_offset = echo_pos + i + s.delay [i]; + if ( out_offset >= echo_size ) + out_offset -= echo_size; + assert( out_offset < echo_size ); + fixed_t* BLARGG_RESTRICT out_pos = &echo [out_offset]; + + // break into up to three chunks to avoid having to handle wrap-around + // in middle of core loop + int remain = pair_count; + do + { + fixed_t const* pos = in_pos; + if ( pos < out_pos ) + pos = out_pos; + int count = (unsigned) ((char*) echo_end - (char const*) pos) / + (unsigned) (stereo * sizeof (fixed_t)); + if ( count > remain ) + count = remain; + remain -= count; + + in_pos += count * stereo; + out_pos += count * stereo; + int offset = -count; + do + { + low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble; + out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback; + } + while ( ++offset ); + + if ( in_pos >= echo_end ) in_pos -= echo_size; + if ( out_pos >= echo_end ) out_pos -= echo_size; + } + while ( remain ); + + s.low_pass [i] = low_pass; + } + while ( --i >= 0 ); + } + } + while ( --echo_phase >= 0 ); + + // clamp to 16 bits + { + stereo_fixed_t const* BLARGG_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos]; + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_; + int count = (unsigned) (echo_size - echo_pos) / (unsigned) stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + in += count; + out += count; + int offset = -count; + do + { + fixed_t in_0 = FROM_FIXED( in [offset] [0] ); + fixed_t in_1 = FROM_FIXED( in [offset] [1] ); + + BLIP_CLAMP( in_0, in_0 ); + out [offset] [0] = (blip_sample_t) in_0; + + BLIP_CLAMP( in_1, in_1 ); + out [offset] [1] = (blip_sample_t) in_1; + } + while ( ++offset ); + + in = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + } +} diff --git a/Frameworks/GME/gme/Effects_Buffer.h b/Frameworks/GME/gme/Effects_Buffer.h old mode 100755 new mode 100644 index eb0aa67a3..cd6c620f8 --- a/Frameworks/GME/gme/Effects_Buffer.h +++ b/Frameworks/GME/gme/Effects_Buffer.h @@ -1,86 +1,149 @@ -// Multi-channel effects buffer with panning, echo and reverb +// Multi-channel effects buffer with echo and individual panning for each channel -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef EFFECTS_BUFFER_H #define EFFECTS_BUFFER_H #include "Multi_Buffer.h" -// Effects_Buffer uses several buffers and outputs stereo sample pairs. +// See Simple_Effects_Buffer (below) for a simpler interface + class Effects_Buffer : public Multi_Buffer { public: - // If center_only is true, only center buffers are created and - // less memory is used. - Effects_Buffer( bool center_only = false ); + // To reduce memory usage, fewer buffers can be used (with a best-fit + // approach if there are too few), and maximum echo delay can be reduced + Effects_Buffer( int max_bufs = 32, int echo_size = 24 * 1024 ); - // 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(); + struct pan_vol_t + { + float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal + float pan; // -1.0 = left, 0.0 = center, +1.0 = right }; - // Set configuration of buffer - virtual void config( const config_t& ); - void set_depth( double ); + // Global configuration + struct config_t + { + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent + int delay [2]; // left, right delays (msec) + float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony + pan_vol_t side_chans [2]; // left and right side channel volume and pan + }; + config_t& config() { return config_; } + // Limits of delay (msec) + int min_delay() const; + int max_delay() const; + + // Per-channel configuration. Two or more channels with matching parameters are + // optimized to internally use the same buffer. + struct chan_config_t : pan_vol_t + { + // (inherited from pan_vol_t) + //float vol; // these only affect center channel + //float pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo + }; + chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } + + // Applies any changes made to config() and chan_config() + virtual void apply_config(); + +// Implementation public: ~Effects_Buffer(); - blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); - void clock_rate( long ); + blargg_err_t set_sample_rate( int samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_channel_count( int, int const* = NULL ); + void clock_rate( int ); void bass_freq( int ); void clear(); - channel_t channel( int, int ); + channel_t channel( int ); void end_frame( blip_time_t ); - long read_samples( blip_sample_t*, long ); - long samples_avail() const; + int read_samples( blip_sample_t [], int ); + int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + enum { stereo = 2 }; + typedef int fixed_t; + +protected: + enum { extra_chans = stereo * stereo }; + private: - typedef long fixed_t; - - enum { max_buf_count = 7 }; - Blip_Buffer bufs [max_buf_count]; - enum { chan_types_count = 3 }; - channel_t chan_types [3]; config_t config_; - long stereo_remain; - long effect_remain; - int buf_count; - bool effects_enabled; + int clock_rate_; + int bass_freq_; - blargg_vector reverb_buf; - blargg_vector echo_buf; - int reverb_pos; - int echo_pos; + int echo_size; + + struct chan_t + { + fixed_t vol [stereo]; + chan_config_t cfg; + channel_t channel; + }; + blargg_vector chans; + + struct buf_t : Tracked_Blip_Buffer + { + // nasty: Blip_Buffer has something called fixed_t + Effects_Buffer::fixed_t vol [stereo]; + bool echo; + + void* operator new ( size_t, void* p ) { return p; } + void operator delete ( void* ) { } + + ~buf_t() { } + }; + buf_t* bufs; + int bufs_size; + int bufs_max; // bufs_size <= bufs_max, to limit memory usage + Stereo_Mixer mixer; 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; + int delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; + } s; - 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 ); + blargg_vector echo; + int echo_pos; + + bool no_effects; + bool no_echo; + + void assign_buffers(); + void clear_echo(); + void mix_effects( blip_sample_t out [], int pair_count ); + blargg_err_t new_bufs( int size ); + void delete_bufs(); +}; + +// Simpler interface and lower memory usage +class Simple_Effects_Buffer : public Effects_Buffer { +public: + struct config_t + { + bool enabled; // false = disable all effects + + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back + }; + config_t& config() { return config_; } + + // Applies any changes made to config() + void apply_config(); + +// Implementation +public: + Simple_Effects_Buffer(); +private: + config_t config_; + void chan_config(); // hide }; #endif diff --git a/Frameworks/GME/gme/Fir_Resampler.cpp b/Frameworks/GME/gme/Fir_Resampler.cpp old mode 100755 new mode 100644 index 4e0a46313..5dddf29b6 --- a/Frameworks/GME/gme/Fir_Resampler.cpp +++ b/Frameworks/GME/gme/Fir_Resampler.cpp @@ -1,13 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Fir_Resampler.h" -#include -#include -#include #include -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* Copyright (C) 2004-2008 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 @@ -52,148 +49,75 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing, } } -Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : +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; + imp = NULL; } -Fir_Resampler_::~Fir_Resampler_() { } - -void Fir_Resampler_::clear() +void Fir_Resampler_::clear_() { - imp_phase = 0; - if ( buf.size() ) - { - write_pos = &buf [write_offset]; - memset( buf.begin(), 0, write_offset * sizeof buf [0] ); - } + imp = impulses; + Resampler::clear_(); } -blargg_err_t Fir_Resampler_::buffer_size( int new_size ) +blargg_err_t Fir_Resampler_::set_rate_( double new_factor ) { - RETURN_ERR( buf.resize( new_size + write_offset ) ); - clear(); - return 0; -} + double const rolloff = 0.999; + double const gain = 1.0; -double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) -{ - ratio_ = new_factor; - - double fstep = 0.0; + // determine number of sub-phases that yield lowest error + double ratio_ = 0.0; + int res = -1; { double least_error = 2; double pos = 0; - res = -1; for ( int r = 1; r <= max_res; r++ ) { - pos += ratio_; + pos += new_factor; double nearest = floor( pos + 0.5 ); double error = fabs( pos - nearest ); if ( error < least_error ) { res = r; - fstep = nearest / res; + ratio_ = nearest / res; least_error = error; } } } + RETURN_ERR( Resampler::set_rate_( ratio_ ) ); - skip_bits = 0; + // how much of input is used for each output sample + int const step = stereo * (int) floor( ratio_ ); + double fraction = fmod( ratio_, 1.0 ); - step = stereo * (int) floor( fstep ); - - ratio_ = fstep; - fstep = fmod( fstep, 1.0 ); - - double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; double pos = 0.0; - input_per_cycle = 0; - for ( int i = 0; i < res; i++ ) + //int input_per_cycle = 0; + sample_t* out = impulses; + for ( int n = res; --n >= 0; ) { gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, - double (0x7FFF * gain * filter), - (int) width_, impulses + i * width_ ); + double (0x7FFF * gain * filter), (int) width_, out ); + out += width_; - pos += fstep; - input_per_cycle += step; + int cur_step = step; + pos += fraction; if ( pos >= 0.9999999 ) { pos -= 1.0; - skip_bits |= 1 << i; - input_per_cycle++; + cur_step += stereo; } + + *out++ = (cur_step - width_ * 2 + 4) * sizeof (sample_t); + *out++ = 4 * sizeof (sample_t); + //input_per_cycle += cur_step; } + // last offset moves back to beginning of impulses + out [-1] -= (char*) out - (char*) impulses; - clear(); + imp = impulses; - 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; + return blargg_ok; } diff --git a/Frameworks/GME/gme/Fir_Resampler.h b/Frameworks/GME/gme/Fir_Resampler.h old mode 100755 new mode 100644 index 339dfce37..2344493c1 --- a/Frameworks/GME/gme/Fir_Resampler.h +++ b/Frameworks/GME/gme/Fir_Resampler.h @@ -1,171 +1,101 @@ // Finite impulse response (FIR) resampler with adjustable FIR size -// Game_Music_Emu 0.5.2 +// $package #ifndef FIR_RESAMPLER_H #define FIR_RESAMPLER_H -#include "blargg_common.h" -#include +#include "Resampler.h" + +template +class Fir_Resampler; + +// Use one of these typedefs +typedef Fir_Resampler< 8> Fir_Resampler_Fast; +typedef Fir_Resampler<16> Fir_Resampler_Norm; +typedef Fir_Resampler<24> Fir_Resampler_Good; + +// Implementation +class Fir_Resampler_ : public Resampler { +protected: + virtual blargg_err_t set_rate_( double ); + virtual void clear_(); -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; + enum { max_res = 32 }; // TODO: eliminate and keep impulses on freestore? + sample_t const* imp; 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; + Fir_Resampler_( int width, sample_t [] ); }; -// 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. +// Width is number of points in FIR. 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]; + enum { min_width = (width < 4 ? 4 : width) }; + enum { adj_width = min_width / 4 * 4 + 2 }; + enum { write_offset = adj_width * stereo }; + short impulses [max_res * (adj_width + 2)]; 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 ); + Fir_Resampler() : Fir_Resampler_( adj_width, impulses ) { } + +protected: + virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); }; -// 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 ) +Resampler::sample_t const* Fir_Resampler::resample_( sample_t** out_, + sample_t const* out_end, sample_t const in [], int in_size ) { - 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 ) + in_size -= write_offset; + if ( in_size > 0 ) { - end_pos -= width * stereo; + sample_t* BLARGG_RESTRICT out = *out_; + sample_t const* const in_end = in + in_size; + sample_t const* imp = this->imp; + do { - count--; - // accumulate in extended precision - blargg_long l = 0; - blargg_long r = 0; - - const sample_t* i = in; - if ( count < 0 ) + int pt = imp [0]; + int l = pt * in [0]; + int r = pt * in [1]; + if ( out >= out_end ) break; - - for ( int n = width / 2; n; --n ) + for ( int n = (adj_width - 2) / 2; n; --n ) { - int pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - int pt1 = imp [1]; + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; + + // pre-increment more efficient on some RISC processors imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; + pt = imp [0]; + r += pt * in [5]; + in += 4; + l += pt * in [0]; } + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; - remain--; + // these two "samples" after the end of the impulse give the + // proper offsets to the next input sample and next impulse + in = (sample_t const*) ((char const*) in + imp [2]); // some negative value + imp = (sample_t const*) ((char const*) imp + imp [3]); // small positive or large negative - 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 [0] = sample_t (l >> 15); + out [1] = sample_t (r >> 15); out += 2; } - while ( in <= end_pos ); + while ( in < in_end ); + + this->imp = imp; + *out_ = out; } - - imp_phase = res - remain; - - int left = write_pos - in; - write_pos = &buf [left]; - memmove( buf.begin(), in, left * sizeof *in ); - - return out - out_begin; + return in; } #endif diff --git a/Frameworks/GME/gme/Gb_Apu.cpp b/Frameworks/GME/gme/Gb_Apu.cpp old mode 100755 new mode 100644 index 932ebb838..35b77e574 --- a/Frameworks/GME/gme/Gb_Apu.cpp +++ b/Frameworks/GME/gme/Gb_Apu.cpp @@ -1,10 +1,10 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Apu.h" -#include +//#include "gb_apu_logger.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -17,289 +17,390 @@ 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; +int const vol_reg = 0xFF24; +int const stereo_reg = 0xFF25; +int const status_reg = 0xFF26; +int const wave_ram = 0xFF30; + +int const power_mask = 0x80; + +void Gb_Apu::treble_eq( blip_eq_t const& eq ) +{ + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); +} + +inline int Gb_Apu::calc_output( int osc ) const +{ + int bits = regs [stereo_reg - io_addr] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void Gb_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Gb_Osc& o = *oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Gb_Apu::synth_volume( int iv ) +{ + double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; + norm_synth.volume( v ); + fast_synth.volume( v ); +} + +void Gb_Apu::apply_volume() +{ + // TODO: Doesn't handle differing left and right volumes (panning). + // Not worth the complexity. + int data = regs [vol_reg - io_addr]; + int left = data >> 4 & 7; + int right = data & 7; + //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); + //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); + synth_volume( max( left, right ) + 1 ); +} + +void Gb_Apu::volume( double v ) +{ + if ( volume_ != v ) + { + volume_ = v; + apply_volume(); + } +} + +void Gb_Apu::reset_regs() +{ + for ( int i = 0; i < 0x20; i++ ) + regs [i] = 0; + + square1.reset(); + square2.reset(); + wave .reset(); + noise .reset(); + + apply_volume(); +} + +void Gb_Apu::reset_lengths() +{ + square1.length_ctr = 64; + square2.length_ctr = 64; + wave .length_ctr = 256; + noise .length_ctr = 64; +} + +void Gb_Apu::reduce_clicks( bool reduce ) +{ + reduce_clicks_ = reduce; + + // Click reduction makes DAC off generate same output as volume 0 + int dac_off_amp = 0; + if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks + dac_off_amp = -Gb_Osc::dac_bias; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->dac_off_amp = dac_off_amp; + + // AGB always eliminates clicks on wave channel using same method + if ( wave.mode == mode_agb ) + wave.dac_off_amp = -Gb_Osc::dac_bias; +} + +void Gb_Apu::reset( mode_t mode, bool agb_wave ) +{ + // Hardware mode + if ( agb_wave ) + mode = mode_agb; // using AGB wave features implies AGB hardware + wave.agb_mask = agb_wave ? 0xFF : 0; + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->mode = mode; + reduce_clicks( reduce_clicks_ ); + + // Reset state + frame_time = 0; + last_time = 0; + frame_phase = 0; + + reset_regs(); + reset_lengths(); + + // Load initial wave RAM + static byte const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + // Init both banks (does nothing if not in AGB mode) + // TODO: verify that this works + write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); + } +} + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 512; // 512 Hz + if ( t != 1.0 ) + frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0); +} Gb_Apu::Gb_Apu() { - square1.synth = &square_synth; - square2.synth = &square_synth; - wave.synth = &other_synth; - noise.synth = &other_synth; + wave.wave_ram = ®s [wave_ram - io_addr]; oscs [0] = &square1; oscs [1] = &square2; oscs [2] = &wave; oscs [3] = &noise; - for ( int i = 0; i < osc_count; i++ ) + for ( int i = osc_count; --i >= 0; ) { - 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; + Gb_Osc& o = *oscs [i]; + o.regs = ®s [i * 5]; + o.output = NULL; + o.outputs [0] = NULL; + o.outputs [1] = NULL; + o.outputs [2] = NULL; + o.outputs [3] = NULL; + o.norm_synth = &norm_synth; + o.fast_synth = &fast_synth; } + reduce_clicks_ = false; set_tempo( 1.0 ); - volume( 1.0 ); + volume_ = 1.0; reset(); } -void Gb_Apu::treble_eq( const blip_eq_t& eq ) +void Gb_Apu::run_until_( blip_time_t end_time ) { - 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 wave.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; + if ( !frame_period ) + frame_time += end_time - last_time; 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; - } - } - } + blip_time_t time = end_time; + if ( time > frame_time ) + time = frame_time; + + square1.run( last_time, time ); + square2.run( last_time, time ); + wave .run( last_time, time ); + noise .run( last_time, time ); 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 ) + // run frame sequencer + assert( frame_period ); + frame_time += frame_period * Gb_Osc::clk_mul; + switch ( frame_phase++ ) { - // 64 Hz actions + case 2: + case 6: + // 128 Hz + square1.clock_sweep(); + case 0: + case 4: + // 256 Hz + square1.clock_length(); + square2.clock_length(); + wave .clock_length(); + noise .clock_length(); + break; + + case 7: + // 64 Hz + frame_phase = 0; square1.clock_envelope(); square2.clock_envelope(); - noise.clock_envelope(); + noise .clock_envelope(); } - - if ( frame_count & 1 ) - square1.clock_sweep(); // 128 Hz action } } +inline void Gb_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); // end_time must not be before previous time + if ( time > last_time ) + run_until_( time ); +} + void Gb_Apu::end_frame( blip_time_t end_time ) { + #ifdef LOG_FRAME + LOG_FRAME( end_time ); + #endif + if ( end_time > last_time ) run_until( end_time ); - assert( next_frame_time >= end_time ); - next_frame_time -= end_time; + frame_time -= end_time; + assert( frame_time >= 0 ); - assert( last_time >= end_time ); last_time -= end_time; + assert( last_time >= 0 ); } -void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +void Gb_Apu::silence_osc( Gb_Osc& o ) +{ + int delta = -o.last_amp; + if ( reduce_clicks_ ) + delta += o.dac_off_amp; + + if ( delta ) + { + o.last_amp = o.dac_off_amp; + if ( o.output ) + { + o.output->set_modified(); + fast_synth.offset( last_time, delta, o.output ); + } + } +} + +void Gb_Apu::apply_stereo() +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + Blip_Buffer* out = o.outputs [calc_output( i )]; + if ( o.output != out ) + { + silence_osc( o ); + o.output = out; + } + } +} + +void Gb_Apu::write_register( blip_time_t time, int addr, int data ) { require( (unsigned) data < 0x100 ); - int reg = addr - start_addr; - if ( (unsigned) reg >= register_count ) + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); return; + } + + #ifdef LOG_WRITE + LOG_WRITE( time, addr, data ); + #endif + + if ( addr < status_reg && !(regs [status_reg - io_addr] & power_mask) ) + { + // Power is off + + // length counters can only be written in DMG mode + if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) + return; + + if ( reg < 10 ) + data &= 0x3F; // clear square duty + } run_until( time ); - int old_reg = regs [reg]; - regs [reg] = data; - - if ( addr < vol_reg ) + if ( addr >= wave_ram ) { - write_osc( reg / 5, reg, data ); + wave.write( addr, data ); } - else if ( addr == vol_reg && data != old_reg ) // global volume + else { - // return all oscs to 0 - for ( int i = 0; i < osc_count; i++ ) + int old_data = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) { - 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 ); + // Oscillator + write_osc( reg, old_data, data ); } - - 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++ ) + else if ( addr == vol_reg && data != old_data ) { - 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 ); - } + // Master volume + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + apply_volume(); } - - if ( addr == status_reg && data != old_reg ) + else if ( addr == stereo_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 - { - //dprintf( "APU powered on\n" ); - } + // Stereo panning + apply_stereo(); + } + else if ( addr == status_reg && (data ^ old_data) & power_mask ) + { + // Power control + frame_phase = 0; + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + reset_regs(); + if ( wave.mode != mode_dmg ) + reset_lengths(); + + regs [status_reg - io_addr] = data; } - } - 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 ) +int Gb_Apu::read_register( blip_time_t time, int addr ) { - run_until( time ); + if ( addr >= status_reg ) + run_until( time ); - int index = addr - start_addr; - require( (unsigned) index < register_count ); - int data = regs [index]; + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); + return 0; + } + if ( addr >= wave_ram ) + return wave.read( addr ); + + // Value read back has some bits always set + static byte const masks [] = { + 0x80,0x3F,0x00,0xFF,0xBF, + 0xFF,0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, + 0xFF,0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + int mask = masks [reg]; + if ( wave.agb_mask && (reg == 10 || reg == 12) ) + mask = 0x1F; // extra implemented bits in wave regs on AGB + int data = regs [reg] | mask; + + // Status register 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; - } + data &= 0xF0; + data |= (int) square1.enabled << 0; + data |= (int) square2.enabled << 1; + data |= (int) wave .enabled << 2; + data |= (int) noise .enabled << 3; } return data; diff --git a/Frameworks/GME/gme/Gb_Apu.h b/Frameworks/GME/gme/Gb_Apu.h old mode 100755 new mode 100644 index e74ebc55b..ebc72687c --- a/Frameworks/GME/gme/Gb_Apu.h +++ b/Frameworks/GME/gme/Gb_Apu.h @@ -1,90 +1,193 @@ -// Nintendo Game Boy PAPU sound chip emulator +// Nintendo Game Boy sound hardware emulator with save state support -// Gb_Snd_Emu 0.1.5 +// Gb_Snd_Emu $vers #ifndef GB_APU_H #define GB_APU_H #include "Gb_Oscs.h" +struct gb_apu_state_t; + class Gb_Apu { public: +// Basics + + // Sets buffer(s) to generate sound into, or NULL to mute. If only center is not NULL, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - // Set overall volume of all oscillators, where 1.0 is full volume + // Emulates to time t, then writes data to addr + void write_register( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Clock rate sound hardware runs at + enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xFF10 }; + enum { io_size = 0x30 }; + + // Emulates to time t, then reads from addr + int read_register( blip_time_t t, int addr ); + + // Resets hardware to state after power, BEFORE boot ROM runs. Mode selects + // sound hardware. If agb_wave is true, enables AGB's extra wave features. + enum mode_t { + mode_dmg, // Game Boy monochrome + mode_cgb, // Game Boy Color + mode_agb // Game Boy Advance + }; + void reset( mode_t mode = mode_cgb, bool agb_wave = false ); + + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, + Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets overall volume, where 1.0 is normal void volume( double ); - // Set treble equalization - void treble_eq( const blip_eq_t& ); + // Sets treble equalization + void treble_eq( blip_eq_t const& ); - // 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). + // Treble and bass values for various hardware. + enum { + speaker_treble = -47, // speaker on system + speaker_bass = 2000, + dmg_treble = 0, // headphones on each system + dmg_bass = 30, + cgb_treble = 0, + cgb_bass = 300, // CGB has much less bass + agb_treble = 0, + agb_bass = 30 + }; - // 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 ); + // If true, reduces clicking by disabling DAC biasing. Note that this reduces + // emulation accuracy, since the clicks are authentic. + void reduce_clicks( bool reduce = true ); + // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the + // tempo in a music player. void set_tempo( double ); -public: - Gb_Apu(); + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( gb_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( gb_apu_state_t const& in ); + private: // noncopyable Gb_Apu( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& ); + +// Implementation +public: + Gb_Apu(); + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0xFF10 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0xFF3F }; ) + BLARGG_DEPRECATED_TEXT( enum { register_count = end_addr - start_addr + 1 }; ) + +private: 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; + blip_time_t last_time; // time sound emulator has been run to + blip_time_t frame_period; // clocks between each frame sequencer step + double volume_; + bool reduce_clicks_; - Gb_Square square1; - Gb_Square square2; - Gb_Wave wave; - Gb_Noise noise; - BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // used by squares - Gb_Wave::Synth other_synth; // used by wave and noise + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + blip_time_t frame_time; // time of next frame sequencer action + int frame_phase; // phase of next frame sequencer step + enum { regs_size = io_size + 0x10 }; + BOOST::uint8_t regs [regs_size];// last values written to registers - void update_volume(); + // large objects after everything else + Blip_Synth_Norm norm_synth; + Blip_Synth_Fast fast_synth; + + void reset_lengths(); + void reset_regs(); + int calc_output( int osc ) const; + void apply_stereo(); + void apply_volume(); + void synth_volume( int ); + void run_until_( blip_time_t ); void run_until( blip_time_t ); - void write_osc( int index, int reg, int data ); + void silence_osc( Gb_Osc& ); + void write_osc( int reg, int old_data, int data ); + const char* save_load( gb_apu_state_t*, bool save ); + void save_load2( gb_apu_state_t*, bool save ); + friend class Gb_Apu2; }; -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 ) +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t { - volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; - update_volume(); +#if GB_APU_CUSTOM_STATE + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; +#else + // Values written in portable little-endian format, allowing structure + // to be written directly to disk. + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414247 }; // 'GBAP' + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +inline void Gb_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); } +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c ) { set_output( c, c, c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + #endif diff --git a/Frameworks/GME/gme/Gb_Cpu.cpp b/Frameworks/GME/gme/Gb_Cpu.cpp old mode 100755 new mode 100644 index b1f22bd9a..c8b7005a0 --- a/Frameworks/GME/gme/Gb_Cpu.cpp +++ b/Frameworks/GME/gme/Gb_Cpu.cpp @@ -1,12 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Cpu.h" -#include +#include "blargg_endian.h" -//#include "gb_cpu_log.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -17,1040 +15,37 @@ 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 ) +inline void Gb_Cpu::set_code_page( int i, void* p ) { - state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); + byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size ); + cpu_state_.code_map [i] = p2; + cpu_state->code_map [i] = p2; } void Gb_Cpu::reset( void* unmapped ) { - check( state == &state_ ); - state = &state_; + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; - state_.remain = 0; + cpu_state_.time = 0; - for ( int i = 0; i < page_count + 1; i++ ) - set_code_page( i, (uint8_t*) unmapped ); + for ( int i = 0; i < page_count + 1; ++i ) + set_code_page( i, 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 ) +void Gb_Cpu::map_code( addr_t start, int size, void* data ) { // address range must begin and end on page boundaries require( start % page_size == 0 ); - require( size % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= mem_size ); - 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 ); - - typedef BOOST::uint16_t uint16_t; - -#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 { - BOOST::uint16_t bc, de, hl, unused; // pairs - } rp; - - uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::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 = (BOOST::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 = BOOST::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 = BOOST::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; + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset ); } diff --git a/Frameworks/GME/gme/Gb_Cpu.h b/Frameworks/GME/gme/Gb_Cpu.h old mode 100755 new mode 100644 index 953fbaf50..eb9fc81a1 --- a/Frameworks/GME/gme/Gb_Cpu.h +++ b/Frameworks/GME/gme/Gb_Cpu.h @@ -1,93 +1,82 @@ // Nintendo Game Boy CPU emulator -// Treats every instruction as taking 4 cycles -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #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: - typedef BOOST::uint8_t uint8_t; + typedef int addr_t; + typedef BOOST::uint8_t byte; - // Clear registers and map all pages to unmapped - void reset( void* unmapped = 0 ); + enum { mem_size = 0x10000 }; - // Map code memory (memory accessed via the program counter). Start and size + // Clears registers and map all pages to unmapped + void reset( void* unmapped = NULL ); + + // Maps 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 ); + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void* code ); - uint8_t* get_code( gb_addr_t ); + // Accesses emulated memory as CPU does + byte* get_code( addr_t ); - // Push a byte on the stack - void push_byte( int ); - - // Game Boy Z80 registers. *Not* kept updated during a call to run(). + // Game Boy Z-80 registers. NOT kept updated during emulation. 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 + BOOST::uint16_t bc, de, hl, fa; }; struct registers_t : core_regs_t { - long pc; // more than 16 bits to allow overflow detection + int pc; // more than 16 bits to allow overflow detection BOOST::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, to simplify GBS player (normally 0) + addr_t rst_base; - // Base address for RST vectors (normally 0) - gb_addr_t rst_base; + // Current time. + int time() const { return cpu_state->time; } - // If CPU executes opcode 0xFF at this address, it treats as illegal instruction - enum { idle_addr = 0xF00D }; + // Changes time. Must not be called during emulation. + // Should be negative, because emulation stops once it becomes >= 0. + void set_time( int t ) { cpu_state->time = t; } - // 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 + // Emulator reads this many bytes past end of a page enum { cpu_padding = 8 }; + +// Implementation 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& ); + Gb_Cpu() : rst_base( 0 ) { cpu_state = &cpu_state_; } + enum { page_count = mem_size >> page_bits }; - struct state_t { - uint8_t* code_map [page_count + 1]; - blargg_long remain; + struct cpu_state_t { + byte* code_map [page_count + 1]; + int time; }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; + cpu_state_t* cpu_state; // points to state_ or a local copy within run() + cpu_state_t cpu_state_; - void set_code_page( int, uint8_t* ); +private: + void set_code_page( int, void* ); }; -inline BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) +#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> Gb_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define GB_CPU_OFFSET( addr ) (addr) +#else + #define GB_CPU_OFFSET( addr ) ((addr) & (Gb_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t* Gb_Cpu::get_code( addr_t addr ) { - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr ); } #endif diff --git a/Frameworks/GME/gme/Gb_Oscs.cpp b/Frameworks/GME/gme/Gb_Oscs.cpp old mode 100755 new mode 100644 index 735653fa9..08e06e31b --- a/Frameworks/GME/gme/Gb_Oscs.cpp +++ b/Frameworks/GME/gme/Gb_Oscs.cpp @@ -1,10 +1,8 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Apu.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -17,320 +15,698 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// Gb_Osc +bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games +bool const cgb_05 = false; // enables CGB-05 zombie behavior + +int const trigger_mask = 0x80; +int const length_enabled = 0x40; void Gb_Osc::reset() { - delay = 0; + output = NULL; last_amp = 0; - length = 0; - output_select = 3; - output = outputs [output_select]; + delay = 0; + phase = 0; + enabled = false; } -void Gb_Osc::clock_length() +inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) { - if ( (regs [4] & len_enabled_mask) && length ) - length--; -} - -// Gb_Env - -void Gb_Env::clock_envelope() -{ - if ( env_delay && !--env_delay ) + output->set_modified(); + int delta = new_amp - last_amp; + if ( delta ) { - env_delay = regs [2] & 7; - int v = volume - 1 + (regs [2] >> 2 & 2); - if ( (unsigned) v < 15 ) - volume = v; + last_amp = new_amp; + fast_synth->offset( time, delta, output ); } } -bool Gb_Env::write_register( int reg, int data ) +// Units + +void Gb_Osc::clock_length() { + if ( (regs [4] & length_enabled) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } +} + +inline int Gb_Env::reload_env_timer() +{ + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; +} + +void Gb_Env::clock_envelope() +{ + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } +} + +inline void Gb_Sweep_Square::reload_sweep_timer() +{ + sweep_delay = (regs [0] & period_mask) >> 4; + if ( !sweep_delay ) + sweep_delay = 8; +} + +void Gb_Sweep_Square::calc_sweep( bool update ) +{ + int const shift = regs [0] & shift_mask; + int const delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + int const freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + { + enabled = false; + } + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } +} + +void Gb_Sweep_Square::clock_sweep() +{ + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & period_mask) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } +} + +int Gb_Wave::access( int addr ) const +{ + if ( enabled ) + { + addr = phase & (bank_size - 1); + if ( mode == Gb_Apu::mode_dmg ) + { + addr++; + if ( delay > clk_mul ) + return -1; // can only access within narrow time window while playing + } + addr >>= 1; + } + return addr & 0x0F; +} + +// write_register + +int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) +{ + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) + { + if ( (data & length_enabled) || cgb_02 ) + length_ctr--; + } + + if ( data & trigger_mask ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & length_enabled) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & trigger_mask; +} + +inline void Gb_Env::zombie_volume( int old, int data ) +{ + int v = volume; + if ( mode == Gb_Apu::mode_agb || cgb_05 ) + { + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + { + v++; + } + } + else + { + // CGB-04&02 behavior, very close to MGB behavior as well + if ( !(old & 7) && env_enabled ) + v++; + else if ( !(old & 8) ) + v += 2; + + if ( (old ^ data) & 8 ) + v = 16 - v; + } + volume = v & 0x0F; +} + +bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) +{ + int const max_len = 64; + switch ( reg ) { case 1: - length = 64 - (regs [1] & 0x3F); + length_ctr = max_len - (data & (max_len - 1)); break; case 2: - if ( !(data >> 4) ) + if ( !dac_enabled() ) enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } break; case 4: - if ( data & trigger ) + if ( write_trig( frame_phase, max_len, old ) ) { - env_delay = regs [2] & 7; volume = regs [2] >> 4; - enabled = true; - if ( length == 0 ) - length = 64; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !dac_enabled() ) + enabled = false; return true; } } return false; } -// Gb_Square - -void Gb_Square::reset() +bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) { - phase = 0; - sweep_freq = 0; - sweep_delay = 0; - Gb_Env::reset(); + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (4 * clk_mul - 1)) + period(); + return result; } -void Gb_Square::clock_sweep() +inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) { - int sweep_period = (regs [0] & period_mask) >> 4; - if ( sweep_period && sweep_delay && !--sweep_delay ) + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) { - 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 - } + phase = 0x7FFF; + delay += 8 * clk_mul; } } -void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) +inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) { - if ( sweep_freq == 2048 ) - playing = false; + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used - 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 + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) { - // really high frequency results in DC at half volume - amp = volume >> 1; - playing = false; + sweep_freq = frequency(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; + if ( regs [0] & shift_mask ) + calc_sweep( 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 ) +void Gb_Wave::corrupt_wave() { - 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; + int pos = ((phase + 1) & (bank_size - 1)) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; } -// Gb_Wave - -inline void Gb_Wave::write_register( int reg, int data ) +inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) { + int const max_len = 256; + switch ( reg ) { case 0: - if ( !(data & 0x80) ) + if ( !dac_enabled() ) enabled = false; break; case 1: - length = 256 - regs [1]; - break; - - case 2: - volume = data >> 5 & 3; + length_ctr = max_len - data; break; case 4: - if ( data & trigger & regs [0] ) + bool was_enabled = enabled; + if ( write_trig( frame_phase, max_len, old_data ) ) { - wave_pos = 0; - enabled = true; - if ( length == 0 ) - length = 256; + if ( !dac_enabled() ) + enabled = false; + else if ( mode == Gb_Apu::mode_dmg && was_enabled && + (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) + corrupt_wave(); + + phase = 0; + delay = period() + 6 * clk_mul; } } } -void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) +void Gb_Apu::write_osc( int reg, int old_data, int data ) { - int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 - int frequency; + int index = (reg * 3 + 3) >> 4; // avoids divide + assert( index == reg / 5 ); + reg -= index * 5; + switch ( index ) { - int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - - frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 + case 0: square1.write_register( frame_phase, reg, old_data, data ); break; + case 1: square2.write_register( frame_phase, reg, old_data, data ); break; + case 2: wave .write_register( frame_phase, reg, old_data, data ); break; + case 3: noise .write_register( frame_phase, reg, old_data, data ); break; + } +} + +// Synthesis + +void Gb_Square::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc duty and phase + static byte const duty_offsets [4] = { 1, 1, 3, 7 }; + static byte const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int duty_offset = duty_offsets [duty_code]; + int duty = duties [duty_code]; + if ( mode == Gb_Apu::mode_agb ) + { + // AGB uses inverted duty + duty_offset -= duty; + duty = 8 - duty; + } + int ph = (this->phase + duty_offset) & 7; + + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) { - amp = 30 >> volume_shift & playing; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + // Play inaudible frequencies as constant amplitude + if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } } + update_amp( time, amp ); } + // Generate wave 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 const per = this->period(); + if ( !vol ) { - 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; + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif } - while ( time < end_time ); - - this->wave_pos = (wave_pos - 1) & (wave_size - 1); + else + { + // Output amplitude transitions + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + norm_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + this->phase = (ph - duty_offset) & 7; } delay = time - end_time; } -// Gb_Apu::write_osc - -void Gb_Apu::write_osc( int index, int reg, int data ) +#if !GB_APU_FAST +// Quickly runs LFSR for a large number of clocks. For use when noise is generating +// no sound. +static unsigned run_lfsr( unsigned s, unsigned mask, int count ) { - reg -= index * 5; - Gb_Square* sq = &square2; - switch ( index ) + bool const optimized = true; // set to false to use only unoptimized loop in middle + + // optimization used in several places: + // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) + + if ( mask == 0x4000 && optimized ) { - case 0: - sq = &square1; - case 1: - if ( sq->write_register( reg, data ) && index == 0 ) + if ( count >= 32767 ) + count %= 32767; + + // Convert from Fibonacci to Galois configuration, + // shifted left 1 bit + s ^= (s & 1) * 0x8000; + + // Each iteration is equivalent to clocking LFSR 255 times + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + // Each iteration is equivalent to clocking LFSR 15 times + // (interesting similarity to single clocking below) + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + + // Convert back to Fibonacci configuration + s &= 0x7FFF; + } + else if ( count < 8 || !optimized ) + { + // won't fully replace upper 8 bits, so have to do the unoptimized way + while ( --count >= 0 ) + s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); + } + else + { + if ( count > 127 ) { - square1.sweep_freq = square1.frequency(); - if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + count %= 127; + if ( !count ) + count = 127; // must run at least once + } + + // Need to keep one extra bit of history + s = s << 1 & 0xFF; + + // Convert from Fibonacci to Galois configuration, + // shifted left 2 bits + s ^= (s & 2) * 0x80; + + // Each iteration is equivalent to clocking LFSR 7 times + // (interesting similarity to single clocking below) + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + // Convert back to Fibonacci configuration and + // repeat last 8 bits above significant 7 + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; +} +#endif + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + if ( !(phase & 1) ) { - square1.sweep_delay = 1; // cause sweep to recalculate now - square1.clock_sweep(); + amp += vol; + vol = -vol; } } - break; - - case 2: - wave.write_register( reg, data ); - break; - - case 3: - if ( noise.write_register( reg, data ) ) - noise.bits = 0x7FFF; + + // AGB negates final output + if ( mode == Gb_Apu::mode_agb ) + { + vol = -vol; + amp = -amp; + } + + update_amp( time, amp ); } + + // Run timer and calculate time of next LFSR clock + static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * clk_mul; + + #if GB_APU_FAST + time += delay; + #else + { + int extra = (end_time - time) - delay; + int const per2 = this->period2(); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & period2_mask; + delay = count * period1 - extra; + } + #endif + + // Generate wave + if ( time < end_time ) + { + unsigned const mask = this->lfsr_mask(); + unsigned bits = this->phase; + + int per = period2( period1 * 8 ); + #if GB_APU_FAST + // Noise can be THE biggest time hog; adjust as necessary + int const min_period = 24; + if ( per < min_period ) + per = min_period; + #endif + if ( period2_index() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + time += (blip_time_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + this->phase = bits; + } + + #if GB_APU_FAST + delay = time - end_time; + #endif +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc volume +#if GB_APU_NO_AGB + static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 }; + int const volume_idx = regs [2] >> 5 & 3; + int const volume_shift = shifts [volume_idx]; + int const volume_mul = 1; +#else + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_shift = 2 + 4; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_mul = volumes [volume_idx]; +#endif + + // Determine what will be generated + int playing = false; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + // Play inaudible frequencies as constant amplitude + amp = 8 << 4; // really depends on average of all samples in wave + + // if delay is larger, constant amplitude won't start yet + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + { + if ( volume_mul && volume_shift != 4+4 ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> volume_shift) - dac_bias; + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + byte const* wave = this->wave_ram; + + // wave size and bank + #if GB_APU_NO_AGB + int const wave_mask = 0x1F; + int const swap_banks = 0; + #else + int const size20_mask = 0x20; + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & size20_mask) | 0x1F; + int swap_banks = 0; + if ( flags & bank40_mask ) + { + swap_banks = flags & size20_mask; + wave += bank_size/2 - (swap_banks >> 1); + } + #endif + + int ph = this->phase ^ swap_banks; + ph = (ph + 1) & wave_mask; // pre-advance + + int const per = this->period(); + if ( !playing ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int lamp = this->last_amp + dac_bias; + do + { + // Extract nibble + int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + // Scale by volume + int amp = (nibble * volume_mul) >> volume_shift; + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + this->last_amp = lamp - dac_bias; + } + ph = (ph - 1) & wave_mask; // undo pre-advance and mask position + + // Keep track of last byte read + if ( enabled ) + sample_buf = wave [ph >> 1]; + + this->phase = ph ^ swap_banks; // undo swapped banks + } + delay = time - end_time; } diff --git a/Frameworks/GME/gme/Gb_Oscs.h b/Frameworks/GME/gme/Gb_Oscs.h old mode 100755 new mode 100644 index d7f88ea14..fb29853e3 --- a/Frameworks/GME/gme/Gb_Oscs.h +++ b/Frameworks/GME/gme/Gb_Oscs.h @@ -1,83 +1,188 @@ // Private oscillators used by Gb_Apu -// Gb_Snd_Emu 0.1.5 +// Gb_Snd_Emu $vers #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 }; +#ifndef GB_APU_OVERCLOCK + #define GB_APU_OVERCLOCK 1 +#endif + +#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) + #error "GB_APU_OVERCLOCK must be a power of 2" +#endif + +class Gb_Osc { +protected: - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - BOOST::uint8_t* regs; // osc's 5 registers - - int delay; - int last_amp; - int volume; - int length; - int enabled; - - void reset(); - void clock_length(); + // 11-bit frequency in NRx3 and NRx4 int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } -}; - -struct Gb_Env : Gb_Osc -{ - int env_delay; + void update_amp( blip_time_t, int new_amp ); + int write_trig( int frame_phase, int max_len, int old_data ); +public: + + enum { clk_mul = GB_APU_OVERCLOCK }; + enum { dac_bias = 7 }; + + Blip_Buffer* outputs [4];// NULL, right, left, center + Blip_Buffer* output; // where to output sound + BOOST::uint8_t* regs; // osc's 5 registers + int mode; // mode_dmg, mode_cgb, mode_agb + int dac_off_amp;// amplitude when DAC is off + int last_amp; // current amplitude in Blip_Buffer + Blip_Synth_Norm const* norm_synth; + Blip_Synth_Fast const* fast_synth; + + int delay; // clocks until frequency timer expires + int length_ctr; // length counter + unsigned phase; // waveform phase (or equivalent) + bool enabled; // internal enabled flag + + void clock_length(); void reset(); - void clock_envelope(); - bool write_register( int, int ); }; -struct Gb_Square : Gb_Env -{ +class Gb_Env : public Gb_Osc { +public: + int env_delay; + int volume; + bool env_enabled; + + void clock_envelope(); + bool write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } +protected: + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [2] & 0xF8; } +private: + void zombie_volume( int old, int data ); + int reload_env_timer(); +}; + +class Gb_Square : public Gb_Env { +public: + bool write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) + } +private: + // Frequency timer period + int period() const { return (2048 - frequency()) * (4 * clk_mul); } +}; + +class Gb_Sweep_Square : public Gb_Square { +public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void clock_sweep(); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } +private: 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 ); + void calc_sweep( bool update ); + void reload_sweep_timer(); }; -struct Gb_Noise : Gb_Env -{ - typedef Blip_Synth Synth; - Synth const* synth; - unsigned bits; +class Gb_Noise : public Gb_Env { +public: - void run( blip_time_t, blip_time_t, int playing ); + int divider; // noise has more complex frequency divider setup + + void run( blip_time_t, blip_time_t ); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = 4 * clk_mul; // TODO: remove? + } +private: + enum { period2_mask = 0x1FFFF }; + + int period2_index() const { return regs [3] >> 4; } + int period2( int base = 8 ) const { return base << period2_index(); } + unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } }; -struct Gb_Wave : Gb_Osc -{ - typedef Blip_Synth Synth; - Synth const* synth; - int wave_pos; - enum { wave_size = 32 }; - BOOST::uint8_t wave [wave_size]; +class Gb_Wave : public Gb_Osc { +public: + int sample_buf; // last wave RAM byte read (hardware has this as well) - void write_register( int, int ); - void run( blip_time_t, blip_time_t, int playing ); + void write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + // Reads/writes wave RAM + int read( int addr ) const; + void write( int addr, int data ); + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + +private: + enum { bank40_mask = 0x40 }; + enum { bank_size = 32 }; + + int agb_mask; // 0xFF if AGB features enabled, 0 otherwise + BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU + + friend class Gb_Apu; + + // Frequency timer period + int period() const { return (2048 - frequency()) * (2 * clk_mul); } + + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [0] & 0x80; } + + void corrupt_wave(); + + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + + // Wave index that would be accessed, or -1 if no access would occur + int access( int addr ) const; }; -inline void Gb_Env::reset() +inline int Gb_Wave::read( int addr ) const { - env_delay = 0; - Gb_Osc::reset(); + int index = access( addr ); + return (index < 0 ? 0xFF : wave_bank() [index]); +} + +inline void Gb_Wave::write( int addr, int data ) +{ + int index = access( addr ); + if ( index >= 0 ) + wave_bank() [index] = data;; } #endif diff --git a/Frameworks/GME/gme/Gbs_Emu.cpp b/Frameworks/GME/gme/Gbs_Emu.cpp old mode 100755 new mode 100644 index 30a147e5f..383066e03 --- a/Frameworks/GME/gme/Gbs_Emu.cpp +++ b/Frameworks/GME/gme/Gbs_Emu.cpp @@ -1,288 +1,167 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 */ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Emu.h" + +/* Copyright (C) 2003-2009 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 = { -47.0, 2000 }; -Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 }; +Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::cgb_eq = { 0.0, 300, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30, 0,0,0,0,0,0,0,0 }; // DMG 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 ); + sound_hardware = sound_gbs; + enable_clicking( false ); + set_type( gme_gbs_type ); + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); set_gain( 1.2 ); - - static equalizer_t const eq = { -1.0, 120 }; + + // kind of midway between headphones and speaker + static equalizer_t const eq = { -1.0, 120, 0,0,0,0,0,0,0,0 }; set_equalizer( eq ); } -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; } - -gme_type_t_ const gme_gbs_type [1] = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }; - -// 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. - //dprintf( "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 BOOST::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] ); - - cpu::reset( rom.unmapped() ); - - unsigned load_addr = get_le16( header_.load_addr ); - cpu::rst_base = load_addr; - rom.set_addr( load_addr ); - - 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 ) - { - dprintf( "PC wrapped around\n" ); - cpu::r.pc &= 0xFFFF; - } - else - { - set_warning( "Emulation error (illegal/unsupported instruction)" ); - dprintf( "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; -} +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + core_.unload(); + 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 ); +} + +static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); + out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); + out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); + out.hash_( data, data_size ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header(), out ); + return blargg_ok; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t const* h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Gbs_Emu::header_t * ) begin; + + set_track_count( h->track_count ); + if ( !h->valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } +static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } + +gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; + +// Setup + +blargg_err_t Gbs_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_warning( core_.warning() ); + set_track_count( header().track_count ); + set_voice_count( Gb_Apu::osc_count ); + core_.apu().volume( gain() ); + + 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+3, mixed_type+1 + }; + set_voice_types( types ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.apu().treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + core_.apu().set_output( i, c, l, r ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + sound_t mode = sound_hardware; + if ( mode == sound_gbs ) + mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; + + RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); + + // clear buffer AFTER track is started, eliminating initial click + return Classic_Emu::start_track_( track ); +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core_.end_frame( duration ); +} + +blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const +{ + hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Gbs_Emu.h b/Frameworks/GME/gme/Gbs_Emu.h old mode 100755 new mode 100644 index 93fe691e5..b070582b7 --- a/Frameworks/GME/gme/Gbs_Emu.h +++ b/Frameworks/GME/gme/Gbs_Emu.h @@ -1,88 +1,63 @@ // Nintendo Game Boy GBS music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef GBS_EMU_H #define GBS_EMU_H #include "Classic_Emu.h" -#include "Gb_Apu.h" -#include "Gb_Cpu.h" +#include "Gbs_Core.h" -class Gbs_Emu : private Gb_Cpu, public Classic_Emu { - typedef Gb_Cpu cpu; +class Gbs_Emu : public Classic_Emu { public: - // Equalizer profiles for Game Boy Color speaker and headphones + // Equalizer profiles for Game Boy speaker and headphones static equalizer_t const handheld_eq; static equalizer_t const headphones_eq; + static equalizer_t const cgb_eq; // Game Boy Color headphones have less bass - // 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]; - }; + // GBS file header (see Gbs_Core.h) + typedef Gbs_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } + header_t const& header() const { return core_.header(); } + + // Selects which sound hardware to use. AGB hardware is cleaner than the + // others. Doesn't take effect until next start_track(). + enum sound_t { + sound_dmg = Gb_Apu::mode_dmg, // Game Boy monochrome + sound_cgb = Gb_Apu::mode_cgb, // Game Boy Color + sound_agb = Gb_Apu::mode_agb, // Game Boy Advance + sound_gbs // Use DMG/CGB based on GBS (default) + }; + void set_sound( sound_t s ) { sound_hardware = s; } + + // If true, makes APU more accurate, which results in more clicking. + void enable_clicking( bool enable = true ) { core_.apu().reduce_clicks( !enable ); } static gme_type_t static_type() { return gme_gbs_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } + Gbs_Core& core() { return core_; } + + blargg_err_t hash_( Hash_Function& ) const; + +// Internal 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(); + // Overrides + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual 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 ); + sound_t sound_hardware; + Gbs_Core core_; }; #endif diff --git a/Frameworks/GME/gme/Gme_File.cpp b/Frameworks/GME/gme/Gme_File.cpp old mode 100755 new mode 100644 index 6821c3a56..377f04e76 --- a/Frameworks/GME/gme/Gme_File.cpp +++ b/Frameworks/GME/gme/Gme_File.cpp @@ -1,216 +1,183 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 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; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gme_File.h" + +/* Copyright (C) 2003-2008 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 Gme_File::unload() +{ + clear_playlist(); // BEFORE clearing track count + track_count_ = 0; + raw_track_count_ = 0; + Gme_Loader::unload(); +} + +Gme_File::Gme_File() +{ + type_ = NULL; + user_data_ = NULL; + user_cleanup_ = NULL; + Gme_File::unload(); // clears fields +} + +Gme_File::~Gme_File() +{ + if ( user_cleanup_ ) + user_cleanup_( user_data_ ); +} + +blargg_err_t Gme_File::post_load() +{ + if ( !track_count() ) + set_track_count( type()->track_count ); + return Gme_Loader::post_load(); +} + +void Gme_File::clear_playlist() +{ + playlist.clear(); + clear_playlist_(); + track_count_ = raw_track_count_; +} + +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 BLARGG_ERR( BLARGG_ERR_CALLER, "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; + // TODO: really needs to be removed? + //if ( !(type_->flags_ & 0x02) ) + // *track_io -= e.decimal_track; + } + if ( *track_io >= raw_track_count_ ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "invalid track in m3u playlist" ); + } + else + { + check( !playlist.size() ); + } + return blargg_ok; +} + +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->fade_length = -1; + out->play_length = -1; + out->repeat_count = -1; + out->song [0] = 0; + out->game [0] = 0; + out->author [0] = 0; + out->composer [0] = 0; + out->engineer [0] = 0; + out->sequencer [0] = 0; + out->tagger [0] = 0; + out->copyright [0] = 0; + out->date [0] = 0; + out->comment [0] = 0; + out->dumper [0] = 0; + out->system [0] = 0; + out->disc [0] = 0; + out->track [0] = 0; + out->ost [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.artist ); + copy_field_( out->engineer , i.engineer ); + copy_field_( out->composer , i.composer ); + copy_field_( out->sequencer, i.sequencer ); + copy_field_( out->copyright, i.copyright ); + copy_field_( out->dumper , i.ripping ); + copy_field_( out->tagger , i.tagging ); + copy_field_( out->date , i.date ); + + M3u_Playlist::entry_t const& e = playlist [track]; + if ( e.length >= 0 ) out->length = e.length; + if ( e.intro >= 0 ) out->intro_length = e.intro; + if ( e.loop >= 0 ) out->loop_length = e.loop; + if ( e.fade >= 0 ) out->fade_length = e.fade; + if ( e.repeat >= 0 ) out->repeat_count = e.repeat; + copy_field_( out->song, e.name ); + } + + // play_length + out->play_length = out->length; + if ( out->play_length <= 0 ) + { + out->play_length = out->intro_length + 2 * out->loop_length; // intro + 2 loops + if ( out->play_length <= 0 ) + out->play_length = 150 * 1000; // 2.5 minutes + } + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Gme_File.h b/Frameworks/GME/gme/Gme_File.h old mode 100755 new mode 100644 index a535633bc..8f9e86e32 --- a/Frameworks/GME/gme/Gme_File.h +++ b/Frameworks/GME/gme/Gme_File.h @@ -1,145 +1,153 @@ -// Common interface to game music file loading and information - -// Game_Music_Emu 0.5.2 -#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_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 BOOST::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 - #ifdef HAVE_ZLIB_H - #define GME_FILE_READER Gzip_File_Reader - #else - #define GME_FILE_READER Std_File_Reader - #endif -#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 +// Common interface for track information + +// Game_Music_Emu $vers +#ifndef GME_FILE_H +#define GME_FILE_H + +#include "gme.h" +#include "Gme_Loader.h" +#include "M3u_Playlist.h" + +struct track_info_t +{ + int track_count; + + /* 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 */ + int fade_length; + int repeat_count; + + /* Length if available, otherwise intro_length+loop_length*2 if available, + otherwise a default of 150000 (2.5 minutes). */ + int play_length; + + /* empty string if not available */ + char system [256]; + char game [256]; + char song [256]; + char author [256]; + char composer [256]; + char engineer [256]; + char sequencer [256]; + char tagger [256]; + char copyright [256]; + char date [256]; + char comment [256]; + char dumper [256]; + char disc [256]; + char track [256]; + char ost [256]; +}; +enum { gme_max_field = 255 }; + +class Gme_File : public Gme_Loader { +public: + // Type of emulator. For example if this returns gme_nsfe_type, this object + // is an NSFE emulator, and you can downcast to an Nsfe_Emu* if necessary. + gme_type_t type() const; + + // Loads 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(); + + // Number of tracks or 0 if no file has been loaded + int track_count() const; + + // Gets 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 + + // Sets/gets 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_; } + + // Registers 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: + Gme_File(); + ~Gme_File(); + +protected: + // Services + void set_type( gme_type_t t ) { type_ = t; } + void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } + + // Must be overridden + virtual blargg_err_t track_info_( track_info_t* out, int track ) const BLARGG_PURE( ; ) + + // Optionally overridden + virtual void clear_playlist_() { } + +protected: // Gme_Loader overrides + virtual void unload(); + virtual blargg_err_t post_load(); + +protected: + blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu +private: + gme_type_t type_; + void* user_data_; + gme_user_cleanup_t user_cleanup_; + int track_count_; + int raw_track_count_; + M3u_Playlist playlist; + char playlist_warning [64]; + + blargg_err_t load_m3u_( blargg_err_t ); + +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 ); +}; + +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 (C++ only) */ + Music_Emu* (*new_info)();/* Create new info reader for this type (C++ only) */ + + /* internal */ + const char* extension_; + int flags_; +}; + +/* Emulator type constants for each supported file type */ +extern const gme_type_t_ + gme_ay_type [1], + gme_gbs_type [1], + gme_gym_type [1], + gme_hes_type [1], + gme_kss_type [1], + gme_nsf_type [1], + gme_nsfe_type [1], + gme_sap_type [1], + gme_sfm_type [1], + gme_sgc_type [1], + gme_spc_type [1], + gme_vgm_type [1], + gme_vgz_type [1]; + +#define GME_COPY_FIELD( in, out, name ) \ + { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } + +inline gme_type_t Gme_File::type() const { return type_; } + +inline int Gme_File::track_count() const { return track_count_; } + +inline blargg_err_t Gme_File::track_info_( track_info_t*, int ) const { return blargg_ok; } + +#endif diff --git a/Frameworks/GME/gme/Gym_Emu.cpp b/Frameworks/GME/gme/Gym_Emu.cpp old mode 100755 new mode 100644 index 499a9ca2a..bcac416c3 --- a/Frameworks/GME/gme/Gym_Emu.cpp +++ b/Frameworks/GME/gme/Gym_Emu.cpp @@ -1,379 +1,428 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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; } - -gme_type_t_ const gme_gym_type [1] = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; - -// 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; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gym_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 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 = 5 / 3.0; +double const fm_gain = 3.0; + +int const base_clock = 53700300; +int const clock_rate = base_clock / 15; + +Gym_Emu::Gym_Emu() +{ + resampler.set_callback( play_frame_, this ); + pos = NULL; + disable_oversampling_ = false; + set_type( gme_gym_type ); + set_silence_lookahead( 1 ); // tracks should already be trimmed + pcm_buf = stereo_buf.center(); +} + +Gym_Emu::~Gym_Emu() { } + +// Track info + +static void get_gym_info( Gym_Emu::header_t const& h, int length, track_info_t* out ) +{ + if ( 0 != memcmp( h.tag, "GYMX", 4 ) ) + return; + + length = length * 50 / 3; // 1000 / 60 + int 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 blank + 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 ); +} + +static void hash_gym_file( Gym_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.loop_start[0], sizeof(h.loop_start) ); + out.hash_( &h.packed[0], sizeof(h.packed) ); + out.hash_( data, data_size ); +} + +static int gym_track_length( byte const p [], byte const* end ) +{ + int 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; +} + +blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const +{ + get_gym_info( header_, gym_track_length( log_begin(), file_end() ), out ); + return blargg_ok; +} + +static blargg_err_t check_header( byte const in [], int size, int* data_offset = NULL ) +{ + if ( size < 4 ) + return blargg_err_file_type; + + if ( memcmp( in, "GYMX", 4 ) == 0 ) + { + if ( size < Gym_Emu::header_t::size + 1 ) + return blargg_err_file_type; + + if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "packed GYM file" ); + + if ( data_offset ) + *data_offset = Gym_Emu::header_t::size; + } + else if ( *in > 3 ) + { + return blargg_err_file_type; + } + + return blargg_ok; +} + +struct Gym_File : Gme_Info_ +{ + int data_offset; + + Gym_File() { set_type( gme_gym_type ); } + + blargg_err_t load_mem_( byte const in [], int size ) + { + data_offset = 0; + return check_header( in, size, &data_offset ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + int length = gym_track_length( &file_begin() [data_offset], file_end() ); + get_gym_info( *(Gym_Emu::header_t const*) file_begin(), length, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + Gym_Emu::header_t const* h = ( Gym_Emu::header_t const* ) file_begin(); + byte const* data = &file_begin() [data_offset]; + + hash_gym_file( *h, data, file_end() - data, out ); + + return blargg_ok; + } +}; + +static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } +static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } + +gme_type_t_ const gme_gym_type [1] = {{ "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }}; + +// Setup + +blargg_err_t Gym_Emu::set_sample_rate_( int sample_rate ) +{ + blip_eq_t eq( -32, 8000, sample_rate ); + apu.treble_eq( eq ); + pcm_synth.treble_eq( eq ); + + apu.volume( 0.135 * fm_gain * gain() ); + + double factor = oversample; + if ( disable_oversampling_ ) + factor = (double) base_clock / 7 / 144 / sample_rate; + RETURN_ERR( resampler.setup( factor, 0.990, fm_gain * gain() ) ); + factor = resampler.rate(); + double fm_rate = sample_rate * factor; + + RETURN_ERR( stereo_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); + stereo_buf.clock_rate( clock_rate ); + + RETURN_ERR( fm.set_rate( fm_rate, base_clock / 7.0 ) ); + RETURN_ERR( resampler.reset( (int) (1.0 / 60 / min_tempo * sample_rate) ) ); + + return blargg_ok; +} + +void Gym_Emu::set_tempo_( double t ) +{ + if ( t < min_tempo ) + { + set_tempo( min_tempo ); + return; + } + + if ( stereo_buf.sample_rate() ) + { + double denom = tempo() * 60; + clocks_per_frame = (int) (clock_rate / denom); + resampler.resize( (int) (sample_rate() / denom) ); + } +} + +void Gym_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + fm.mute_voices( mask ); + apu.set_output( (mask & 0x80) ? 0 : stereo_buf.center() ); + pcm_synth.volume( (mask & 0x40) ? 0.0 : 0.125 / 256 * fm_gain * gain() ); +} + +blargg_err_t Gym_Emu::load_mem_( byte const in [], int size ) +{ + assert( offsetof (header_t,packed [4]) == header_t::size ); + log_offset = 0; + RETURN_ERR( check_header( in, size, &log_offset ) ); + + loop_begin = NULL; + + static const char* const names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + set_voice_names( names ); + + set_voice_count( 8 ); + + if ( log_offset ) + header_ = *(header_t const*) in; + else + memset( &header_, 0, sizeof header_ ); + + return blargg_ok; +} + +// Emulation + +blargg_err_t Gym_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + + pos = log_begin(); + loop_remain = get_le32( header_.loop_start ); + + prev_pcm_count = 0; + pcm_enabled = 0; + pcm_amp = -1; + + fm.reset(); + apu.reset(); + stereo_buf.clear(); + resampler.clear(); + pcm_buf = stereo_buf.center(); + return blargg_ok; +} + +void Gym_Emu::run_pcm( byte const pcm_in [], int pcm_count ) +{ + // Guess beginning and end of sample and adjust rate and buffer position accordingly. + + // count dac samples in next frame + int next_pcm_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_pcm_count++; + } + + // detect beginning and end of sample + int rate_count = pcm_count; + int start = 0; + if ( !prev_pcm_count && next_pcm_count && pcm_count < next_pcm_count ) + { + rate_count = next_pcm_count; + start = next_pcm_count - pcm_count; + } + else if ( prev_pcm_count && !next_pcm_count && pcm_count < prev_pcm_count ) + { + rate_count = prev_pcm_count; + } + + // Evenly space samples within buffer section being used + blip_resampled_time_t period = pcm_buf->resampled_duration( clocks_per_frame ) / rate_count; + + blip_resampled_time_t time = pcm_buf->resampled_time( 0 ) + period * start + (unsigned) period / 2; + + int pcm_amp = this->pcm_amp; + if ( pcm_amp < 0 ) + pcm_amp = pcm_in [0]; + + for ( int i = 0; i < pcm_count; i++ ) + { + int delta = pcm_in [i] - pcm_amp; + pcm_amp += delta; + pcm_synth.offset_resampled( time, delta, pcm_buf ); + time += period; + } + this->pcm_amp = pcm_amp; + pcm_buf->set_modified(); +} + +void Gym_Emu::parse_frame() +{ + byte pcm [1024]; // all PCM writes for frame + int pcm_size = 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 ) + { + pcm [pcm_size] = data2; + if ( pcm_size < (int) sizeof pcm - 1 ) + pcm_size += pcm_enabled; + } + else + { + if ( data == 0x2B ) + pcm_enabled = data2 >> 7 & 1; + + fm.write0( data, data2 ); + } + } + else if ( cmd == 2 ) + { + int data2 = *pos++; + if ( data == 0xB6 ) + { + Blip_Buffer * pcm_buf = NULL; + switch ( data2 >> 6 ) + { + case 0: pcm_buf = NULL; break; + case 1: pcm_buf = stereo_buf.right(); break; + case 2: pcm_buf = stereo_buf.left(); break; + case 3: pcm_buf = stereo_buf.center(); break; + } + /*if ( this->pcm_buf != pcm_buf ) + { + if ( this->pcm_buf ) pcm_synth.offset_inline( 0, -pcm_amp, this->pcm_buf ); + if ( pcm_buf ) pcm_synth.offset_inline( 0, pcm_amp, pcm_buf ); + }*/ + this->pcm_buf = pcm_buf; + } + fm.write1( data, data2 ); + } + 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 + } + } + + if ( pos >= file_end() ) + { + // Reached end + check( pos == file_end() ); + + if ( loop_begin ) + pos = loop_begin; + else + set_track_ended(); + } + this->pos = pos; + + // PCM + if ( pcm_buf && pcm_size ) + run_pcm( pcm, pcm_size ); + prev_pcm_count = pcm_size; +} + +inline 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; +} + +int Gym_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) +{ + return STATIC_CAST(Gym_Emu*,p)->play_frame( a, b, c ); +} + +blargg_err_t Gym_Emu::play_( int count, sample_t out [] ) +{ + resampler.dual_play( count, out, stereo_buf ); + return blargg_ok; +} + +blargg_err_t Gym_Emu::hash_( Hash_Function& out ) const +{ + hash_gym_file( header(), log_begin(), file_end() - log_begin(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Gym_Emu.h b/Frameworks/GME/gme/Gym_Emu.h old mode 100755 new mode 100644 index 05419ea20..cd28a29cb --- a/Frameworks/GME/gme/Gym_Emu.h +++ b/Frameworks/GME/gme/Gym_Emu.h @@ -1,82 +1,88 @@ -// Sega Genesis/Mega Drive GYM music file emulator -// Includes with PCM timing recovery to improve sample quality. - -// Game_Music_Emu 0.5.2 -#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 - 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 +// Sega Genesis/Mega Drive GYM music file emulator +// Performs PCM timing recovery to improve sample quality. + +// Game_Music_Emu $vers +#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 { +public: + + // GYM file header (optional; many files have NO header at all) + struct header_t + { + enum { size = 428 }; + + 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; } + + // Disables 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; } + + blargg_err_t hash_( Hash_Function& ) const; + +// Implementation +public: + Gym_Emu(); + ~Gym_Emu(); + +protected: + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int count, sample_t [] ); + virtual void mute_voices_( int ); + virtual void set_tempo_( double ); + +private: + // Log + byte const* pos; // current position + byte const* loop_begin; + int log_offset; // size of header (0 or header_t::size) + int loop_remain; // frames remaining until loop_begin has been located + int clocks_per_frame; + + bool disable_oversampling_; + + // PCM + int pcm_amp; + int prev_pcm_count; // for detecting beginning/end of group of samples + int pcm_enabled; + + // large objects + Dual_Resampler resampler; + Stereo_Buffer stereo_buf; + Blip_Buffer * pcm_buf; + Ym2612_Emu fm; + Sms_Apu apu; + Blip_Synth_Fast pcm_synth; + header_t header_; + + byte const* log_begin() const { return file_begin() + log_offset; } + void parse_frame(); + void run_pcm( byte const in [], int count ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ); + static int play_frame_( void*, blip_time_t, int, sample_t [] ); +}; + +#endif diff --git a/Frameworks/GME/gme/Hes_Apu.cpp b/Frameworks/GME/gme/Hes_Apu.cpp old mode 100755 new mode 100644 index 223891212..edbbf1f80 --- a/Frameworks/GME/gme/Hes_Apu.cpp +++ b/Frameworks/GME/gme/Hes_Apu.cpp @@ -1,10 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Hes_Apu.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 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 @@ -21,17 +19,15 @@ bool const center_waves = true; // reduces asymmetry and clamping when starting Hes_Apu::Hes_Apu() { - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - osc->outputs [0] = 0; - osc->outputs [1] = 0; - osc->chans [0] = 0; - osc->chans [1] = 0; - osc->chans [2] = 0; + osc->output [0] = NULL; + osc->output [1] = NULL; + osc->outputs [0] = NULL; + osc->outputs [1] = NULL; + osc->outputs [2] = NULL; } - while ( osc != oscs ); reset(); } @@ -41,150 +37,192 @@ void Hes_Apu::reset() latch = 0; balance = 0xFF; - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - memset( osc, 0, offsetof (Hes_Osc,outputs) ); - osc->noise_lfsr = 1; - osc->control = 0x40; - osc->balance = 0xFF; + memset( osc, 0, offsetof (Osc,output) ); + osc->lfsr = 0; + 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 ); + // Only last two oscs support noise + oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR + oscs [osc_count - 1].lfsr = 0x200C3; } -void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) +void Hes_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values - if ( osc_outputs_0 && control & 0x80 ) + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) { - int dac = this->dac; - - int const volume_0 = volume [0]; + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = center; + o.outputs [1] = left; + o.outputs [2] = right; + balance_changed( o ); +} + +void Hes_Apu::run_osc( Blip_Synth_Fast& syn, Osc& o, blip_time_t end_time ) +{ + int vol0 = o.volume [0]; + int vol1 = o.volume [1]; + int dac = o.dac; + + Blip_Buffer* out0 = o.output [0]; // cache often-used values + Blip_Buffer* out1 = o.output [1]; + if ( !(o.control & 0x80) ) + out0 = NULL; + + if ( out0 ) + { + // Update amplitudes + if ( out1 ) { - int delta = dac * volume_0 - last_amp [0]; + int delta = dac * vol1 - o.last_amp [1]; if ( delta ) - synth_.offset( last_time, delta, osc_outputs_0 ); - osc_outputs_0->set_modified(); + { + syn.offset( o.last_time, delta, out1 ); + out1->set_modified(); + } + } + int delta = dac * vol0 - o.last_amp [0]; + if ( delta ) + { + syn.offset( o.last_time, delta, out0 ); + out0->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(); - } + // Don't generate if silent + if ( !(vol0 | vol1) ) + out0 = NULL; + } + + // Generate noise + int noise = 0; + if ( o.lfsr ) + { + noise = o.noise & 0x80; - blip_time_t time = last_time + delay; + blip_time_t time = o.last_time + o.noise_delay; if ( time < end_time ) { - if ( noise & 0x80 ) + int period = (~o.noise & 0x1F) * 128; + if ( !period ) + period = 64; + + if ( noise && out0 ) { - if ( volume_0 | volume_1 ) + unsigned lfsr = o.lfsr; + do { - // 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 ); + int new_dac = -(lfsr & 1); + lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); - this->noise_lfsr = noise_lfsr; - assert( noise_lfsr ); + int delta = (new_dac &= 0x1F) - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; } + while ( time < end_time ); + + if ( !lfsr ) + { + lfsr = 1; + check( false ); + } + o.lfsr = lfsr; + + out0->set_modified(); + if ( out1 ) + out1->set_modified(); } - else if ( !(control & 0x40) ) + else { - // 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) ) - // dprintf( "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 + // Maintain phase when silent + int count = (end_time - time + period - 1) / period; + time += count * period; + + // not worth it + //while ( count-- ) + // o.lfsr = (o.lfsr >> 1) ^ (0x30061 * (o.lfsr & 1)); } } - 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; + o.noise_delay = time - end_time; } - last_time = end_time; + + // Generate wave + blip_time_t time = o.last_time + o.delay; + if ( time < end_time ) + { + int phase = (o.phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = o.period * 2; + + if ( period >= 14 && out0 && !((o.control & 0x40) | noise) ) + { + do + { + int new_dac = o.wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; + } + while ( time < end_time ); + out0->set_modified(); + if ( out1 ) + out1->set_modified(); + } + else + { + // Maintain phase when silent + int count = end_time - time; + if ( !period ) + period = 1; + count = (count + period - 1) / period; + + phase += count; // phase will be masked below + time += count * period; + } + + // TODO: Find whether phase increments even when both volumes are zero. + // CAN'T simply check for out0 being non-NULL, since it could be NULL + // if channel is muted in player, but still has non-zero volume. + // City Hunter breaks when this check is removed. + if ( !(o.control & 0x40) && (vol0 | vol1) ) + o.phase = (phase - 1) & 0x1F; // undo pre-advance + } + o.delay = time - end_time; + check( o.delay >= 0 ); + + o.last_time = end_time; + o.dac = dac; + o.last_amp [0] = dac * vol0; + o.last_amp [1] = dac * vol1; } -void Hes_Apu::balance_changed( Hes_Osc& osc ) +void Hes_Apu::balance_changed( 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) + #define ENTRY( factor ) short (factor * 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 ), @@ -204,27 +242,40 @@ void Hes_Apu::balance_changed( Hes_Osc& osc ) 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 ) + + // Separate balance into center volume and additional on either left or right + osc.output [0] = osc.outputs [0]; // center + osc.output [1] = osc.outputs [2]; // right + int base = log_table [left ]; + int side = log_table [right] - base; + if ( side < 0 ) { - osc.outputs [0] = osc.chans [1]; // left - osc.outputs [1] = osc.chans [2]; // right + base += side; + side = -side; + osc.output [1] = osc.outputs [1]; // left + } + + // Optimize when output is far left, center, or far right + if ( !base || osc.output [0] == osc.output [1] ) + { + base += side; + side = 0; + osc.output [0] = osc.output [1]; + osc.output [1] = NULL; + osc.last_amp [1] = 0; } if ( center_waves ) { - osc.last_amp [0] += (left - osc.volume [0]) * 16; - osc.last_amp [1] += (right - osc.volume [1]) * 16; + // TODO: this can leave a non-zero level in a buffer (minor) + osc.last_amp [0] += (base - osc.volume [0]) * 16; + osc.last_amp [1] += (side - osc.volume [1]) * 16; } - osc.volume [0] = left; - osc.volume [1] = right; + osc.volume [0] = base; + osc.volume [1] = side; } void Hes_Apu::write_data( blip_time_t time, int addr, int data ) @@ -239,20 +290,18 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) { balance = data; - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - osc->run_until( synth, time ); + run_osc( synth, *osc, time ); balance_changed( *oscs ); } - while ( osc != oscs ); } } else if ( latch < osc_count ) { - Hes_Osc& osc = oscs [latch]; - osc.run_until( synth, time ); + Osc& osc = oscs [latch]; + run_osc( synth, osc, time ); switch ( addr ) { case 0x802: @@ -289,8 +338,7 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) break; case 0x807: - if ( &osc >= &oscs [4] ) - osc.noise = data; + osc.noise = data; break; case 0x809: @@ -302,14 +350,12 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) void Hes_Apu::end_frame( blip_time_t end_time ) { - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; if ( end_time > osc->last_time ) - osc->run_until( synth, end_time ); - assert( osc->last_time >= end_time ); + run_osc( synth, *osc, end_time ); osc->last_time -= end_time; + check( osc->last_time >= 0 ); } - while ( osc != oscs ); } diff --git a/Frameworks/GME/gme/Hes_Apu.h b/Frameworks/GME/gme/Hes_Apu.h old mode 100755 new mode 100644 index ca0c932fd..e6f099cef --- a/Frameworks/GME/gme/Hes_Apu.h +++ b/Frameworks/GME/gme/Hes_Apu.h @@ -1,66 +1,87 @@ // Turbo Grafx 16 (PC Engine) PSG sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #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 ); +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); - enum { osc_count = 6 }; - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); +// More features + + // Resets sound chip void reset(); - enum { start_addr = 0x0800 }; - enum { end_addr = 0x0809 }; - void write_data( blip_time_t, int addr, int data ); + // Same as set_output(), but for a particular channel + enum { osc_count = 6 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - void end_frame( blip_time_t ); + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth.volume( 1.8 / osc_count / amp_range * v ); } + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0x0800 }; + enum { io_size = 10 }; + +// Implementation public: Hes_Apu(); + typedef BOOST::uint8_t byte; + private: - Hes_Osc oscs [osc_count]; + enum { amp_range = 0x8000 }; + struct Osc + { + byte wave [32]; + int delay; + int period; + int phase; + + int noise_delay; + byte noise; + unsigned lfsr; + + byte control; + byte balance; + byte dac; + short volume [2]; + int last_amp [2]; + + blip_time_t last_time; + Blip_Buffer* output [2]; + Blip_Buffer* outputs [3]; + }; + Osc oscs [osc_count]; int latch; int balance; - Hes_Osc::synth_t synth; + Blip_Synth_Fast synth; - void balance_changed( Hes_Osc& ); - void recalc_chans(); + void balance_changed( Osc& ); + static void run_osc( Blip_Synth_Fast&, Osc&, blip_time_t ); }; -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 ); } +inline void Hes_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} #endif diff --git a/Frameworks/GME/gme/Hes_Cpu.cpp b/Frameworks/GME/gme/Hes_Cpu.cpp old mode 100755 new mode 100644 index 2615a0bb9..5f70ad45f --- a/Frameworks/GME/gme/Hes_Cpu.cpp +++ b/Frameworks/GME/gme/Hes_Cpu.cpp @@ -1,12 +1,13 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Hes_Cpu.h" #include "blargg_endian.h" +#include "Hes_Core.h" //#include "hes_cpu_log.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -17,1287 +18,106 @@ 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 +#define PAGE HES_CPU_PAGE -// 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() +int Hes_Core::read_mem( addr_t addr ) { - 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(); + check( addr < 0x10000 ); + int result = *cpu.get_code( addr ); + if ( cpu.mmr [PAGE( addr )] == 0xFF ) + result = read_mem_( addr ); + return result; } -void Hes_Cpu::set_mmr( int reg, int bank ) +void Hes_Core::write_mem( addr_t addr, int data ) { - 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 ); + check( addr < 0x10000 ); + byte* out = write_pages [PAGE( addr )]; + if ( out ) + out [addr & (cpu.page_size - 1)] = data; + else if ( cpu.mmr [PAGE( addr )] == 0xFF ) + write_mem_( addr, data ); } -#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 )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Hes_Cpu::run( hes_time_t end_time ) +void Hes_Core::set_mmr( int page, int bank ) { - 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 - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 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 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + write_pages [page] = 0; + byte* data = rom.at_addr( bank * cpu.page_size ); + if ( bank >= 0x80 ) { - fuint8 temp = r.status; - SET_STATUS( temp ); + data = 0; + switch ( bank ) + { + case 0xF8: + data = ram; + break; + + case 0xF9: + case 0xFA: + case 0xFB: + data = &sgx [(bank - 0xF9) * cpu.page_size]; + break; + + default: + if ( bank != 0xFF ) + dprintf( "Unmapped bank $%02X\n", bank ); + data = rom.unmapped(); + goto end; + } + + write_pages [page] = data; } - - 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 ) dprintf( "%ld\n", count ); - count++; - */ - } - #endif +end: + cpu.set_mmr( page, bank, data ); +} - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - - uint8_t const* instr = s.code_map [pc >> page_shift]; - fuint8 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 - - fuint16 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 )\ +#define READ_FAST( addr, out ) \ {\ - fint16 offset = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) ) goto branch_not_taken;\ - pc = BOOST::uint16_t (pc + offset);\ - goto loop;\ + out = READ_CODE( addr );\ + if ( CPU.mmr [PAGE( addr )] == 0xFF )\ + {\ + FLUSH_TIME();\ + out = read_mem_( addr );\ + CACHE_TIME();\ + }\ } - 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: { - fuint16 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 - fuint16 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 ); - fuint16 addr = GET_ADDR() + x; - pc += 2; - CPU_READ_FAST( this, addr, TIME, nz ); - a = nz; - goto loop; - } - - case 0x9D:{// STA abs,X - fuint16 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 - fuint16 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 - - { - fuint16 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; - } - - { - fuint16 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 ); - fuint16 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 - fuint16 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; - - { - fuint16 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; - - { - fuint16 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 - fuint16 addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - fuint16 addr = GET_ADDR(); - pc += 2; - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - fuint16 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 - fuint16 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 */\ - fuint16 temp = READ_LOW( data ) + y;\ - PAGE_CROSS_PENALTY( temp );\ - data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - goto ptr##op;\ +#define WRITE_FAST( addr, data ) \ +{\ + int page = PAGE( addr );\ + byte* out = write_pages [page];\ + addr &= CPU.page_size - 1;\ + if ( out )\ + {\ + out [addr] = data;\ }\ - 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:\ + else if ( CPU.mmr [page] == 0xFF )\ + {\ FLUSH_TIME();\ - data = READ( data );\ + write_mem_( addr, 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 +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) write_mem( addr, data ) +#define WRITE_VDP( addr, data ) write_vdp( addr, data ) +#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done(); CACHE_TIME(); } +#define SET_MMR( reg, bank ) set_mmr( reg, bank ) - ARITH_ADDR_MODES( 0xE5 ) // SBC - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - if ( status & st_d ) - dprintf( "Decimal mode not supported\n" ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::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 +#define CPU cpu +#define IDLE_ADDR idle_addr - 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; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } +#define CPU_BEGIN \ +bool Hes_Core::run_cpu( time_t end_time )\ +{\ + cpu.set_end_time( end_time ); - 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 ) {\ - fuint8 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 - fuint8 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 - fuint8 temp = POP(); - fuint8 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 - fuint8 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: - dprintf( "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; - dprintf( "Delayed SEI not supported\n" ); // TODO: implement - goto loop; - } - -// Special - - case 0x53:{// TAM - fuint8 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 - fuint16 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 - dprintf( "CSL not supported\n" ); - illegal_encountered = true; - goto loop; - - case 0xD4: // CSH - goto loop; - - case 0xF4: { // SET - //fuint16 operand = GET_MSB(); - dprintf( "SET not handled\n" ); - //switch ( data ) - //{ - //} - illegal_encountered = true; - goto loop; - } - -// Block transfer - - { - fuint16 in_alt; - fint16 in_inc; - fuint16 out_alt; - fint16 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: - fuint16 in = GET_LE16( instr + 0 ); - fuint16 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 - fuint8 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: - assert( (unsigned) opcode <= 0xFF ); - dprintf( "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; - fuint8 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; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; + #include "Hes_Cpu_run.h" return illegal_encountered; } - diff --git a/Frameworks/GME/gme/Hes_Cpu.h b/Frameworks/GME/gme/Hes_Cpu.h old mode 100755 new mode 100644 index 9cb8b006b..b182390cc --- a/Frameworks/GME/gme/Hes_Cpu.h +++ b/Frameworks/GME/gme/Hes_Cpu.h @@ -1,124 +1,139 @@ // PC Engine CPU emulator for use with HES music files -// Game_Music_Emu 0.5.2 +// $package #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 = LONG_MAX / 2 + 1 }; - class Hes_Cpu { public: - typedef BOOST::uint8_t uint8_t; + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; void reset(); - enum { page_size = 0x2000 }; - enum { page_shift = 13 }; - enum { page_count = 8 }; - void set_mmr( int reg, int bank ); + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + enum { page_count = 0x10000 / page_size }; + void set_mmr( int reg, int bank, void const* code ); - uint8_t const* get_code( hes_addr_t ); + byte const* get_code( addr_t ); - uint8_t ram [page_size]; - - // not kept updated during a call to run() + // NOT kept updated during emulation. struct registers_t { BOOST::uint16_t pc; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; + byte a; + byte x; + byte y; + byte flags; + byte 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 ); + byte mmr [page_count + 1]; // 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; } + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } - hes_time_t irq_time() const { return irq_time_; } - void set_irq_time( hes_time_t ); + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } - hes_time_t end_time() const { return end_time_; } - void set_end_time( hes_time_t ); + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); - void end_frame( hes_time_t ); + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( 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 }; + // Subtracts t from all times + void end_frame( time_t t ); // 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; + + +// Implementation +public: + Hes_Cpu() { cpu_state = &cpu_state_; } + enum { irq_inhibit_mask = 0x04 }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + time_t base; + int 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_; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; +private: void set_code_page( int, void const* ); - inline int update_end_time( hes_time_t end, hes_time_t irq ); + inline void update_end_time( time_t end, time_t irq ); }; -inline BOOST::uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) +#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> Hes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define HES_CPU_OFFSET( addr ) (addr) +#else + #define HES_CPU_OFFSET( addr ) ((addr) & (Hes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Hes_Cpu::get_code( addr_t addr ) { - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr ); } -inline int Hes_Cpu::update_end_time( hes_time_t t, hes_time_t irq ) +inline void Hes_Cpu::update_end_time( time_t end, time_t irq ) { - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; } -inline void Hes_Cpu::set_irq_time( hes_time_t t ) +inline void Hes_Cpu::set_irq_time( time_t t ) { - state->time += update_end_time( end_time_, (irq_time_ = t) ); + irq_time_ = t; + update_end_time( end_time_, t ); } -inline void Hes_Cpu::set_end_time( hes_time_t t ) +inline void Hes_Cpu::set_end_time( time_t t ) { - state->time += update_end_time( (end_time_ = t), irq_time_ ); + end_time_ = t; + update_end_time( t, irq_time_ ); } -inline void Hes_Cpu::end_frame( hes_time_t t ) +inline void Hes_Cpu::end_frame( 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; + assert( cpu_state == &cpu_state_ ); + cpu_state_.base -= t; + if ( irq_time_ < future_time ) irq_time_ -= t; + if ( end_time_ < future_time ) end_time_ -= t; +} + +inline void Hes_Cpu::set_mmr( int reg, int bank, void const* code ) +{ + assert( (unsigned) reg <= page_count ); // allow page past end to be set + assert( (unsigned) bank < 0x100 ); + mmr [reg] = bank; + byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits ); + cpu_state->code_map [reg] = p; + cpu_state_.code_map [reg] = p; } #endif diff --git a/Frameworks/GME/gme/Hes_Emu.cpp b/Frameworks/GME/gme/Hes_Emu.cpp old mode 100755 new mode 100644 index fafb2666d..266c14394 --- a/Frameworks/GME/gme/Hes_Emu.cpp +++ b/Frameworks/GME/gme/Hes_Emu.cpp @@ -1,529 +1,192 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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; } - -gme_type_t_ const gme_hes_type [1] = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; - -// 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 - { - dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); - } - break; - - case 3: - dprintf( "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 - dprintf( "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: - dprintf( "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: - dprintf( "VDP read not supported: %d\n", addr ); - return 0; - - case 0x0C01: - //return timer.enabled; // TODO: remove? - case 0x0C00: - run_until( time ); - dprintf( "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: - dprintf( "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; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Hes_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2008 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" + +Hes_Emu::Hes_Emu() +{ + set_type( gme_hes_type ); + set_silence_lookahead( 6 ); + set_gain( 1.11 ); +} + +Hes_Emu::~Hes_Emu() { } + +void Hes_Emu::unload() +{ + core.unload(); + Music_Emu::unload(); +} + +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 < len && in [i]; i++ ) + if ( (unsigned) (in [i] - ' ') >= 0xFF - ' ' ) // 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 byte const* copy_hes_fields( byte const in [], track_info_t* out ) +{ + byte const* in_offset = in; + if ( *in_offset >= ' ' ) + { + in_offset = copy_field( in_offset, out->game ); + in_offset = copy_field( in_offset, out->author ); + in_offset = copy_field( in_offset, out->copyright ); + } + return in_offset ? in_offset : in; +} + +static void hash_hes_file( Hes_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.banks[0], sizeof(h.banks) ); + out.hash_( &h.data_size[0], sizeof(h.data_size) ); + out.hash_( &h.addr[0], sizeof(h.addr) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + out.hash_( data, Hes_Core::info_offset ); + + track_info_t temp; // GCC whines about passing a pointer to a temporary here + byte const* more_data = copy_hes_fields( data + Hes_Core::info_offset, &temp ); + out.hash_( more_data, data_size - ( more_data - data ) ); +} + +blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const +{ + copy_hes_fields( core.data() + core.info_offset, out ); + return blargg_ok; +} + +struct Hes_File : Gme_Info_ +{ + enum { fields_offset = Hes_Core::header_t::size + Hes_Core::info_offset }; + + union header_t { + Hes_Core::header_t header; + byte data [fields_offset + 0x30 * 3]; + } const* h; + + Hes_File() + { + set_type( gme_hes_type ); + } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( header_t const* ) begin; + + if ( !h->header.valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_hes_fields( h->data + fields_offset, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_hes_file( h->header, file_begin() + h->header.size, file_end() - file_begin() - h->header.size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } +static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } + +gme_type_t_ const gme_hes_type [1] = {{ "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }}; + +blargg_err_t Hes_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core.load( in ) ); + + static const char* const names [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2", "ADPCM" + }; + set_voice_names( names ); + + static int const types [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = { + wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2 + }; + set_voice_types( types ); + + set_voice_count( core.apu().osc_count + core.adpcm().osc_count ); + core.apu().volume( gain() ); + core.adpcm().volume( gain() ); + + return setup_buffer( 7159091 ); +} + +void Hes_Emu::update_eq( blip_eq_t const& eq ) +{ + core.apu().treble_eq( eq ); + core.adpcm().treble_eq( eq ); +} + +void Hes_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < core.apu().osc_count ) + core.apu().set_output( i, c, l, r ); + else if ( i == core.apu().osc_count ) + core.adpcm().set_output( 0, c, l, r ); +} + +void Hes_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Hes_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + return core.start_track( track ); +} + +blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) +{ + return core.end_frame( duration_ ); +} + +blargg_err_t Hes_Emu::hash_( Hash_Function& out ) const +{ + hash_hes_file( header(), core.data(), core.data_size(), out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Hes_Emu.h b/Frameworks/GME/gme/Hes_Emu.h old mode 100755 new mode 100644 index 9951eb6a2..50568da31 --- a/Frameworks/GME/gme/Hes_Emu.h +++ b/Frameworks/GME/gme/Hes_Emu.h @@ -1,94 +1,42 @@ // TurboGrafx-16/PC Engine HES music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef HES_EMU_H #define HES_EMU_H #include "Classic_Emu.h" -#include "Hes_Apu.h" -#include "Hes_Cpu.h" +#include "Hes_Core.h" -class Hes_Emu : private Hes_Cpu, public Classic_Emu { - typedef Hes_Cpu cpu; +class Hes_Emu : public Classic_Emu { 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]; - }; + + static gme_type_t static_type() { return gme_hes_type; } + + // HES file header (see Hes_Core.h) + typedef Hes_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_hes_type; } + header_t const& header() const { return core.header(); } + blargg_err_t hash_( Hash_Function& ) const; + +// Implementation public: Hes_Emu(); ~Hes_Emu(); + virtual void unload(); + 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(); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + 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 ); + Hes_Core core; }; #endif diff --git a/Frameworks/GME/gme/Kss_Cpu.cpp b/Frameworks/GME/gme/Kss_Cpu.cpp old mode 100755 new mode 100644 index 37c4a7241..8abffca1f --- a/Frameworks/GME/gme/Kss_Cpu.cpp +++ b/Frameworks/GME/gme/Kss_Cpu.cpp @@ -1,1706 +1,35 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -/* -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, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -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; - - typedef BOOST::int8_t int8_t; - - 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; - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 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 ) - - fuint8 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 - }; - - fuint16 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) - fuint16 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 = (BOOST::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: - fuint16 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) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 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 - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 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]; - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 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 ) {\ - fuint16 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 ) {\ - fuint16 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 ) {\ - fuint8 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 ) {\ - fuint8 result = read;\ - fuint8 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 ) {\ - fuint8 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 ) {\ - fuint8 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 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 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 - fuint8 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; - fuint16 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; - fuint16 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; - fuint16 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; - - fuint16 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 ); - dprintf( "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(); - dprintf( "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: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 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 - fuint16 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) - fuint16 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) - fuint8 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: - dprintf( "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 - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "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; -} +// $package. http://www.slack.net/~ant/ + +#include "Kss_Core.h" + +#include "blargg_endian.h" +//#include "z80_cpu_log.h" + +/* Copyright (C) 2006-2008 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 OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) +#define IN_PORT( addr ) cpu_in( TIME(), addr ) +#define WRITE_MEM( addr, data ) {FLUSH_TIME(); cpu_write( addr, data );} +#define IDLE_ADDR idle_addr +#define CPU cpu + +#define CPU_BEGIN \ +bool Kss_Core::run_cpu( time_t end_time )\ +{\ + cpu.set_end_time( end_time ); + + #include "Z80_Cpu_run.h" + + return warning; +} diff --git a/Frameworks/GME/gme/Kss_Emu.cpp b/Frameworks/GME/gme/Kss_Emu.cpp old mode 100755 new mode 100644 index 1b26ef0b8..e7643b50c --- a/Frameworks/GME/gme/Kss_Emu.cpp +++ b/Frameworks/GME/gme/Kss_Emu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Kss_Emu.h" #include "blargg_endian.h" -#include -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2009 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 @@ -18,397 +17,477 @@ 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; +#define IF_PTR( ptr ) if ( ptr ) (ptr) -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 ); +int const clock_rate = 3579545; + +#define FOR_EACH_APU( macro )\ +{\ + macro( sms.psg );\ + macro( sms.fm );\ + macro( msx.psg );\ + macro( msx.scc );\ + macro( msx.music );\ + macro( msx.audio );\ } -Kss_Emu::~Kss_Emu() { unload(); } +Kss_Emu::Kss_Emu() : + core( this ) +{ + #define ACTION( apu ) { core.apu = NULL; } + FOR_EACH_APU( ACTION ); + #undef ACTION + + set_type( gme_kss_type ); +} + +Kss_Emu::~Kss_Emu() +{ + unload(); +} + +inline void Kss_Emu::Core::unload() +{ + #define ACTION( ptr ) { delete (ptr); (ptr) = 0; } + FOR_EACH_APU( ACTION ); + #undef ACTION +} void Kss_Emu::unload() { - delete sn; - sn = 0; + core.unload(); Classic_Emu::unload(); } // Track info -static void copy_kss_fields( Kss_Emu::header_t const& h, track_info_t* out ) +static void copy_kss_fields( Kss_Core::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"; + + if ( h.device_flags & 0x01 ) + system = "Sega Mark III"; + } + else + { + if ( h.device_flags & 0x09 ) + system = "MSX + FM Sound"; } Gme_File::copy_field_( out->system, system ); } +static void hash_kss_file( Kss_Core::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.load_size[0], sizeof(h.load_size) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.first_bank, sizeof(h.first_bank) ); + out.hash_( &h.bank_mode, sizeof(h.bank_mode) ); + out.hash_( &h.extra_header, sizeof(h.extra_header) ); + out.hash_( &h.device_flags, sizeof(h.device_flags) ); + + out.hash_( data, data_size ); +} + blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const { - copy_kss_fields( header_, out ); - return 0; + copy_kss_fields( header(), out ); +// TODO: remove +//if ( msx.music ) strcpy( out->system, "msxmusic" ); +//if ( msx.audio ) strcpy( out->system, "msxaudio" ); +//if ( sms.fm ) strcpy( out->system, "fmunit" ); + return blargg_ok; } 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; + return blargg_err_file_type; + + return blargg_ok; } struct Kss_File : Gme_Info_ { - Kss_Emu::header_t header_; - + Kss_Emu::header_t const* header_; + Kss_File() { set_type( gme_kss_type ); } - - blargg_err_t load_( Data_Reader& in ) + + blargg_err_t load_mem_( byte const begin [], int size ) { - 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_ ); + header_ = ( Kss_Emu::header_t const* ) begin; + + if ( header_->tag [3] == 'X' && header_->extra_header == 0x10 ) + set_track_count( get_le16( header_->last_track ) + 1 ); + + return check_kss_header( header_ ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { - copy_kss_fields( header_, out ); - return 0; + copy_kss_fields( *header_, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_kss_file( *header_, file_begin() + Kss_Core::header_t::base_size, file_end() - file_begin() - Kss_Core::header_t::base_size, out ); + return blargg_ok; } }; static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } -gme_type_t_ const gme_kss_type [1] = { "MSX", 256, &new_kss_emu, &new_kss_file, "KSS", 0x03 }; +gme_type_t_ const gme_kss_type [1] = {{ + "MSX", + 256, + &new_kss_emu, + &new_kss_file, + "KSS", + 0x03 +}}; // Setup -void Kss_Emu::update_gain() +void Kss_Emu::Core::update_gain_() { - double g = gain() * 1.4; - if ( scc_accessed ) - g *= 1.5; - ay.volume( g ); - scc.volume( g ); - if ( sn ) - sn->volume( g ); + double g = emu.gain(); + if ( msx.music || msx.audio || sms.fm ) + { + g *= 0.3; + } + else + { + g *= 1.2; + if ( scc_accessed ) + g *= 1.4; + } + + #define ACTION( apu ) IF_PTR( apu )->volume( g ) + FOR_EACH_APU( ACTION ); + #undef ACTION +} + +static blargg_err_t new_opl_apu( Opl_Apu::type_t type, Opl_Apu** out ) +{ + check( !*out ); + CHECK_ALLOC( *out = BLARGG_NEW( Opl_Apu ) ); + blip_time_t const period = 72; + int const rate = clock_rate / period; + return (*out)->init( rate * period, rate, period, type ); } 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' ) + RETURN_ERR( core.load( in ) ); + set_warning( core.warning() ); + + set_track_count( get_le16( header().last_track ) + 1 ); + + core.scc_enabled = false; + if ( header().device_flags & 0x02 ) // Sega Master System { - if ( header_.extra_header ) + int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "Noise", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0 + }; + set_voice_types( types ); + + // sms.psg + set_voice_count( Sms_Apu::osc_count ); + check( !core.sms.psg ); + CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu ); + + // sms.fm + if ( header().device_flags & 0x01 ) { - header_.extra_header = 0; - set_warning( "Unknown data in header" ); + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) ); } - if ( header_.device_flags & ~0x0F ) + + } + else // MSX + { + int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, wave_type+0 + }; + set_voice_types( types ); + + // msx.psg + set_voice_count( Ay_Apu::osc_count ); + check( !core.msx.psg ); + CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu ); + + if ( header().device_flags & 0x10 ) + set_warning( "MSX stereo not supported" ); + + // msx.music + if ( header().device_flags & 0x01 ) { - header_.device_flags &= 0x0F; - set_warning( "Unknown data in header" ); + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) ); + } + + // msx.audio + if ( header().device_flags & 0x08 ) + { + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) ); + } + + if ( !(header().device_flags & 0x80) ) + { + if ( !(header().device_flags & 0x84) ) + core.scc_enabled = core.scc_enabled_true; + + // msx.scc + check( !core.msx.scc ); + CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu ); + + int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; + 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+1, wave_type+3, wave_type+2, + wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7, + }; + set_voice_types( types ); + + set_voice_count( osc_count ); } } - else + + set_silence_lookahead( 6 ); + if ( core.sms.fm || core.msx.music || core.msx.audio ) { - 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 ( !Opl_Apu::supported() ) + set_warning( "FM sound not supported" ); + else + set_silence_lookahead( 3 ); // Opl_Apu is really slow } - - 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 ); + #define ACTION( apu ) IF_PTR( core.apu )->treble_eq( eq ) + FOR_EACH_APU( ACTION ); + #undef ACTION } 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 ); -} + if ( core.sms.psg ) // Sega Master System + { + i -= core.sms.psg->osc_count; + if ( i < 0 ) + { + core.sms.psg->set_output( i + core.sms.psg->osc_count, center, left, right ); + return; + } -// Emulation + if ( core.sms.fm && i < core.sms.fm->osc_count ) + core.sms.fm->set_output( i, center, NULL, NULL ); + } + else if ( core.msx.psg ) // MSX + { + i -= core.msx.psg->osc_count; + if ( i < 0 ) + { + core.msx.psg->set_output( i + core.msx.psg->osc_count, center ); + return; + } + + if ( core.msx.scc && i < core.msx.scc->osc_count ) core.msx.scc ->set_output( i, center ); + if ( core.msx.music && i < core.msx.music->osc_count ) core.msx.music->set_output( i, center, NULL, NULL ); + if ( core.msx.audio && i < core.msx.audio->osc_count ) core.msx.audio->set_output( i, center, NULL, NULL ); + } +} 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); + int period = (header().device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); + core.set_play_period( (Kss_Core::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" ); - } - //dprintf( "load_size : $%X\n", load_size ); - //dprintf( "bank_size : $%X\n", bank_size ); - //dprintf( "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; + #define ACTION( apu ) IF_PTR( core.apu )->reset() + FOR_EACH_APU( ACTION ); + #undef ACTION + + core.scc_accessed = false; + core.update_gain_(); + + return core.start_track( track ); } -void Kss_Emu::set_bank( int logical, int physical ) +void Kss_Emu::Core::cpu_write_( addr_t addr, int data ) { - 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 ) ); - } -} + // TODO: SCC+ support -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; + + case 0xBFFE: // selects between mapping areas (we just always enable both) + if ( data == 0 || data == 0x20 ) + return; } - - int scc_addr = (addr & 0xDFFF) ^ 0x9800; - if ( scc_addr < scc.reg_count ) + + int scc_addr = (addr & 0xDFFF) - 0x9800; + if ( (unsigned) scc_addr < 0xB0 && msx.scc ) { scc_accessed = true; - scc.write( time(), scc_addr, data ); + //if ( (unsigned) (scc_addr - 0x90) < 0x10 ) + // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F + if ( scc_addr < Scc_Apu::reg_count ) + msx.scc->write( cpu.time(), addr, data ); return; } - + dprintf( "LD ($%04X),$%02X\n", addr, data ); } -void kss_cpu_write( Kss_Cpu* cpu, unsigned addr, int data ) +void Kss_Emu::Core::cpu_write( addr_t 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 ); + *cpu.write( addr ) = data; + if ( (addr & scc_enabled) == 0x8000 ) + cpu_write_( addr, data ); } -void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +void Kss_Emu::Core::cpu_out( time_t time, addr_t addr, int data ) { data &= 0xFF; - Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); switch ( addr & 0xFF ) { case 0xA0: - emu.ay_latch = data & 0x0F; + if ( msx.psg ) + msx.psg->write_addr( data ); return; - + case 0xA1: - GME_APU_HOOK( &emu, emu.ay_latch, data ); - emu.ay.write( time, emu.ay_latch, data ); + if ( msx.psg ) + msx.psg->write_data( time, data ); return; - + case 0x06: - if ( emu.sn && (emu.header_.device_flags & 0x04) ) + if ( sms.psg && (header().device_flags & 0x04) ) { - emu.sn->write_ggstereo( time, data ); + sms.psg->write_ggstereo( time, data ); return; } break; - + case 0x7E: case 0x7F: - if ( emu.sn ) + if ( sms.psg ) { - GME_APU_HOOK( &emu, 16, data ); - emu.sn->write_data( time, data ); + sms.psg->write_data( time, data ); return; } break; - + + #define OPL_WRITE_HANDLER( base, opl )\ + case base : if ( opl ) { opl->write_addr( data ); return; } break;\ + case base+1: if ( opl ) { opl->write_data( time, data ); return; } break; + + OPL_WRITE_HANDLER( 0x7C, msx.music ) + OPL_WRITE_HANDLER( 0xC0, msx.audio ) + OPL_WRITE_HANDLER( 0xF0, sms.fm ) + case 0xFE: - emu.set_bank( 0, data ); + 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 } - - dprintf( "OUT $%04X,$%02X\n", addr, data ); + + Kss_Core::cpu_out( time, addr, data ); } -int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) +int Kss_Emu::Core::cpu_in( time_t time, addr_t addr ) { - //Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); - //switch ( addr & 0xFF ) - //{ - //} - - dprintf( "IN $%04X\n", addr ); - return 0; + switch ( addr & 0xFF ) + { + case 0xC0: + case 0xC1: + if ( msx.audio ) + return msx.audio->read( time, addr & 1 ); + break; + + case 0xA2: + if ( msx.psg ) + return msx.psg->read(); + break; + + #ifndef NDEBUG + case 0xA8: // PPI + return 0; + #endif + } + + return Kss_Core::cpu_in( time, addr ); } -// Emulation +void Kss_Emu::Core::update_gain() +{ + if ( scc_accessed ) + { + dprintf( "SCC accessed\n" ); + update_gain_(); + } +} 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; + RETURN_ERR( core.end_frame( duration ) ); + + #define ACTION( apu ) IF_PTR( core.apu )->end_frame( duration ) + FOR_EACH_APU( ACTION ); + #undef ACTION + + return blargg_ok; } + +blargg_err_t Kss_Emu::hash_( Hash_Function& out ) const +{ + hash_kss_file( header(), core.rom_().begin(), core.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Kss_Emu.h b/Frameworks/GME/gme/Kss_Emu.h old mode 100755 new mode 100644 index 4d8463abd..cfc7d0730 --- a/Frameworks/GME/gme/Kss_Emu.h +++ b/Frameworks/GME/gme/Kss_Emu.h @@ -1,96 +1,79 @@ // MSX computer KSS music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef KSS_EMU_H #define KSS_EMU_H #include "Classic_Emu.h" +#include "Kss_Core.h" #include "Kss_Scc_Apu.h" -#include "Kss_Cpu.h" #include "Sms_Apu.h" #include "Ay_Apu.h" +#include "Opl_Apu.h" -class Kss_Emu : private Kss_Cpu, public Classic_Emu { - typedef Kss_Cpu cpu; +class Kss_Emu : public Classic_Emu { 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 { }; + // KSS file header (see Kss_Core.h) + typedef Kss_Core::header_t header_t; // Header for currently loaded file - composite_header_t const& header() const { return header_; } + header_t const& header() const { return core.header(); } + + blargg_err_t hash_( Hash_Function& ) const; static gme_type_t static_type() { return gme_kss_type; } + +// Implementation 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(); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual 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 - byte const* bank_data; - 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]; + struct Core; + friend struct Core; + struct Core : Kss_Core { + Kss_Emu& emu; + + // detection of tunes that use SCC so they can be made louder + bool scc_accessed; + + enum { scc_enabled_true = 0xC000 }; + unsigned scc_enabled; // 0 or 0xC000 + int ay_latch; + + struct { + Sms_Apu* psg; + Opl_Apu* fm; + } sms; + + struct { + Ay_Apu* psg; + Scc_Apu* scc; + Opl_Apu* music; + Opl_Apu* audio; + } msx; + + Core( Kss_Emu* e ) : emu( *e ) { } + + virtual void cpu_write( addr_t, int ); + virtual int cpu_in( time_t, addr_t ); + virtual void cpu_out( time_t, addr_t, int ); + virtual void update_gain(); + + void cpu_write_( addr_t addr, int data ); + void update_gain_(); + void unload(); + } core; }; #endif diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.cpp b/Frameworks/GME/gme/Kss_Scc_Apu.cpp old mode 100755 new mode 100644 index 1660ac3da..60c53293e --- a/Frameworks/GME/gme/Kss_Scc_Apu.cpp +++ b/Frameworks/GME/gme/Kss_Scc_Apu.cpp @@ -1,8 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Kss_Scc_Apu.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 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 @@ -17,79 +17,106 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // 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 inaudible_freq = 16384; int const wave_size = 0x20; +void Scc_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +void Scc_Apu::volume( double v ) +{ + synth.volume( 0.43 / osc_count / amp_range * v ); +} + +void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = osc_count; --i >= 0; ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +Scc_Apu::Scc_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + 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; + + blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0xA0 + index * 2] + 1; int volume = 0; - if ( regs [0x8F] & (1 << index) ) + if ( regs [0xAF] & (1 << index) ) { - blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + - inaudible_freq * 32) / (inaudible_freq * 16); + blip_time_t inaudible_period = (unsigned) (output->clock_rate() + + inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); if ( period > inaudible_period ) - volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); + volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15); } - + BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; - if ( index == osc_count - 1 ) - wave -= wave_size; // last two oscs share wave + /*if ( index == osc_count - 1 ) + wave -= wave_size; // last two oscs share same wave RAM*/ + { - int amp = wave [osc.phase] * volume; - int delta = amp - osc.last_amp; + int delta = wave [osc.phase] * volume - osc.last_amp; if ( delta ) { - osc.last_amp = amp; + osc.last_amp += delta; + output->set_modified(); synth.offset( last_time, delta, output ); } } - + blip_time_t time = last_time + osc.delay; if ( time < end_time ) { + int phase = osc.phase; if ( !volume ) { // maintain phase - blargg_long count = (end_time - time + period - 1) / period; - osc.phase = (osc.phase + count) & (wave_size - 1); - time += count * period; + int count = (end_time - time + period - 1) / period; + phase += count; // will be masked below + 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]; + int delta = wave [phase] - last_wave; phase = (phase + 1) & (wave_size - 1); - int delta = amp - last_wave; if ( delta ) { - last_wave = amp; - synth.offset( time, delta * volume, output ); + last_wave += delta; + synth.offset_inline( 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.last_amp = last_wave * volume; + output->set_modified(); + phase--; // undo pre-advance } + osc.phase = phase & (wave_size - 1); } osc.delay = time - end_time; } diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.h b/Frameworks/GME/gme/Kss_Scc_Apu.h old mode 100755 new mode 100644 index 03a6a1080..a48ff5396 --- a/Frameworks/GME/gme/Kss_Scc_Apu.h +++ b/Frameworks/GME/gme/Kss_Scc_Apu.h @@ -1,45 +1,53 @@ // Konami SCC sound chip emulator -// Game_Music_Emu 0.5.2 +// $package #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 ); +// Basics -// 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. + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to reg + enum { reg_count = 0xB0 }; // 0 <= reg < reg_count + void write( blip_time_t t, int reg, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip + void reset(); + + // Same as set_output(), but for a particular channel enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* ); - - // Set overall volume (default is 1.0) + void set_output( int chan, Blip_Buffer* ); + + // Set overall volume, where 1.0 is normal void volume( double ); - - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); - + + // Set treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +private: + // noncopyable + Scc_Apu( const Scc_Apu& ); + Scc_Apu& operator = ( const Scc_Apu& ); + + +// Implementation public: Scc_Apu(); + BLARGG_DISABLE_NOTHROW + private: enum { amp_range = 0x8000 }; struct osc_t @@ -52,16 +60,12 @@ private: osc_t oscs [osc_count]; blip_time_t last_time; unsigned char regs [reg_count]; - Blip_Synth synth; - + Blip_Synth_Fast 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 ) +inline void Scc_Apu::set_output( int index, Blip_Buffer* b ) { assert( (unsigned) index < osc_count ); oscs [index].output = b; @@ -69,38 +73,39 @@ inline void Scc_Apu::osc_output( int index, Blip_Buffer* b ) inline void Scc_Apu::write( blip_time_t time, int addr, int data ) { - assert( (unsigned) addr < reg_count ); + //assert( (unsigned) addr < reg_count ); + assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); run_until( time ); - regs [addr] = data; + + addr -= 0x9800; + if ( ( unsigned ) addr < 0x90 ) + { + if ( ( unsigned ) addr < 0x60 ) + regs [addr] = data; + else if ( ( unsigned ) addr < 0x80 ) + { + regs [addr] = regs[addr + 0x20] = data; + } + else if ( ( unsigned ) addr < 0x90 ) + { + regs [addr + 0x20] = data; + } + } + else + { + addr -= 0xB800 - 0x9800; + if ( ( unsigned ) addr < 0xB0 ) + 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/Frameworks/GME/gme/M3u_Playlist.cpp b/Frameworks/GME/gme/M3u_Playlist.cpp old mode 100755 new mode 100644 index 0a1475db3..cfcf4d94f --- a/Frameworks/GME/gme/M3u_Playlist.cpp +++ b/Frameworks/GME/gme/M3u_Playlist.cpp @@ -1,426 +1,476 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 ) ); } - -gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } - -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(); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "M3u_Playlist.h" +#include "Music_Emu.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" + +// 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 ) +{ + if ( !err ) + { + require( raw_track_count_ ); // file must be loaded first + 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 ) ); } + +gme_err_t gme_load_m3u( Music_Emu* me, const char path [] ) { return me->load_m3u( path ); } + +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 ( unsigned (*in - 1) <= ' ' - 1 ) + 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; + while ( *in == ':' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out * 60 + n; + } + *out *= 1000; + if ( *in == '.' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out + 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 = entry.length - entry.loop; + 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, char *& last_comment_value, bool first ) +{ + in = skip_white( in + 1 ); + const char* field = in; + if ( *field != '@' ) + 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 if ( !strcmp( "Game" , field ) ) info.title = text; + else if ( !strcmp( "Artist" , field ) ) info.artist = text; + else if ( !strcmp( "Copyright", field ) ) info.copyright = text; + else + text = 0; + if ( text ) + return; + *in = ':'; + } + } + else if ( *field == '@' ) + { + ++field; + in = (char*)field; + while ( *in && *in > ' ' ) + in++; + const char* text = skip_white( in ); + if ( *text ) + { + char saved = *in; + *in = 0; + if ( !strcmp( "TITLE" , field ) ) info.title = text; + else if ( !strcmp( "ARTIST", field ) ) info.artist = text; + else if ( !strcmp( "DATE", field ) ) info.date = text; + else if ( !strcmp( "COMPOSER", field ) ) info.composer = text; + else if ( !strcmp( "SEQUENCER", field ) ) info.sequencer = text; + else if ( !strcmp( "ENGINEER", field ) ) info.engineer = text; + else if ( !strcmp( "RIPPER", field ) ) info.ripping = text; + else if ( !strcmp( "TAGGER", field ) ) info.tagging = text; + else + text = 0; + if ( text ) + { + last_comment_value = (char*)text; + return; + } + *in = saved; + } + } + else if ( last_comment_value ) + { + size_t len = strlen( last_comment_value ); + last_comment_value[ len ] = ','; + last_comment_value[ len + 1 ] = ' '; + size_t field_len = strlen( field ); + memmove( last_comment_value + len + 2, field, field_len ); + last_comment_value[ len + 2 + field_len ] = 0; + return; + } + + if ( first ) + info.title = field; +} + +blargg_err_t M3u_Playlist::parse_() +{ + info_.title = ""; + info_.artist = ""; + info_.date = ""; + info_.composer = ""; + info_.sequencer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + info_.copyright = ""; + + 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(); + char* last_comment_value = 0; + while ( in < data.end() ) + { + // find end of line and terminate it + line++; + char* begin = in; + while ( *in != CR && *in != LF ) + { + if ( !*in ) + return blargg_err_file_type; + 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_, last_comment_value, 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; + } + else last_comment_value = 0; + } + if ( count <= 0 ) + return blargg_err_file_type; + + // Treat first comment as title only if another field is also specified + if ( !(info_.artist [0] | info_.composer [0] | info_.date [0] | info_.engineer [0] | info_.ripping [0] | info_.sequencer [0] | info_.tagging [0] | info_.copyright[0]) ) + info_.title = ""; + + return entries.resize( count ); +} + +blargg_err_t M3u_Playlist::parse() +{ + blargg_err_t err = parse_(); + if ( err ) + 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/Frameworks/GME/gme/M3u_Playlist.h b/Frameworks/GME/gme/M3u_Playlist.h old mode 100755 new mode 100644 index eda0dc89b..11d902a50 --- a/Frameworks/GME/gme/M3u_Playlist.h +++ b/Frameworks/GME/gme/M3u_Playlist.h @@ -1,67 +1,87 @@ -// M3U playlist file parser, with support for subtrack information - -// Game_Music_Emu 0.5.2 -#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 +// M3U playlist file parser, with support for subtrack information + +// Game_Music_Emu $vers +#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_; } + + // All string pointers point to valid string, or "" if not available + struct info_t + { + const char* title; + const char* artist; + const char* date; + const char* composer; + const char* sequencer; + const char* engineer; + const char* ripping; + const char* tagging; + const char* copyright; + }; + 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 is "TYPE", otherwise "" + const char* name; + bool decimal_track; // true if track was specified in decimal + // integers are -1 if not present + int track; + int length; // milliseconds + 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_(); + void clear_(); +}; + +inline void M3u_Playlist::clear_() +{ + info_.title = ""; + info_.artist = ""; + info_.date = ""; + info_.composer = ""; + info_.sequencer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + info_.copyright = ""; + entries.clear(); + data.clear(); +} + +inline void M3u_Playlist::clear() +{ + first_error_ = 0; + clear_(); +} + +#endif diff --git a/Frameworks/GME/gme/Multi_Buffer.cpp b/Frameworks/GME/gme/Multi_Buffer.cpp old mode 100755 new mode 100644 index ecd8f8add..b1d3c5fd0 --- a/Frameworks/GME/gme/Multi_Buffer.cpp +++ b/Frameworks/GME/gme/Multi_Buffer.cpp @@ -1,232 +1,290 @@ -// 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 ( unsigned 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 ( unsigned 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; - //dprintf( "%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 ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - BLIP_READER_NEXT( center, bass ); - if ( (BOOST::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 ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - blargg_long r = BLIP_READER_READ( right ); - if ( (BOOST::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 ( (BOOST::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] ); -} +// Blip_Buffer $vers. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2008 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" + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; + channel_types_ = NULL; + channel_count_ = 0; + immediate_removal_ = true; +} + +Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ ) +{ + channel_t ch; + ch.center = ch.left = ch.right = NULL; + return ch; +} + +// 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 = NULL; + chan.center = NULL; + chan.right = NULL; +} + +// 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( int rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + + +// Tracked_Blip_Buffer + +int const blip_buffer_extra = 32; // TODO: explain why this value + +Tracked_Blip_Buffer::Tracked_Blip_Buffer() +{ + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::clear() +{ + last_non_silence = 0; + Blip_Buffer::clear(); +} + +void Tracked_Blip_Buffer::end_frame( blip_time_t t ) +{ + Blip_Buffer::end_frame( t ); + if ( modified() ) + { + clear_modified(); + last_non_silence = samples_avail() + blip_buffer_extra; + } +} + +unsigned Tracked_Blip_Buffer::non_silent() const +{ + return last_non_silence | unsettled(); +} + +inline void Tracked_Blip_Buffer::remove_( int n ) +{ + if ( (last_non_silence -= n) < 0 ) + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::remove_silence( int n ) +{ + remove_( n ); + Blip_Buffer::remove_silence( n ); +} + +void Tracked_Blip_Buffer::remove_samples( int n ) +{ + remove_( n ); + Blip_Buffer::remove_samples( n ); +} + +void Tracked_Blip_Buffer::remove_all_samples() +{ + int avail = samples_avail(); + if ( !non_silent() ) + remove_silence( avail ); + else + remove_samples( avail ); +} + +int Tracked_Blip_Buffer::read_samples( blip_sample_t out [], int count ) +{ + count = Blip_Buffer::read_samples( out, count ); + remove_( count ); + return count; +} + +// Stereo_Buffer + +int const stereo = 2; + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = mixer.bufs [2] = &bufs [2]; + chan.left = mixer.bufs [0] = &bufs [0]; + chan.right = mixer.bufs [1] = &bufs [1]; + mixer.samples_read = 0; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( int rate, int msec ) +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + 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( int rate ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +int Stereo_Buffer::read_samples( blip_sample_t out [], int out_size ) +{ + require( (out_size & 1) == 0 ); // must read an even number of samples + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + if ( pair_count ) + { + mixer.read_pairs( out, pair_count ); + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( !b.non_silent() ) + b.remove_silence( mixer.samples_read ); + else + b.remove_samples( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + + +// Stereo_Mixer + +// mixers use a single index value to improve performance on register-challenged processors +// offset goes from negative to zero + +void Stereo_Mixer::read_pairs( blip_sample_t out [], int count ) +{ + // TODO: if caller never marks buffers as modified, uses mono + // except that buffer isn't cleared, so caller can encounter + // subtle problems and not realize the cause. + samples_read += count; + if ( bufs [0]->non_silent() | bufs [1]->non_silent() ) + mix_stereo( out, count ); + else + mix_mono( out, count ); +} + +void Stereo_Mixer::mix_mono( blip_sample_t out_ [], int count ) +{ + int const bass = bufs [2]->highpass_shift(); + Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read; + int center_sum = bufs [2]->integrator(); + + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_ + count; + int offset = -count; + do + { + int s = center_sum >> bufs [2]->delta_bits; + + center_sum -= center_sum >> bass; + center_sum += center [offset]; + + BLIP_CLAMP( s, s ); + + out [offset] [0] = (blip_sample_t) s; + out [offset] [1] = (blip_sample_t) s; + } + while ( ++offset ); + + bufs [2]->set_integrator( center_sum ); +} + +void Stereo_Mixer::mix_stereo( blip_sample_t out_ [], int count ) +{ + blip_sample_t* BLARGG_RESTRICT out = out_ + count * stereo; + + // do left + center and right + center separately to reduce register load + Tracked_Blip_Buffer* const* buf = &bufs [2]; + while ( true ) // loop runs twice + { + --buf; + --out; + + int const bass = bufs [2]->highpass_shift(); + Blip_Buffer::delta_t const* side = (*buf)->read_pos() + samples_read; + Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read; + + int side_sum = (*buf)->integrator(); + int center_sum = bufs [2]->integrator(); + + int offset = -count; + do + { + int s = (center_sum + side_sum) >> Blip_Buffer::delta_bits; + + side_sum -= side_sum >> bass; + center_sum -= center_sum >> bass; + + side_sum += side [offset]; + center_sum += center [offset]; + + BLIP_CLAMP( s, s ); + + ++offset; // before write since out is decremented to slightly before end + out [offset * stereo] = (blip_sample_t) s; + } + while ( offset ); + + (*buf)->set_integrator( side_sum ); + + if ( buf != bufs ) + continue; + + // only end center once + bufs [2]->set_integrator( center_sum ); + break; + } +} diff --git a/Frameworks/GME/gme/Multi_Buffer.h b/Frameworks/GME/gme/Multi_Buffer.h old mode 100755 new mode 100644 index a39cca1a5..677d80ddc --- a/Frameworks/GME/gme/Multi_Buffer.h +++ b/Frameworks/GME/gme/Multi_Buffer.h @@ -1,6 +1,6 @@ // Multi-channel sound buffer interface, and basic mono and stereo buffers -// Blip_Buffer 0.4.1 +// Blip_Buffer $vers #ifndef MULTI_BUFFER_H #define MULTI_BUFFER_H @@ -11,146 +11,209 @@ // consisting of left, center, and right buffers. class Multi_Buffer { public: + + // 1=mono, 2=stereo Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } + virtual ~Multi_Buffer() { } - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); + // Sets the number of channels available and optionally their types + // (type information used by Effects_Buffer) + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual blargg_err_t set_channel_count( int, int const types [] = NULL ); + int channel_count() const { return channel_count_; } - // Get indexed channel, from 0 to channel count - 1 + // Gets 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; + virtual channel_t channel( int index ) BLARGG_PURE( ; ) // 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_; } + 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_++; } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ) BLARGG_PURE( ; ) + int sample_rate() const; + int length() const; + virtual void clock_rate( int ) BLARGG_PURE( ; ) + virtual void bass_freq( int ) BLARGG_PURE( ; ) + virtual void clear() BLARGG_PURE( ; ) + virtual void end_frame( blip_time_t ) BLARGG_PURE( ; ) + virtual int read_samples( blip_sample_t [], int ) BLARGG_PURE( ; ) + virtual int samples_avail() const BLARGG_PURE( ; ) + private: // noncopyable Multi_Buffer( const Multi_Buffer& ); Multi_Buffer& operator = ( const Multi_Buffer& ); - + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + void disable_immediate_removal() { immediate_removal_ = false; } + +protected: + bool immediate_removal() const { return immediate_removal_; } + int const* channel_types() const { return channel_types_; } + void channels_changed() { channels_changed_count_++; } + +private: unsigned channels_changed_count_; - long sample_rate_; + int sample_rate_; int length_; + int channel_count_; int const samples_per_frame_; + int const* channel_types_; + bool immediate_removal_; }; + // 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; } + Blip_Buffer* center() { return &buf; } +// Implementation 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 ); } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int rate ) { buf.clock_rate( rate ); } + virtual void bass_freq( int freq ) { buf.bass_freq( freq ); } + virtual void clear() { buf.clear(); } + virtual int samples_avail() const { return buf.samples_avail(); } + virtual int read_samples( blip_sample_t p [], int s ) { return buf.read_samples( p, s ); } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t t ) { buf.end_frame( t ); } + +private: + Blip_Buffer buf; + channel_t chan; }; + class Tracked_Blip_Buffer : public Blip_Buffer { + public: + // Non-zero if buffer still has non-silent samples in it. Requires that you call + // set_modified() appropriately. + unsigned non_silent() const; + + // remove_samples( samples_avail() ) + void remove_all_samples(); + + // Implementation + public: + BLARGG_DISABLE_NOTHROW + int read_samples( blip_sample_t [], int ); + void remove_silence( int ); + void remove_samples( int ); + Tracked_Blip_Buffer(); + void clear(); + void end_frame( blip_time_t ); + + private: + int last_non_silence; + + delta_t unsettled() const { return integrator() >> delta_bits; } + void remove_( int ); + }; + + class Stereo_Mixer { + public: + Tracked_Blip_Buffer* bufs [3]; + int samples_read; + + Stereo_Mixer() : samples_read( 0 ) { } + void read_pairs( blip_sample_t out [], int count ); + + private: + void mix_mono ( blip_sample_t out [], int pair_count ); + void mix_stereo( blip_sample_t out [], int pair_count ); + }; + + // 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]; } + Blip_Buffer* center() { return &bufs [2]; } + Blip_Buffer* left() { return &bufs [0]; } + Blip_Buffer* right() { return &bufs [1]; } +// Implementation 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 ); + virtual blargg_err_t set_sample_rate( int, int msec = blip_default_length ); + virtual void clock_rate( int ); + virtual void bass_freq( int ); + virtual void clear(); + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ); + virtual int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + virtual int read_samples( blip_sample_t [], int ); private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; + enum { bufs_size = 3 }; + typedef Tracked_Blip_Buffer buf_t; + buf_t bufs [bufs_size]; + Stereo_Mixer mixer; 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 ); + int samples_avail_; }; + // 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 ) - { - return Multi_Buffer::set_sample_rate( rate, msec ); - } - 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; } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int ) { } + virtual void bass_freq( int ) { } + virtual void clear() { } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ) { } + virtual int samples_avail() const { return 0; } + virtual int read_samples( blip_sample_t [], int ) { return 0; } }; -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +inline blargg_err_t Multi_Buffer::set_sample_rate( int rate, int msec ) { sample_rate_ = rate; length_ = msec; - return 0; + return blargg_ok; } -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } +inline int Multi_Buffer::sample_rate() const { return sample_rate_; } +inline int Multi_Buffer::length() const { return length_; } +inline void Multi_Buffer::clock_rate( int ) { } +inline void Multi_Buffer::bass_freq( int ) { } +inline void Multi_Buffer::clear() { } +inline void Multi_Buffer::end_frame( blip_time_t ) { } +inline int Multi_Buffer::read_samples( blip_sample_t [], int ) { return 0; } +inline int Multi_Buffer::samples_avail() const { return 0; } -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } +inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const types [] ) +{ + channel_count_ = n; + channel_types_ = types; + return blargg_ok; +} -inline int Multi_Buffer::length() const { return length_; } +inline blargg_err_t Silent_Buffer::set_sample_rate( int rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} #endif diff --git a/Frameworks/GME/gme/Music_Emu.cpp b/Frameworks/GME/gme/Music_Emu.cpp old mode 100755 new mode 100644 index 31c7233ca..410168f41 --- a/Frameworks/GME/gme/Music_Emu.cpp +++ b/Frameworks/GME/gme/Music_Emu.cpp @@ -1,410 +1,235 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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" +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +/* Copyright (C) 2003-2008 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 stereo = 2; // number of channels for stereo -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 = { -8.0, 180 }; +Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180, 0,0,0,0,0,0,0,0 }; void Music_Emu::clear_track_vars() { - current_track_ = -1; - out_time = 0; - emu_time = 0; - emu_track_ended_ = true; - track_ended_ = true; - fade_start = LONG_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; - - 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; - - 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 ); -} - -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 * stereo * 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 ); - } -} - -// 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) * stereo; -} - -long Music_Emu::tell() const -{ - blargg_long rate = sample_rate() * stereo; - blargg_long sec = out_time / rate; - return sec * 1000 + (out_time - sec * rate) * 1000 / rate; -} - -blargg_err_t Music_Emu::seek( long msec ) -{ - blargg_long time = msec_to_samples( msec ); - if ( time < out_time ) - RETURN_ERR( start_track( current_track_ ) ); - return skip( time - out_time ); -} - -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 / stereo); - 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 % stereo == 0 ); - - assert( emu_time >= out_time ); - - // prints nifty graph of how far ahead we are when searching for silence - //dprintf( "%*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 * stereo * 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 ( 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_::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"; } + current_track_ = -1; + warning(); // clear warning + track_filter.stop(); +} + +void Music_Emu::unload() +{ + voice_count_ = 0; + clear_track_vars(); + Gme_File::unload(); +} + +Music_Emu::gme_t() +{ + effects_buffer_ = NULL; + sample_rate_ = 0; + mute_mask_ = 0; + tempo_ = 1.0; + gain_ = 1.0; + + // defaults + tfilter = track_filter.setup(); + set_max_initial_silence( 15 ); + set_silence_lookahead( 3 ); + ignore_silence( false ); + + equalizer_.treble = -1.0; + equalizer_.bass = 60; + + 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(); // clears fields +} + +Music_Emu::~gme_t() +{ + assert( !effects_buffer_ ); +} + +blargg_err_t Music_Emu::set_sample_rate( int rate ) +{ + require( !sample_rate() ); // sample rate can't be changed once set + RETURN_ERR( set_sample_rate_( rate ) ); + RETURN_ERR( track_filter.init( this ) ); + sample_rate_ = rate; + tfilter.max_silence = 6 * stereo * sample_rate(); + return blargg_ok; +} + +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 ) +{ + // TODO: why is GCC generating memcpy call here? + // Without the 'if', valgrind flags it. + if ( &eq != &equalizer_ ) + equalizer_ = eq; + set_equalizer_( eq ); +} + +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 ); +} + +const char* Music_Emu::voice_name( int i ) const +{ + if ( (unsigned) i < (unsigned) voice_count_ ) + return voice_names_ [i]; + + //check( false ); // TODO: enable? + return ""; +} + +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 ); +} + +blargg_err_t Music_Emu::post_load() +{ + set_tempo( tempo_ ); + remute_voices(); + return Gme_File::post_load(); +} + +// Tell/Seek + +int Music_Emu::msec_to_samples( int msec ) const +{ + int sec = msec / 1000; + msec -= sec * 1000; + return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; +} + +int Music_Emu::tell() const +{ + int rate = sample_rate() * stereo; + int sec = track_filter.sample_count() / rate; + return sec * 1000 + (track_filter.sample_count() - sec * rate) * 1000 / rate; +} + +blargg_err_t Music_Emu::seek( int msec ) +{ + int time = msec_to_samples( msec ); + if ( time < track_filter.sample_count() ) + RETURN_ERR( start_track( current_track_ ) ); + return skip( time - track_filter.sample_count() ); +} + +blargg_err_t Music_Emu::skip( int count ) +{ + require( current_track() >= 0 ); // start_track() must have been called already + return track_filter.skip( count ); +} + +blargg_err_t Music_Emu::skip_( int count ) +{ + // for long skip, mute sound + const int threshold = 32768; + if ( count > threshold ) + { + int saved_mute = mute_mask_; + mute_voices( ~0 ); + + int n = count - threshold/2; + n &= ~(2048-1); // round to multiple of 2048 + count -= n; + RETURN_ERR( track_filter.skip_( n ) ); + + mute_voices( saved_mute ); + } + + return track_filter.skip_( count ); +} + +// Playback + +blargg_err_t Music_Emu::start_track( int track ) +{ + clear_track_vars(); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + current_track_ = track; + blargg_err_t err = start_track_( remapped ); + if ( err ) + { + current_track_ = -1; + return err; + } + + // convert filter times to samples + Track_Filter::setup_t s = tfilter; + s.max_initial *= sample_rate() * stereo; + #if GME_DISABLE_SILENCE_LOOKAHEAD + s.lookahead = 1; + #endif + track_filter.setup( s ); + + return track_filter.start_track(); +} + +void Music_Emu::set_fade( int start_msec, int length_msec ) +{ + track_filter.set_fade( msec_to_samples( start_msec ), + length_msec * sample_rate() / (1000 / stereo) ); +} + +blargg_err_t Music_Emu::play( int out_count, sample_t out [] ) +{ + require( current_track() >= 0 ); + require( out_count % stereo == 0 ); + + return track_filter.play( out_count, out ); +} + +// Gme_Info_ + +blargg_err_t Gme_Info_::set_sample_rate_( int ) { return blargg_ok; } +void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu +blargg_err_t Gme_Info_::post_load() { return Gme_File::post_load(); } // skip Music_Emu +void Gme_Info_::set_equalizer_( equalizer_t const& ){ 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 BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); } +blargg_err_t Gme_Info_::play_( int, sample_t [] ) { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); } diff --git a/Frameworks/GME/gme/Music_Emu.h b/Frameworks/GME/gme/Music_Emu.h old mode 100755 new mode 100644 index 573403ced..32e32a9c6 --- a/Frameworks/GME/gme/Music_Emu.h +++ b/Frameworks/GME/gme/Music_Emu.h @@ -1,211 +1,252 @@ -// Common interface to game music file emulators - -// Game_Music_Emu 0.5.2 -#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 ); - - // 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; - -// Track status/control - - // Number of milliseconds (1000 msec = 1 second) played since beginning of track - long tell() const; - - // Seek to new time in track. Seeking backwards or far forward can take a while. - blargg_err_t seek( long msec ); - - // 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 ); - - // Disable automatic end-of-track detection and skipping of silence at beginning - void ignore_silence( bool disable = true ); - - // Info for current track - 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* ) { } - -// 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& ); - - // 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(); - - virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; - virtual void set_equalizer_( equalizer_t const& ) { }; - 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_; - - 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 - 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_new_emu( gme_type_t, long ); - 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 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::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 char**) 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 +// Common interface to game music file emulators + +// Game_Music_Emu $vers +#ifndef MUSIC_EMU_H +#define MUSIC_EMU_H + +#include "Gme_File.h" +#include "Track_Filter.h" +#include "blargg_errors.h" +class Multi_Buffer; + +struct gme_t : public Gme_File, private Track_Filter::callbacks_t { +public: + // Sets output sample rate. Must be called only once before loading file. + blargg_err_t set_sample_rate( int sample_rate ); + + // Sample rate sound is generated at + int sample_rate() const; + +// File loading + + // See Gme_Loader.h + +// Basic playback + + // Starts a track, where 0 is the first track. Also clears warning string. + blargg_err_t start_track( int ); + + // Generates '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( int count, sample_t* buf ); + +// Track information + + // See Gme_File.h + + // Index of current track or -1 if one hasn't been started + int current_track() const; + + // Info for currently playing track + using Gme_File::track_info; + blargg_err_t track_info( track_info_t* out ) const; + + struct Hash_Function + { + virtual void hash_( byte const* data, size_t size ) BLARGG_PURE( ; ) + }; + virtual blargg_err_t hash_( Hash_Function& ) const BLARGG_PURE( ; ) + +// Track status/control + + // Number of milliseconds played since beginning of track (1000 per second) + int tell() const; + + // Seeks to new time in track. Seeking backwards or far forward can take a while. + blargg_err_t seek( int msec ); + + // Skips n samples + blargg_err_t skip( int n ); + + // True if a track has reached its end + bool track_ended() const; + + // Sets start time and length of track fade out. Once fade ends track_ended() returns + // true. Fade time must be set after track has been started, and can be changed + // at any time. + void set_fade( int start_msec, int length_msec = 8000 ); + + // Disables automatic end-of-track detection and skipping of silence at beginning + void ignore_silence( bool disable = true ); + +// Voices + + // Number of voices used by currently loaded file + int voice_count() const; + + // Name of voice i, from 0 to voice_count()-1 + const char* voice_name( int i ) const; + + // Mutes/unmutes voice i, where voice 0 is first voice + void mute_voice( int index, bool mute = true ); + + // Sets 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 ); + +// Sound customization + + // Adjusts 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 ); + + // Changes overall output amplitude, where 1.0 results in minimal clamping. + // Must be called before set_sample_rate(). + void set_gain( double ); + + // Requests 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( class Multi_Buffer* ) { } + +// 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; + + // Sets frequency equalizer parameters + void set_equalizer( equalizer_t const& ); + + // Equalizer preset for a TV speaker + static equalizer_t const tv_eq; + +// Derived interface +protected: + // Cause any further generated samples to be silence, instead of calling play_() + void set_track_ended() { track_filter.set_track_ended(); } + + // If more than secs of silence are encountered, track is ended + void set_max_initial_silence( int secs ) { tfilter.max_initial = secs; } + + // Sets rate emulator is run at when scanning ahead for silence. 1=100%, 2=200% etc. + void set_silence_lookahead( int rate ) { tfilter.lookahead = rate; } + + // Sets number of voices + void set_voice_count( int n ) { voice_count_ = n; } + + // Sets names of voices + void set_voice_names( const char* const names [] ); + + // Current gain + double gain() const { return gain_; } + + // Current tempo + double tempo() const { return tempo_; } + + // Re-applies muting mask using mute_voices_() + void remute_voices(); + +// Overrides should do the indicated task + + // Set sample rate as close as possible to sample_rate, then call + // Music_Emu::set_sample_rate_() with the actual rate used. + virtual blargg_err_t set_sample_rate_( int sample_rate ) BLARGG_PURE( ; ) + + // Set equalizer parameters + virtual void set_equalizer_( equalizer_t const& ) { } + + // Mute voices based on mask + virtual void mute_voices_( int mask ) BLARGG_PURE( ; ) + + // Set tempo to t, which is constrained to the range 0.02 to 4.0. + virtual void set_tempo_( double t ) BLARGG_PURE( ; ) + + // Start track t, where 0 is the first track + virtual blargg_err_t start_track_( int t ) BLARGG_PURE( ; ) // tempo is set before this + + // Generate count samples into *out. Count will always be even. + virtual blargg_err_t play_( int count, sample_t out [] ) BLARGG_PURE( ; ) + + // Skip count samples. Count will always be even. + virtual blargg_err_t skip_( int count ); + + +// Implementation +public: + gme_t(); + ~gme_t(); + BLARGG_DEPRECATED( const char** voice_names() const { return CONST_CAST(const char**,voice_names_); } ) + +protected: + virtual void unload(); + virtual void pre_load(); + virtual blargg_err_t post_load(); + +private: + Track_Filter::setup_t tfilter; + Track_Filter track_filter; + equalizer_t equalizer_; + const char* const* voice_names_; + int voice_count_; + int mute_mask_; + double tempo_; + double gain_; + int sample_rate_; + int current_track_; + + void clear_track_vars(); + int msec_to_samples( int msec ) const; + + friend Music_Emu* gme_new_emu( gme_type_t, int ); + friend void gme_effects( Music_Emu const*, gme_effects_t* ); + friend void gme_set_effects( Music_Emu*, gme_effects_t const* ); + friend void gme_set_stereo_depth( Music_Emu*, double ); + friend const char** gme_voice_names ( Music_Emu const* ); + +protected: + Multi_Buffer* effects_buffer_; +}; + +// base class for info-only derivations +struct Gme_Info_ : Music_Emu +{ + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual void set_equalizer_( equalizer_t const& ); + virtual void mute_voices_( int mask ); + virtual void set_tempo_( double ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int count, sample_t out [] ); + virtual void pre_load(); + virtual blargg_err_t post_load(); +}; + +inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const +{ + return track_info( out, current_track_ ); +} + +inline int Music_Emu::sample_rate() const { return sample_rate_; } +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_filter.track_ended(); } +inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } + +inline void Music_Emu::ignore_silence( bool b ) { track_filter.ignore_silence( 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::set_voice_names( const char* const p [] ) { voice_names_ = p; } + +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; +} + +inline blargg_err_t Music_Emu::start_track_( int ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::set_sample_rate_( int ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::play_( int, sample_t [] ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::hash_( Hash_Function& ) const { return BLARGG_ERR( BLARGG_ERR_CALLER, "no hashing function defined" ); } + +inline void Music_Emu::Hash_Function::hash_( byte const*, size_t ) { } + +#endif diff --git a/Frameworks/GME/gme/Nes_Apu.cpp b/Frameworks/GME/gme/Nes_Apu.cpp old mode 100755 new mode 100644 index 8daf5d0e1..1862846ea --- a/Frameworks/GME/gme/Nes_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Apu.cpp @@ -1,8 +1,8 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Apu.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -23,8 +23,6 @@ Nes_Apu::Nes_Apu() : { tempo_ = 1.0; dmc.apu = this; - dmc.prg_reader = NULL; - irq_notifier_ = NULL; oscs [0] = &square1; oscs [1] = &square2; @@ -32,28 +30,28 @@ Nes_Apu::Nes_Apu() : oscs [3] = &noise; oscs [4] = &dmc; - output( NULL ); + set_output( NULL ); + dmc.nonlinear = false; volume( 1.0 ); reset( false ); } void Nes_Apu::treble_eq( const blip_eq_t& eq ) { - square_synth.treble_eq( eq ); + square_synth .treble_eq( eq ); triangle.synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); - dmc.synth.treble_eq( eq ); + noise .synth.treble_eq( eq ); + dmc .synth.treble_eq( eq ); } -void Nes_Apu::enable_nonlinear( double v ) +void Nes_Apu::enable_nonlinear_( double sq, double tnd ) { dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); + square_synth.volume( sq ); - 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 ); + triangle.synth.volume( tnd * 2.752 ); + noise .synth.volume( tnd * 1.849 ); + dmc .synth.volume( tnd ); square1 .last_amp = 0; square2 .last_amp = 0; @@ -64,17 +62,20 @@ void Nes_Apu::enable_nonlinear( double v ) 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 ); + if ( !dmc.nonlinear ) + { + v *= 1.0 / 1.11; // TODO: merge into values below + square_synth .volume( 0.125 / amp_range * v ); // was 0.1128 1.108 + triangle.synth.volume( 0.150 / amp_range * v ); // was 0.12765 1.175 + noise .synth.volume( 0.095 / amp_range * v ); // was 0.0741 1.282 + dmc .synth.volume( 0.450 / 2048 * v ); // was 0.42545 1.058 + } } -void Nes_Apu::output( Blip_Buffer* buffer ) +void Nes_Apu::set_output( Blip_Buffer* buffer ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buffer ); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buffer ); } void Nes_Apu::set_tempo( double t ) @@ -100,12 +101,13 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) last_dmc_time = 0; osc_enables = 0; irq_flag = false; + enable_w4011 = true; 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++ ) + for ( int addr = io_addr; addr <= 0x4013; addr++ ) write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); dmc.dac = initial_dmc_dac; @@ -117,7 +119,7 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) void Nes_Apu::irq_changed() { - nes_time_t new_irq = dmc.next_irq; + blip_time_t new_irq = dmc.next_irq; if ( dmc.irq_flag | irq_flag ) { new_irq = 0; } @@ -127,25 +129,25 @@ void Nes_Apu::irq_changed() if ( new_irq != earliest_irq_ ) { earliest_irq_ = new_irq; - if ( irq_notifier_ ) - irq_notifier_( irq_data ); + if ( irq_notifier.f ) + irq_notifier.f( irq_notifier.data ); } } // frames -void Nes_Apu::run_until( nes_time_t end_time ) +void Nes_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_dmc_time ); if ( end_time > next_dmc_read_time() ) { - nes_time_t start = last_dmc_time; + blip_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 ) +void Nes_Apu::run_until_( blip_time_t end_time ) { require( end_time >= last_time ); @@ -154,7 +156,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) if ( last_dmc_time < end_time ) { - nes_time_t start = last_dmc_time; + blip_time_t start = last_dmc_time; last_dmc_time = end_time; dmc.run( start, end_time ); } @@ -162,7 +164,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) while ( true ) { // earlier of next frame time or end time - nes_time_t time = last_time + frame_delay; + blip_time_t time = last_time + frame_delay; if ( time > end_time ) time = end_time; frame_delay -= time - last_time; @@ -226,7 +228,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) } template -inline void zero_apu_osc( T* osc, nes_time_t time ) +inline void zero_apu_osc( T* osc, blip_time_t time ) { Blip_Buffer* output = osc->output; int last_amp = osc->last_amp; @@ -235,7 +237,7 @@ inline void zero_apu_osc( T* osc, nes_time_t time ) osc->synth.offset( time, -last_amp, output ); } -void Nes_Apu::end_frame( nes_time_t end_time ) +void Nes_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until_( end_time ); @@ -280,13 +282,13 @@ static const unsigned char length_table [0x20] = { 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E }; -void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) +void Nes_Apu::write_register( blip_time_t time, int 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 ) + if ( unsigned (addr - io_addr) >= io_size ) return; run_until_( time ); @@ -294,7 +296,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) if ( addr < 0x4014 ) { // Write to channel - int osc_index = (addr - start_addr) >> 2; + int osc_index = (addr - io_addr) >> 2; Nes_Osc* osc = oscs [osc_index]; int reg = addr & 3; @@ -304,7 +306,8 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) if ( osc_index == 4 ) { // handle DMC specially - dmc.write_register( reg, data ); + if ( enable_w4011 || reg != 1 ) + dmc.write_register( reg, data ); } else if ( reg == 3 ) { @@ -366,7 +369,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) } } -int Nes_Apu::read_status( nes_time_t time ) +int Nes_Apu::read_status( blip_time_t time ) { run_until_( time - 1 ); diff --git a/Frameworks/GME/gme/Nes_Apu.h b/Frameworks/GME/gme/Nes_Apu.h old mode 100755 new mode 100644 index dbd8484c0..ed6374685 --- a/Frameworks/GME/gme/Nes_Apu.h +++ b/Frameworks/GME/gme/Nes_Apu.h @@ -1,14 +1,10 @@ // NES 2A03 APU sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #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; @@ -16,73 +12,76 @@ class Nes_Buffer; class Nes_Apu { public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); +// Basics + + typedef int nes_time_t; // NES CPU clock cycle count - // Set memory reader callback used by DMC oscillator to fetch samples. + // Sets 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 ); + //void dmc_reader( int (*callback)( void* user_data, int addr ), void* user_data = NULL ); + + // Sets buffer to generate sound into, or 0 to mute output (reduces + // emulation accuracy). + void set_output( Blip_Buffer* ); // 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 ); + // Writes to register (0x4000-0x4013, and 0x4015 and 0x4017) + enum { io_addr = 0x4000 }; + enum { io_size = 0x18 }; + void write_register( nes_time_t, int addr, int data ); - // Read from status register at 0x4015 + // Reads from status register (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 + // Runs all oscillators up to specified time, ends current time frame, then + // starts 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) +// Optional - // 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 + // Resets internal frame counter, registers, and all oscillators. + // Uses PAL timing if pal_timing is true, otherwise use NTSC timing. + // Sets 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 + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Triangle, 3: Noise, 4: DMC + enum { osc_count = 5 }; + void set_output( int chan, Blip_Buffer* buf ); + + // Adjusts frame period void set_tempo( double ); - // Save/load exact emulation state + // Saves/loads 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) + // Sets overall volume (default is 1.0) void volume( double ); - // Set treble equalization (see notes.txt) + // Sets 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 + // Sets 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 ); + //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 + // Gets 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 = LONG_MAX / 2 + 1 }; + 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. + // Counts 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; @@ -90,17 +89,30 @@ public: // 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 + // Runs 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 ); + +// Implementation public: Nes_Apu(); BLARGG_DISABLE_NOTHROW -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); - static double nonlinear_tnd_gain() { return 0.75; } + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ); ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4000 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4017 }; ) + + blargg_callback dmc_reader; + blargg_callback irq_notifier; + + void enable_nonlinear_( double sq, double tnd ); + static float tnd_total_() { return 196.015f; } + + void enable_w4011_( bool enable = true ) { enable_w4011 = enable; } + private: friend struct Nes_Dmc; @@ -126,8 +138,7 @@ private: int osc_enables; int frame_mode; bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; + bool enable_w4011; Nes_Square::Synth square_synth; // shared by squares void irq_changed(); @@ -138,42 +149,36 @@ private: friend class Nes_Core; }; -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) +inline void Nes_Apu::set_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 +inline Nes_Apu::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 +inline Nes_Apu::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; + return apu->last_dmc_time + delay + (bits_remain - 1) * period; } -inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } +inline Nes_Apu::nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +BLARGG_DEPRECATED( typedef int nes_time_t; ) // use your own typedef +BLARGG_DEPRECATED( typedef unsigned nes_addr_t; ) // use your own typedef + +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::output ( Blip_Buffer* c ) { set_output( c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::osc_output( int i, Blip_Buffer* c ) { set_output( i, c ); } ) #endif diff --git a/Frameworks/GME/gme/Nes_Cpu.cpp b/Frameworks/GME/gme/Nes_Cpu.cpp old mode 100755 new mode 100644 index 480b4aa48..96d594177 --- a/Frameworks/GME/gme/Nes_Cpu.cpp +++ b/Frameworks/GME/gme/Nes_Cpu.cpp @@ -1,13 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #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 +/* Copyright (C) 2003-2008 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 @@ -18,1067 +15,48 @@ 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 ); + byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size ); + cpu_state->code_map [i] = p2; + cpu_state_.code_map [i] = p2; } -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::map_code( addr_t start, int size, void const* data, int mirror_size ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + require( mirror_size % page_size == 0 ); + + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( NES_CPU_PAGE( start + offset ), + STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) ); +} void Nes_Cpu::reset( void const* unmapped_page ) { - check( state == &state_ ); - state = &state_; - r.status = st_i; + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + + r.flags = irq_inhibit_mask; 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; + + cpu_state_.time = 0; + cpu_state_.base = 0; + irq_time_ = future_time; + end_time_ = future_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 ); + map_code( 0, 0x10000, unmapped_page, page_size ); 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 )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -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 - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 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 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - fuint8 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]; - fuint8 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 - - fuint16 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 ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ - } - -#define IND_X( out ) {\ - fuint16 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 )\ -{\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc = BOOST::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 - fuint16 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; - - { - fuint16 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 - { - fuint16 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; - } - - { - fuint8 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: { - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::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; - fint16 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 = (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 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 - fuint8 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 - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 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 - fuint8 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 = (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: { - //dprintf( "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: - dprintf( "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; - - dprintf( "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--; - if ( pc > 0xFFFF ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc &= 0xFFFF; - goto loop; - } - 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 - }; - fuint8 opcode = instr [-1]; - fint16 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; - fuint8 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; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return s_time < 0; -} - diff --git a/Frameworks/GME/gme/Nes_Cpu.h b/Frameworks/GME/gme/Nes_Cpu.h old mode 100755 new mode 100644 index d303b57c7..8fa09f2e3 --- a/Frameworks/GME/gme/Nes_Cpu.h +++ b/Frameworks/GME/gme/Nes_Cpu.h @@ -1,114 +1,131 @@ -// NES 6502 CPU emulator +// NES CPU emulator -// Game_Music_Emu 0.5.2 +// $package #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 = LONG_MAX / 2 + 1 }; - class Nes_Cpu { public: - typedef BOOST::uint8_t uint8_t; + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; - // 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 ); + // Clears registers and maps all pages to unmapped_page + void reset( void const* unmapped_page = NULL ); - // 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 ); + // Maps code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. If mirror_size is non-zero, the first + // mirror_size bytes are repeated over the range. mirror_size must be a + // multiple of page_size. + enum { page_bits = 11 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void const* code, int mirror_size = 0 ); - // Access emulated memory as CPU does - uint8_t const* get_code( nes_addr_t ); + // Accesses emulated memory as CPU does + byte const* get_code( addr_t ) const; - // 2KB of RAM at address 0 - uint8_t low_mem [0x800]; - - // NES 6502 registers. Not kept updated during a call to run(). + // NES 6502 registers. NOT kept updated during emulation. struct registers_t { BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; + byte a; + byte x; + byte y; + byte flags; + byte 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; } + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } - nes_time_t irq_time() const { return irq_time_; } - void set_irq_time( nes_time_t ); + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } - nes_time_t end_time() const { return end_time_; } - void set_end_time( nes_time_t ); + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); - // Number of undefined instructions encountered and skipped - void clear_error_count() { error_count_ = 0; } - unsigned long error_count() const { return error_count_; } + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( time_t ); - // CPU invokes bad opcode handler if it encounters this - enum { bad_opcode = 0xF2 }; + // Number of unimplemented instructions encountered and skipped + void clear_error_count() { error_count_ = 0; } + unsigned error_count() const { return error_count_; } + void count_error() { error_count_++; } -public: - Nes_Cpu() { state = &state_; } - enum { page_bits = 11 }; - enum { page_count = 0x10000 >> page_bits }; - enum { irq_inhibit = 0x04 }; + // Unmapped page should be filled with this + enum { halt_opcode = 0x22 }; + + enum { irq_inhibit_mask = 0x04 }; + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + private: - struct state_t { - uint8_t const* code_map [page_count + 1]; - nes_time_t base; + // noncopyable + Nes_Cpu( const Nes_Cpu& ); + Nes_Cpu& operator = ( const Nes_Cpu& ); + + +// Implementation +public: + Nes_Cpu() { cpu_state = &cpu_state_; } + enum { page_count = 0x10000 >> page_bits }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + 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_; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; + unsigned error_count_; +private: void set_code_page( int, void const* ); - inline int update_end_time( nes_time_t end, nes_time_t irq ); + inline void update_end_time( time_t end, time_t irq ); }; -inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) +#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define NES_CPU_OFFSET( addr ) (addr) +#else + #define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const { - return state->code_map [addr >> page_bits] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr ); } -inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) +inline void Nes_Cpu::update_end_time( time_t end, time_t irq ) { - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; } -inline void Nes_Cpu::set_irq_time( nes_time_t t ) +inline void Nes_Cpu::set_irq_time( time_t t ) { - state->time += update_end_time( end_time_, (irq_time_ = t) ); + irq_time_ = t; + update_end_time( end_time_, t ); } -inline void Nes_Cpu::set_end_time( nes_time_t t ) +inline void Nes_Cpu::set_end_time( time_t t ) { - state->time += update_end_time( (end_time_ = t), irq_time_ ); -} + end_time_ = t; + update_end_time( t, irq_time_ ); +} #endif diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp old mode 100755 new mode 100644 index c058f6b1f..b3ab6d093 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp @@ -1,9 +1,7 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #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 @@ -51,7 +49,6 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) Blip_Buffer* const osc_output = oscs [index].output; if ( !osc_output ) continue; - osc_output->set_modified(); // check for unsupported mode #ifndef NDEBUG @@ -78,11 +75,13 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) int amp = volume; if ( !phases [index] ) amp = 0; + { int delta = amp - oscs [index].last_amp; if ( delta ) { oscs [index].last_amp = amp; + osc_output->set_modified(); synth.offset( last_time, delta, osc_output ); } } @@ -91,6 +90,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) if ( time < end_time ) { int delta = amp * 2 - volume; + osc_output->set_modified(); if ( volume ) { do @@ -109,7 +109,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) // maintain phase when silent int count = (end_time - time + period - 1) / period; phases [index] ^= count & 1; - time += (blargg_long) count * period; + time += count * period; } } diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.h b/Frameworks/GME/gme/Nes_Fme7_Apu.h old mode 100755 new mode 100644 index eb60af038..e7272637a --- a/Frameworks/GME/gme/Nes_Fme7_Apu.h +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.h @@ -1,6 +1,6 @@ // Sunsoft FME-7 sound emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef NES_FME7_APU_H #define NES_FME7_APU_H @@ -22,9 +22,9 @@ public: void reset(); void volume( double ); void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_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& ); @@ -57,7 +57,7 @@ private: blip_time_t last_time; enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff - Blip_Synth synth; + Blip_Synth_Norm synth; void run_until( blip_time_t ); }; @@ -72,21 +72,21 @@ 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 ) +inline void Nes_Fme7_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; } -inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) +inline void Nes_Fme7_Apu::set_output( Blip_Buffer* buf ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); } inline Nes_Fme7_Apu::Nes_Fme7_Apu() { - output( NULL ); + set_output( NULL ); volume( 1.0 ); reset(); } diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.cpp b/Frameworks/GME/gme/Nes_Namco_Apu.cpp old mode 100755 new mode 100644 index f3235b383..cba754a14 --- a/Frameworks/GME/gme/Nes_Namco_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Namco_Apu.cpp @@ -1,145 +1,152 @@ -// 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 BOOST::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; -} - +// Nes_Snd_Emu $vers. 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() +{ + set_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::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_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; + + 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 BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; + if ( !(osc_reg [4] & 0xE0) ) + continue; + + int volume = osc_reg [7] & 15; + if ( !volume ) + continue; + + int freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100 + osc_reg [0]; + if ( freq < 64 * active_oscs ) + continue; // prevent low frequencies from excessively delaying freq changes + + int const master_clock_divider = 12; // NES time derived via divider of master clock + int const n106_divider = 45; // N106 then divides master clock by this + int const max_freq = 0x3FFFF; + int const lowest_freq_period = (max_freq + 1) * n106_divider / master_clock_divider; + // divide by 8 to avoid overflow + blip_resampled_time_t period = + output->resampled_duration( lowest_freq_period / 8 ) / freq * 8 * 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; + + output->set_modified(); + + 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/Frameworks/GME/gme/Nes_Namco_Apu.h b/Frameworks/GME/gme/Nes_Namco_Apu.h old mode 100755 new mode 100644 index db5fea4bf..2a067d9e9 --- a/Frameworks/GME/gme/Nes_Namco_Apu.h +++ b/Frameworks/GME/gme/Nes_Namco_Apu.h @@ -1,6 +1,6 @@ // Namco 106 sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_NAMCO_APU_H #define NES_NAMCO_APU_H @@ -14,9 +14,9 @@ public: // See Nes_Apu.h for reference. void volume( double ); void treble_eq( const blip_eq_t& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 8 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); void reset(); void end_frame( blip_time_t ); @@ -42,7 +42,7 @@ private: Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); struct Namco_Osc { - blargg_long delay; + int delay; Blip_Buffer* output; short last_amp; short wave_pos; @@ -55,7 +55,7 @@ private: enum { reg_count = 0x80 }; BOOST::uint8_t reg [reg_count]; - Blip_Synth synth; + Blip_Synth_Norm synth; BOOST::uint8_t& access(); void run_until( blip_time_t ); @@ -79,7 +79,7 @@ inline BOOST::uint8_t& Nes_Namco_Apu::access() return reg [addr]; } -inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } +inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count / 15 * v ); } inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } @@ -87,7 +87,7 @@ 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 ) +inline void Nes_Namco_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; diff --git a/Frameworks/GME/gme/Nes_Oscs.cpp b/Frameworks/GME/gme/Nes_Oscs.cpp old mode 100755 new mode 100644 index 1ad3f59c0..367744a7a --- a/Frameworks/GME/gme/Nes_Oscs.cpp +++ b/Frameworks/GME/gme/Nes_Oscs.cpp @@ -1,4 +1,4 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Apu.h" @@ -26,12 +26,14 @@ void Nes_Osc::clock_length( int halt_mask ) void Nes_Envelope::clock_envelope() { int period = regs [0] & 15; - if ( reg_written [3] ) { + if ( reg_written [3] ) + { reg_written [3] = false; env_delay = period; envelope = 15; } - else if ( --env_delay < 0 ) { + else if ( --env_delay < 0 ) + { env_delay = period; if ( envelope | (regs [0] & 0x20) ) envelope = (envelope - 1) & 15; @@ -72,14 +74,15 @@ void Nes_Square::clock_sweep( int negative_adjust ) } } - if ( reg_written [1] ) { + 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, +inline Nes_Square::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; @@ -87,7 +90,7 @@ inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_ti { int count = (remain + timer_period - 1) / timer_period; phase = (phase + count) & (phase_range - 1); - time += (blargg_long) count * timer_period; + time += count * timer_period; } return time; } @@ -103,8 +106,6 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) return; } - output->set_modified(); - int offset = period >> (regs [1] & shift_mask); if ( regs [1] & negate_flag ) offset = 0; @@ -112,7 +113,9 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) const int volume = this->volume(); if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) { - if ( last_amp ) { + if ( last_amp ) + { + output->set_modified(); synth.offset( time, -last_amp, output ); last_amp = 0; } @@ -126,13 +129,15 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) int duty_select = (regs [0] >> 6) & 3; int duty = 1 << duty_select; // 1, 2, 4, 2 int amp = 0; - if ( duty_select == 3 ) { + if ( duty_select == 3 ) + { duty = 2; // negated 25% amp = volume; } if ( phase < duty ) amp ^= volume; + output->set_modified(); { int delta = update_amp( amp ); if ( delta ) @@ -147,9 +152,11 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) int delta = amp * 2 - volume; int phase = this->phase; - do { + do + { phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) { + if ( phase == 0 || phase == duty ) + { delta = -delta; synth.offset_inline( time, delta, output ); } @@ -187,7 +194,7 @@ inline int Nes_Triangle::calc_amp() const } // TODO: clean up -inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, +inline Nes_Square::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; @@ -196,7 +203,7 @@ inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_ int count = (remain + timer_period - 1) / timer_period; phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); phase++; - time += (blargg_long) count * timer_period; + time += count * timer_period; } return time; } @@ -213,14 +220,15 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t 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 ) + { + output->set_modified(); synth.offset( time, delta, output ); + } time += delay; if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) @@ -233,17 +241,22 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) int phase = this->phase; int volume = 1; - if ( phase > phase_range ) { + if ( phase > phase_range ) + { phase -= phase_range; volume = -volume; } + output->set_modified(); - do { - if ( --phase == 0 ) { + do + { + if ( --phase == 0 ) + { phase = phase_range; volume = -volume; } - else { + else + { synth.offset_inline( time, volume, output ); } @@ -284,7 +297,8 @@ void Nes_Dmc::recalc_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 ) { + if ( irq != next_irq ) + { next_irq = irq; apu->irq_changed(); } @@ -332,18 +346,27 @@ inline void Nes_Dmc::reload_sample() length_counter = regs [3] * 0x10 + 1; } -static byte const dac_table [128] = +static int const dmc_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, + 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340, + 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664, + 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955, + 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217, +1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454, +1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670, +1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867, +1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048, }; +inline int Nes_Dmc::update_amp_nonlinear( int in ) +{ + if ( !nonlinear ) + in = dmc_table [in]; + int delta = in - last_amp; + last_amp = in; + return delta; +} + void Nes_Dmc::write_register( int addr, int data ) { if ( addr == 0 ) @@ -355,14 +378,7 @@ void Nes_Dmc::write_register( int addr, int data ) } 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; } } @@ -377,16 +393,18 @@ 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 ); + require( apu->dmc_reader.f ); // dmc_reader must be set + buf = apu->dmc_reader.f( apu->dmc_reader.data, 0x8000u + address ); address = (address + 1) & 0x7FFF; buf_full = true; if ( --length_counter == 0 ) { - if ( regs [0] & loop_flag ) { + if ( regs [0] & loop_flag ) + { reload_sample(); } - else { + else + { apu->osc_enables &= ~0x10; irq_flag = irq_enabled; next_irq = Nes_Apu::no_irq; @@ -398,16 +416,15 @@ void Nes_Dmc::fill_buffer() void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) { - int delta = update_amp( dac ); + int delta = update_amp_nonlinear( dac ); if ( !output ) { silence = true; } - else + else if ( delta ) { output->set_modified(); - if ( delta ) - synth.offset( time, delta, output ); + synth.offset( time, delta, output ); } time += delay; @@ -426,6 +443,8 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) const int period = this->period; int bits = this->bits; int dac = this->dac; + if ( output ) + output->set_modified(); do { @@ -433,9 +452,10 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) { int step = (bits & 1) * 4 - 2; bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { + if ( unsigned (dac + step) <= 0x7F ) + { dac += step; - synth.offset_inline( time, step, output ); + synth.offset_inline( time, update_amp_nonlinear( dac ), output ); } } @@ -444,10 +464,12 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) if ( --bits_remain == 0 ) { bits_remain = 8; - if ( !buf_full ) { + if ( !buf_full ) + { silence = true; } - else { + else + { silence = false; bits = buf; buf_full = false; @@ -460,7 +482,6 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) while ( time < end_time ); this->dac = dac; - this->last_amp = dac; this->bits = bits; } this->bits_remain = bits_remain; @@ -487,14 +508,16 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) return; } - output->set_modified(); const int volume = this->volume(); int amp = (noise & 1) ? volume : 0; { int delta = update_amp( amp ); if ( delta ) + { + output->set_modified(); synth.offset( time, delta, output ); + } } time += delay; @@ -509,7 +532,8 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) // approximate noise cycling while muted, by shuffling up noise register // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) { + if ( !(regs [2] & mode_flag) ) + { int feedback = (noise << 13) ^ (noise << 14); noise = (feedback & 0x4000) | (noise >> 1); } @@ -525,12 +549,15 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) int noise = this->noise; int delta = amp * 2 - volume; const int tap = (regs [2] & mode_flag ? 8 : 13); + output->set_modified(); - do { + do + { int feedback = (noise << tap) ^ (noise << 14); time += period; - if ( (noise + 1) & 2 ) { + if ( (noise + 1) & 2 ) + { // bits 0 and 1 of noise differ delta = -delta; synth.offset_resampled( rtime, delta, output ); diff --git a/Frameworks/GME/gme/Nes_Oscs.h b/Frameworks/GME/gme/Nes_Oscs.h old mode 100755 new mode 100644 index b675bfb47..0bf922a82 --- a/Frameworks/GME/gme/Nes_Oscs.h +++ b/Frameworks/GME/gme/Nes_Oscs.h @@ -1,6 +1,6 @@ // Private oscillators used by Nes_Apu -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_OSCS_H #define NES_OSCS_H @@ -11,6 +11,8 @@ class Nes_Apu; struct Nes_Osc { + typedef int nes_time_t; + unsigned char regs [4]; bool reg_written [4]; Blip_Buffer* output; @@ -56,7 +58,7 @@ struct Nes_Square : Nes_Envelope int phase; int sweep_delay; - typedef Blip_Synth Synth; + typedef Blip_Synth_Norm Synth; Synth const& synth; // shared between squares Nes_Square( Synth const* s ) : synth( *s ) { } @@ -77,7 +79,7 @@ struct Nes_Triangle : Nes_Osc enum { phase_range = 16 }; int phase; int linear_counter; - Blip_Synth synth; + Blip_Synth_Fast synth; int calc_amp() const; void run( nes_time_t, nes_time_t ); @@ -95,7 +97,7 @@ struct Nes_Triangle : Nes_Osc struct Nes_Noise : Nes_Envelope { int noise; - Blip_Synth synth; + Blip_Synth_Fast synth; void run( nes_time_t, nes_time_t ); void reset() { @@ -126,13 +128,11 @@ struct Nes_Dmc : Nes_Osc 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; + Blip_Synth_Fast synth; + int update_amp_nonlinear( int dac_in ); void start(); void write_register( int, int ); void run( nes_time_t, nes_time_t ); diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp old mode 100755 new mode 100644 index d178407c3..990e60640 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp @@ -1,4 +1,4 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Vrc6_Apu.h" @@ -15,11 +15,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -Nes_Vrc6_Apu::Nes_Vrc6_Apu() +void Nes_Vrc6_Apu::set_output( Blip_Buffer* buf ) { - output( NULL ); - volume( 1.0 ); - reset(); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); } void Nes_Vrc6_Apu::reset() @@ -37,10 +36,11 @@ void Nes_Vrc6_Apu::reset() } } -void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) +Nes_Vrc6_Apu::Nes_Vrc6_Apu() { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); + set_output( NULL ); + volume( 1.0 ); + reset(); } void Nes_Vrc6_Apu::run_until( blip_time_t time ) @@ -107,7 +107,6 @@ 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) ) @@ -120,6 +119,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( delta ) { osc.last_amp += delta; + output->set_modified(); square_synth.offset( time, delta, output ); } @@ -131,6 +131,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( time < end_time ) { int phase = osc.phase; + output->set_modified(); do { diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.h b/Frameworks/GME/gme/Nes_Vrc6_Apu.h old mode 100755 new mode 100644 index 18722233f..56af076f4 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.h @@ -1,6 +1,6 @@ // Konami VRC6 sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_VRC6_APU_H #define NES_VRC6_APU_H @@ -15,9 +15,9 @@ public: void reset(); void volume( double ); void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_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& ); @@ -49,15 +49,15 @@ private: int period() const { - return (regs [2] & 0x0F) * 0x100L + regs [1] + 1; + return (regs [2] & 0x0F) * 0x100 + regs [1] + 1; } }; Vrc6_Osc oscs [osc_count]; blip_time_t last_time; - Blip_Synth saw_synth; - Blip_Synth square_synth; + Blip_Synth_Fast saw_synth; + Blip_Synth_Norm square_synth; void run_until( blip_time_t ); void run_square( Vrc6_Osc& osc, blip_time_t ); @@ -73,7 +73,7 @@ struct vrc6_apu_state_t BOOST::uint8_t unused; }; -inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) +inline void Nes_Vrc6_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; diff --git a/Frameworks/GME/gme/Nsf_Emu.cpp b/Frameworks/GME/gme/Nsf_Emu.cpp old mode 100755 new mode 100644 index 3670d1dbd..449d43a6d --- a/Frameworks/GME/gme/Nsf_Emu.cpp +++ b/Frameworks/GME/gme/Nsf_Emu.cpp @@ -1,557 +1,342 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 */ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsf_Emu.h" + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" + #include "Nes_Vrc6_Apu.h" + #include "Nes_Fme7_Apu.h" + #include "Nes_Fds_Apu.h" + #include "Nes_Mmc5_Apu.h" + #include "Nes_Vrc7_Apu.h" +#endif + +/* Copyright (C) 2003-2008 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 = { -1.0, 80 }; -Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80 }; - -int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) -{ - return *((Nsf_Emu*) emu)->cpu::get_code( addr ); -} +Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80, 0,0,0,0,0,0,0,0 }; +Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80, 0,0,0,0,0,0,0,0 }; 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; } - -gme_type_t_ const gme_nsf_type [1] = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; - -// 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; - - dprintf( "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( 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; -} + set_type( gme_nsf_type ); + set_silence_lookahead( 6 ); + set_gain( 1.4 ); + set_equalizer( nes_eq ); +} + +Nsf_Emu::~Nsf_Emu() +{ + unload(); +} + +void Nsf_Emu::unload() +{ + core_.unload(); + 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 ) + Music_Emu::copy_field_( out->system, "Famicom" ); +} + +void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.ntsc_speed[0], sizeof(h.ntsc_speed) ); + out.hash_( &h.banks[0], sizeof(h.banks) ); + out.hash_( &h.pal_speed[0], sizeof(h.pal_speed) ); + out.hash_( &h.speed_flags, sizeof(h.speed_flags) ); + out.hash_( &h.chip_flags, sizeof(h.chip_flags) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + + out.hash_( data, data_size ); +} + +blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const +{ + copy_nsf_fields( header(), out ); + return blargg_ok; +} + +static blargg_err_t check_nsf_header( Nsf_Emu::header_t const& h ) +{ + if ( !h.valid_tag() ) + return blargg_err_file_type; + return blargg_ok; +} + +struct Nsf_File : Gme_Info_ +{ + Nsf_Emu::header_t const* h; + + Nsf_File() { set_type( gme_nsf_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Nsf_Emu::header_t const* ) begin; + + if ( h->vers != 1 ) + set_warning( "Unknown file version" ); + + int unsupported_chips = ~Nsf_Core::chips_mask; + #if NSF_EMU_NO_VRC7 + unsupported_chips |= Nsf_Emu::header_t::vrc7_mask; + #endif + if ( h->chip_flags & unsupported_chips ) + 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 blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_nsf_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } +static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } + +gme_type_t_ const gme_nsf_type [1] = {{ "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }}; + +// Setup + +void Nsf_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +void Nsf_Emu::append_voices( const char* const names [], int const types [], int count ) +{ + assert( voice_count_ + count < max_voices ); + for ( int i = 0; i < count; i++ ) + { + voice_names_ [voice_count_ + i] = names [i]; + voice_types_ [voice_count_ + i] = types [i]; + } + voice_count_ += count; + set_voice_count( voice_count_ ); + set_voice_types( voice_types_ ); +} + +blargg_err_t Nsf_Emu::init_sound() +{ + voice_count_ = 0; + set_voice_names( voice_names_ ); + + { + int const count = Nes_Apu::osc_count; + static const char* const names [Nes_Apu::osc_count] = { + "Square 1", "Square 2", "Triangle", "Noise", "DMC" + }; + static int const types [count] = { + wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1 + }; + append_voices( names, types, count ); + } + + // Make adjusted_gain * 0.75 = 1.0 so usual APU and one sound chip uses 1.0 + double adjusted_gain = 1.0 / 0.75 * gain(); + +#if !NSF_EMU_APU_ONLY + // TODO: order of chips here must match that in set_voice() + + if ( core_.vrc6_apu() ) + { + int const count = Nes_Vrc6_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "Saw Wave" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.fme7_apu() ) + { + int const count = Nes_Fme7_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "Square 5" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.mmc5_apu() ) + { + int const count = Nes_Mmc5_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "PCM" + }; + static int const types [count] = { + wave_type+3, wave_type+4, mixed_type+2 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.fds_apu() ) + { + int const count = Nes_Fds_Apu::osc_count; + static const char* const names [count] = { + "FM" + }; + static int const types [count] = { + wave_type+0 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.namco_apu() ) + { + int const count = Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, wave_type+ 6, + wave_type+7, wave_type+8, wave_type+9, wave_type+10, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.vrc7_apu() ) + { + int const count = Nes_Vrc7_Apu::osc_count; + static const char* const names [count] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, wave_type+6, + wave_type+7, wave_type+8 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.vrc7_apu() ) core_.vrc7_apu() ->volume( adjusted_gain ); + if ( core_.namco_apu() ) core_.namco_apu()->volume( adjusted_gain ); + if ( core_.vrc6_apu() ) core_.vrc6_apu() ->volume( adjusted_gain ); + if ( core_.fme7_apu() ) core_.fme7_apu() ->volume( adjusted_gain ); + if ( core_.mmc5_apu() ) core_.mmc5_apu() ->volume( adjusted_gain ); + if ( core_.fds_apu() ) core_.fds_apu() ->volume( adjusted_gain ); +#endif + + if ( adjusted_gain > gain() ) + adjusted_gain = gain(); // only occurs if no other sound chips + + core_.nes_apu()->volume( adjusted_gain ); + + return blargg_ok; +} + +blargg_err_t Nsf_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_track_count( header().track_count ); + RETURN_ERR( check_nsf_header( header() ) ); + set_warning( core_.warning() ); + RETURN_ERR( init_sound() ); + set_tempo( tempo() ); + return setup_buffer( (int) (header().clock_rate() + 0.5) ); +} + +void Nsf_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.nes_apu()->treble_eq( eq ); + + #if !NSF_EMU_APU_ONLY + { + if ( core_.namco_apu() ) core_.namco_apu()->treble_eq( eq ); + if ( core_.vrc6_apu() ) core_.vrc6_apu() ->treble_eq( eq ); + if ( core_.fme7_apu() ) core_.fme7_apu() ->treble_eq( eq ); + if ( core_.mmc5_apu() ) core_.mmc5_apu() ->treble_eq( eq ); + if ( core_.fds_apu() ) core_.fds_apu() ->treble_eq( eq ); + if ( core_.vrc7_apu() ) core_.vrc7_apu() ->treble_eq( eq ); + } + #endif +} + +void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) +{ + #define HANDLE_CHIP( chip ) \ + if ( chip && (i -= chip->osc_count) < 0 )\ + {\ + chip->set_output( i + chip->osc_count, buf );\ + return;\ + }\ + + HANDLE_CHIP( core_.nes_apu() ); + + #if !NSF_EMU_APU_ONLY + { + // TODO: order of chips here must match that in init_sound() + HANDLE_CHIP( core_.vrc6_apu() ); + HANDLE_CHIP( core_.fme7_apu() ); + HANDLE_CHIP( core_.mmc5_apu() ); + HANDLE_CHIP( core_.fds_apu() ); + HANDLE_CHIP( core_.namco_apu() ); + HANDLE_CHIP( core_.vrc7_apu() ); + } + #endif +} + +blargg_err_t Nsf_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + return core_.start_track( track ); +} + +blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) +{ + core_.end_frame( duration ); + const char* w = core_.warning(); + if ( w ) + set_warning( w ); + return blargg_ok; +} + +blargg_err_t Nsf_Emu::hash_( Hash_Function& out ) const +{ + hash_nsf_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Nsf_Emu.h b/Frameworks/GME/gme/Nsf_Emu.h old mode 100755 new mode 100644 index e06b91727..9599a2a25 --- a/Frameworks/GME/gme/Nsf_Emu.h +++ b/Frameworks/GME/gme/Nsf_Emu.h @@ -1,106 +1,55 @@ // Nintendo NES/Famicom NSF music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef NSF_EMU_H #define NSF_EMU_H #include "Classic_Emu.h" -#include "Nes_Apu.h" -#include "Nes_Cpu.h" +#include "Nsf_Core.h" -class Nsf_Emu : private Nes_Cpu, public Classic_Emu { - typedef Nes_Cpu cpu; +void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ); + +class Nsf_Emu : public Classic_Emu { 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]; - }; + // NSF file header (see Nsf_Impl.h) + typedef Nsf_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } + header_t const& header() const { return core_.header(); } + + blargg_err_t hash_( Hash_Function& ) const; static gme_type_t static_type() { return gme_nsf_type; } -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - + Nsf_Core& core() { return core_; } + public: Nsf_Emu(); ~Nsf_Emu(); - Nes_Apu* apu_() { return &apu; } + virtual void unload(); + 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 }; + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); 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 ); + enum { max_voices = 32 }; + const char* voice_names_ [32]; + int voice_types_ [32]; + int voice_count_; + Nsf_Core core_; + blargg_err_t init_sound(); - - header_t header_; - - enum { sram_addr = 0x6000 }; - byte sram [0x2000]; - byte unmapped_code [Nes_Cpu::page_size + 8]; + void append_voices( const char* const names [], int const types [], int count ); }; #endif diff --git a/Frameworks/GME/gme/Nsfe_Emu.cpp b/Frameworks/GME/gme/Nsfe_Emu.cpp old mode 100755 new mode 100644 index 0a785e609..af99fc24a --- a/Frameworks/GME/gme/Nsfe_Emu.cpp +++ b/Frameworks/GME/gme/Nsfe_Emu.cpp @@ -1,330 +1,329 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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] ); - - //dprintf( "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 = (BOOST::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; } - -gme_type_t_ const gme_nsfe_type [1] = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; - -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 ) ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsfe_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2005-2009 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() +{ + data.clear(); + 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, int 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, Nsfe_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 (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err); + if ( memcmp( signature, "NSFE", 4 ) ) + return blargg_err_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 ) ); + int size = get_le32( block_header [0] ); + int tag = get_le32( block_header [1] ); + + //dprintf( "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 blargg_err_file_corrupt; + + nsfe_info_t finfo; + finfo.track_count = 1; + finfo.first_track = 0; + + RETURN_ERR( in.read( &finfo, min( size, (int) 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 blargg_err_file_corrupt; + 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( data.resize( size ) ); + RETURN_ERR( in.read( data.begin(), size ) ); + } + else + { + Subset_Reader sub( &in, size ); // limit emu to nsf data + Remaining_Reader rem( &header, header.size, &sub ); + RETURN_ERR( nsf_emu->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 blargg_ok; +} + +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() ) + { + int length = (BOOST::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 blargg_ok; +} + +Nsfe_Emu::Nsfe_Emu() +{ + set_type( gme_nsfe_type ); +} + +Nsfe_Emu::~Nsfe_Emu() { } + +void Nsfe_Emu::unload() +{ + info.unload(); + 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 blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + return info.track_info_( out, track ); + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_nsf_file( info.info, info.data.begin(), info.data.end() - info.data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } +static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } + +gme_type_t_ const gme_nsfe_type [1] = {{ "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }}; + +blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( info.load( in, this ) ); + disable_playlist_( false ); + return blargg_ok; +} + +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_( true ); + 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/Frameworks/GME/gme/Nsfe_Emu.h b/Frameworks/GME/gme/Nsfe_Emu.h old mode 100755 new mode 100644 index 561c3be0f..a166bfc66 --- a/Frameworks/GME/gme/Nsfe_Emu.h +++ b/Frameworks/GME/gme/Nsfe_Emu.h @@ -1,16 +1,17 @@ // Nintendo NES/Famicom NSFE music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef NSFE_EMU_H #define NSFE_EMU_H #include "blargg_common.h" #include "Nsf_Emu.h" +class Nsfe_Emu; // Allows reading info from NSFE file without creating emulator class Nsfe_Info { public: - blargg_err_t load( Data_Reader&, Nsf_Emu* ); + blargg_err_t load( Data_Reader&, Nsfe_Emu* ); struct info_t : Nsf_Emu::header_t { @@ -19,7 +20,9 @@ public: char copyright [256]; char dumper [256]; } info; - + + blargg_vector data; + void disable_playlist( bool = true ); blargg_err_t track_info_( track_info_t* out, int track ) const; @@ -28,8 +31,11 @@ public: void unload(); +// Implementation +public: Nsfe_Info(); ~Nsfe_Info(); + BLARGG_DISABLE_NOTHROW private: blargg_vector track_name_data; blargg_vector track_names; @@ -43,26 +49,26 @@ 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]; }; - 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() + +// Implementation public: Nsfe_Emu(); ~Nsfe_Emu(); + virtual void unload(); + 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_(); + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t start_track_( int ); + virtual void clear_playlist_(); + private: Nsfe_Info info; - bool loading; + + void disable_playlist_( bool b ); + friend class Nsfe_Info; }; #endif diff --git a/Frameworks/GME/gme/Sap_Apu.cpp b/Frameworks/GME/gme/Sap_Apu.cpp old mode 100755 new mode 100644 index 23fa90720..6c7a202a4 --- a/Frameworks/GME/gme/Sap_Apu.cpp +++ b/Frameworks/GME/gme/Sap_Apu.cpp @@ -1,10 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Sap_Apu.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 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 @@ -19,9 +17,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ int const max_frequency = 12000; // pure waves above this frequency are silenced -static void gen_poly( blargg_ulong mask, int count, byte* out ) +static void gen_poly( unsigned mask, int count, byte out [] ) { - blargg_ulong n = 1; + unsigned n = 1; do { int bits = 0; @@ -30,7 +28,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) { // implemented using "Galios configuration" bits |= (n & 1) << b; - n = (n >> 1) ^ (mask & -(n & 1)); + n = (n >> 1) ^ (mask * (n & 1)); } while ( b++ < 7 ); *out++ = bits; @@ -40,16 +38,16 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) // poly5 int const poly5_len = (1 << 5) - 1; -blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; -blargg_ulong const poly5 = 0x167C6EA1; +unsigned const poly5_mask = (1U << poly5_len) - 1; +unsigned const poly5 = 0x167C6EA1; -inline blargg_ulong run_poly5( blargg_ulong in, int shift ) +inline unsigned run_poly5( unsigned 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))) + ((1U << (width - 1 - tap1)) | (1U << (width - 1 - tap2))) Sap_Apu_Impl::Sap_Apu_Impl() { @@ -61,20 +59,25 @@ Sap_Apu_Impl::Sap_Apu_Impl() { 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; + unsigned n = poly5 [3] * 0x1000000 + poly5 [2] * 0x10000 + + poly5 [1] * 0x100 + poly5 [0]; + unsigned rev = n & 1; for ( int i = 1; i < poly5_len; i++ ) rev |= (n >> i & 1) << (poly5_len - i); dprintf( "poly5: 0x%08lX\n", rev ); } } +void Sap_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + Sap_Apu::Sap_Apu() { - impl = 0; - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, 0 ); + impl = NULL; + set_output( NULL ); } void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) @@ -102,14 +105,14 @@ inline void Sap_Apu::calc_periods() osc_t* const osc = &oscs [i]; int const osc_reload = osc->regs [0]; // cache - blargg_long period = (osc_reload + 1) * divider; + int 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; + period = osc_reload * 0x100 + osc [-1].regs [0] + 7; if ( !(this->control & fast_bits [i - 1]) ) period = (period - 6) * divider; @@ -146,8 +149,6 @@ void Sap_Apu::run_until( blip_time_t end_time ) 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 @@ -160,6 +161,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) if ( delta ) { osc->last_amp = volume; + output->set_modified(); impl->synth.offset( last_time, delta, output ); } @@ -208,7 +210,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) poly_inc -= poly_len; // allows more optimized inner loop below // square/poly5 wave - blargg_ulong wave = poly5; + unsigned wave = poly5; check( poly5 & 1 ); // low bit is set for pure wave int poly5_inc = 0; if ( !(osc_control & 0x80) ) @@ -217,6 +219,8 @@ void Sap_Apu::run_until( blip_time_t end_time ) poly5_inc = period % poly5_len; } + output->set_modified(); + // 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. @@ -247,7 +251,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) { if ( wave & 1 ) { - int amp = volume & -(poly [poly_pos >> 3] >> (poly_pos & 7) & 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; @@ -281,7 +285,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) blip_time_t remain = end_time - time; if ( remain > 0 ) { - blargg_long count = (remain + period - 1) / period; + int count = (remain + period - 1) / period; osc->phase ^= count; time += count * period; } @@ -296,11 +300,11 @@ void Sap_Apu::run_until( blip_time_t end_time ) polym_pos += duration; // will get %'d on next call } -void Sap_Apu::write_data( blip_time_t time, unsigned addr, int data ) +void Sap_Apu::write_data( blip_time_t time, int addr, int data ) { run_until( time ); - int i = (addr ^ 0xD200) >> 1; - if ( i < osc_count ) + int i = (addr - 0xD200) >> 1; + if ( (unsigned) i < osc_count ) { oscs [i].regs [addr & 1] = data; } @@ -331,4 +335,5 @@ void Sap_Apu::end_frame( blip_time_t end_time ) run_until( end_time ); last_time -= end_time; + assert( last_time >= 0 ); } diff --git a/Frameworks/GME/gme/Sap_Apu.h b/Frameworks/GME/gme/Sap_Apu.h old mode 100755 new mode 100644 index c71ce31ed..e53834332 --- a/Frameworks/GME/gme/Sap_Apu.h +++ b/Frameworks/GME/gme/Sap_Apu.h @@ -1,6 +1,6 @@ // Atari POKEY sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SAP_APU_H #define SAP_APU_H @@ -11,19 +11,40 @@ class Sap_Apu_Impl; class Sap_Apu { public: +// Basics + + // Sets buffer to generate sound into, or 0 to mute + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Same as set_output(), but for a particular channel enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); - void reset( Sap_Apu_Impl* ); + // Resets sound chip and sets Sap_Apu_Impl + void reset( Sap_Apu_Impl* impl ); - enum { start_addr = 0xD200 }; - enum { end_addr = 0xD209 }; - void write_data( blip_time_t, unsigned addr, int data ); + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xD200 }; + enum { io_size = 0x0A }; - void end_frame( blip_time_t ); +private: + // noncopyable + Sap_Apu( const Sap_Apu& ); + Sap_Apu& operator = ( const Sap_Apu& ); +// Implementation public: Sap_Apu(); + private: struct osc_t { @@ -46,29 +67,34 @@ private: 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 }; + enum { poly4_len = (1 << 4) - 1 }; + enum { poly9_len = (1 << 9) - 1 }; + enum { poly17_len = (1 << 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; + // Set treble with synth.treble_eq() + Blip_Synth_Norm synth; - Sap_Apu_Impl(); + // Sets overall volume, where 1.0is normal void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } + +// Implementation +public: + Sap_Apu_Impl(); + 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]; + BOOST::uint8_t poly4 [Sap_Apu::poly4_len /8 + 1]; + BOOST::uint8_t poly9 [Sap_Apu::poly9_len /8 + 1]; + BOOST::uint8_t poly17 [Sap_Apu::poly17_len/8 + 1]; friend class Sap_Apu; }; -inline void Sap_Apu::osc_output( int i, Blip_Buffer* b ) +inline void Sap_Apu::set_output( int i, Blip_Buffer* b ) { assert( (unsigned) i < osc_count ); oscs [i].output = b; diff --git a/Frameworks/GME/gme/Sap_Cpu.cpp b/Frameworks/GME/gme/Sap_Cpu.cpp old mode 100755 new mode 100644 index 10dc60618..24bd9b202 --- a/Frameworks/GME/gme/Sap_Cpu.cpp +++ b/Frameworks/GME/gme/Sap_Cpu.cpp @@ -1,13 +1,13 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ -#include "Sap_Cpu.h" +#include "Sap_Core.h" -#include #include "blargg_endian.h" +//#define CPU_LOG_MAX 100000 //#include "nes_cpu_log.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -18,994 +18,79 @@ 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; +// functions defined in same file as CPU emulator to help compiler's optimizer -void Sap_Cpu::reset( void* new_mem ) +int Sap_Core::read_d40b() { - 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(); + //dprintf( "D40B read\n" ); + check( cpu.time() >= frame_start ); + return ((unsigned) (cpu.time() - frame_start) / scanline_period % lines_per_frame) / 2; } -#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 )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Sap_Cpu::run( sap_time_t end_time ) +void Sap_Core::write_D2xx( int d2xx, int data ) { - bool illegal_encountered = false; - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - fint32 s_time = s.time; - uint8_t* const mem = this->mem; // cache + addr_t const base = 0xD200; - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 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 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + if ( d2xx < apu_.io_size ) { - fuint8 temp = r.status; - SET_STATUS( temp ); + apu_.write_data( time(), d2xx + base, data ); + return; } - goto loop; -dec_clock_loop: - s_time--; -loop: - - #ifndef NDEBUG + if ( (unsigned) (d2xx - 0x10) < apu2_.io_size && info.stereo ) { - sap_time_t correct = end_time_; - if ( !(status & st_i) && correct > irq_time_ ) - correct = irq_time_; - check( s.base == correct ); + apu2_.write_data( time(), d2xx + (base - 0x10), data ); + return; } - #endif - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - fuint8 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 - - fuint16 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 ) + if ( d2xx == 0xD40A - base ) { -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 ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ + dprintf( "D40A write\n" ); + time_t t = cpu.time(); + time_t into_line = (t - frame_start) % scanline_period; + cpu.set_end_time( t - into_line + scanline_period ); + return; } -#define IND_X( out ) {\ - fuint16 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: + if ( (d2xx & ~0x0010) != 0x0F || data != 0x03 ) + dprintf( "Unmapped write $%04X <- $%02X\n", d2xx + base, data ); +} -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ +inline int Sap_Core::read_mem( addr_t addr ) +{ + int result = mem.ram [addr]; + if ( addr == 0xD40B ) + result = read_d40b(); + else if ( (addr & 0xF900) == 0xD000 ) + dprintf( "Unmapped read $%04X\n", addr ); + return result; +} + + +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) + +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) \ {\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc += offset;\ - s_time += extra_clock >> 8 & 1;\ - goto loop;\ + ram [addr] = data;\ + int d2xx = addr - 0xD200;\ + if ( (unsigned) d2xx < 0x100 )\ + write_D2xx( d2xx, data );\ } -// Often-Used +#define CPU cpu +#define FLAT_MEM ram - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; +#define CPU_BEGIN \ +bool Sap_Core::run_cpu( time_t end )\ +{\ + CPU.set_end_time( end );\ + byte* const ram = this->mem.ram; /* cache */ - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; + #include "Nes_Cpu_run.h" - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - fuint16 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; - - { - fuint16 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 - { - fuint16 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; - } - - { - fuint8 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) ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::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; - fint16 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 = (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 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 - fuint8 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 - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 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 - fuint8 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 = (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: - dprintf( "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; - dprintf( "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: - assert( (unsigned) opcode <= 0xFF ); - illegal_encountered = true; - pc--; - goto stop; - } - assert( false ); - - int result_; -handle_brk: - if ( (pc - 1) >= idle_addr ) - goto idle_done; - pc++; - result_ = 4; - dprintf( "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; - fuint8 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; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return illegal_encountered; + return cpu.time_past_end() < 0; } - diff --git a/Frameworks/GME/gme/Sap_Emu.cpp b/Frameworks/GME/gme/Sap_Emu.cpp old mode 100755 new mode 100644 index 8314fd6e8..c8471890b --- a/Frameworks/GME/gme/Sap_Emu.cpp +++ b/Frameworks/GME/gme/Sap_Emu.cpp @@ -1,442 +1,410 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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; } - -gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; - -// 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 ); - //dprintf( "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 ) - dprintf( "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; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Sap_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2008 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" + +Sap_Emu::Sap_Emu() +{ + set_type( gme_sap_type ); + set_silence_lookahead( 6 ); +} + +Sap_Emu::~Sap_Emu() { } + +// Track info + +// Returns 16 or greater if not hex. Handles uppercase and lowercase. +// Thoroughly tested and rejects ALL non-hex characters. +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static int from_hex( byte const in [] ) +{ + int 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 parse_int( byte const* io [], byte const* end ) +{ + byte const* in = *io; + int n = 0; + while ( in < end ) + { + int dig = *in - '0'; + if ( (unsigned) dig > 9 ) + break; + ++in; + n = n * 10 + dig; + } + if ( in == *io ) + n = -1; // no numeric characters + *io = in; + return n; +} + +static int from_dec( byte const in [], byte const* end ) +{ + int n = parse_int( &in, end ); + if ( in < end ) + n = -1; + 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 int parse_time( byte const in [], byte const* end ) +{ + int minutes = parse_int( &in, end ); + if ( minutes < 0 || *in != ':' ) + return 0; + + ++in; + int seconds = parse_int( &in, end ); + if ( seconds < 0 ) + return 0; + + int time = minutes * 60000 + seconds * 1000; + if ( *in == '.' ) + { + byte const* start = ++in; + int msec = parse_int( &in, end ); + if ( msec >= 0 ) + { + // allow 1-3 digits + for ( int n = in - start; n < 3; n++ ) + msec *= 10; + time += msec; + } + } + + while ( in < end && *in <= ' ' ) + ++in; + + if ( end - in >= 4 && !memcmp( in, "LOOP", 4 ) ) + time = -time; + + return time; +} + +static blargg_err_t parse_info( byte const in [], int size, Sap_Emu::info_t* out ) +{ + out->track_count = 1; + out->author [0] = 0; + out->name [0] = 0; + out->copyright [0] = 0; + + for ( int i = 0; i < Sap_Emu::max_tracks; i++ ) + out->track_times [i] = 0; + + if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) + return blargg_err_file_type; + + int time_count = 0; + 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( "TIME", tag, tag_len ) && time_count < Sap_Emu::max_tracks ) + { + out->track_times [time_count++] = parse_time( in, line_end ); + } + else if ( !strncmp( "INIT", tag, tag_len ) ) + { + out->init_addr = from_hex( in ); + if ( (unsigned) out->init_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "init address" ); + } + else if ( !strncmp( "PLAYER", tag, tag_len ) ) + { + out->play_addr = from_hex( in ); + if ( (unsigned) out->play_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "play address" ); + } + else if ( !strncmp( "MUSIC", tag, tag_len ) ) + { + out->music_addr = from_hex( in ); + if ( (unsigned) out->music_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "music address" ); + } + else if ( !strncmp( "SONGS", tag, tag_len ) ) + { + out->track_count = from_dec( in, line_end ); + if ( out->track_count <= 0 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "track count" ); + } + else if ( !strncmp( "TYPE", tag, tag_len ) ) + { + switch ( out->type = *in ) + { + case 'S': + out->type = 'C'; + case 'B': + case 'C': + case 'D': + break; + + default: + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "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 BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "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 BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "ROM data missing" ); + out->rom_data = in + 2; + + return blargg_ok; +} + +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 ); +} + +static void hash_sap_file( Sap_Emu::info_t const& i, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + unsigned char temp[4]; + set_le32( &temp[0], i.init_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.play_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.music_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.type ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.fastplay ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.stereo ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.track_count ); out.hash_( &temp[0], sizeof(temp) ); + out.hash_( data, data_size ); +} + +blargg_err_t Sap_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_sap_fields( info_, out ); + + if ( track < max_tracks ) + { + int time = info_.track_times [track]; + if ( time ) + { + if ( time > 0 ) + { + out->loop_length = 0; + } + else + { + time = -time; + out->loop_length = time; + } + out->length = time; + } + } + return blargg_ok; +} + +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 [], int size ) + { + RETURN_ERR( parse_info( begin, size, &info ) ); + set_track_count( info.track_count ); + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_sap_fields( info, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_sap_file( info, info.rom_data, file_end() - info.rom_data, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } +static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } + +gme_type_t_ const gme_sap_type [1] = {{ "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }}; + +// Setup + +blargg_err_t Sap_Emu::load_mem_( byte const in [], int size ) +{ + file_end = in + size; + + info_.warning = NULL; + 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 ); + core.apu_impl().volume( gain() ); + + 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 ); + + return setup_buffer( 1773447 ); +} + +void Sap_Emu::update_eq( blip_eq_t const& eq ) +{ + core.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 ) + core.apu2().set_output( i2, right ); + else + core.apu().set_output( i, (info_.stereo ? left : center) ); +} + +// Emulation + +void Sap_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Sap_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + core.setup_ram(); + + // Copy file data to RAM + byte const* in = info_.rom_data; + while ( file_end - in >= 5 ) + { + int start = get_le16( in ); + int end = get_le16( in + 2 ); + //dprintf( "Block $%04X-$%04X\n", start, end ); + in += 4; + int len = end - start + 1; + if ( (unsigned) len > (unsigned) (file_end - in) ) + { + set_warning( "Invalid file data block" ); + break; + } + + memcpy( core.ram() + start, in, len ); + in += len; + if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) + in += 2; + } + + return core.start_track( track, info_ ); +} + +blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core.end_frame( duration ); +} + +blargg_err_t Sap_Emu::hash_( Hash_Function& out ) const +{ + hash_sap_file( info(), info().rom_data, file_end - info().rom_data, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sap_Emu.h b/Frameworks/GME/gme/Sap_Emu.h old mode 100755 new mode 100644 index 4878faa66..aac1acc5f --- a/Frameworks/GME/gme/Sap_Emu.h +++ b/Frameworks/GME/gme/Sap_Emu.h @@ -1,69 +1,53 @@ // Atari XL/XE SAP music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SAP_EMU_H #define SAP_EMU_H #include "Classic_Emu.h" #include "Sap_Apu.h" -#include "Sap_Cpu.h" +#include "Sap_Core.h" -class Sap_Emu : private Sap_Cpu, public Classic_Emu { - typedef Sap_Cpu cpu; +class Sap_Emu : public Classic_Emu { public: - static gme_type_t static_type() { return gme_sap_type; } -public: - Sap_Emu(); - ~Sap_Emu(); - struct info_t { + enum { max_tracks = 32 }; // TODO: no fixed limit + + // SAP file info (see Sap_Core.h for more) + struct info_t : Sap_Core::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; + int track_times [max_tracks]; char author [256]; char name [256]; char copyright [ 32]; }; + + // Info for currently loaded file + info_t const& info() const { return info_; } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_sap_type; } + +// Implementation +public: + Sap_Emu(); + ~Sap_Emu(); + 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 ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + private: - info_t info; - + 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]; - byte padding2 [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 ); + Sap_Core core; }; #endif diff --git a/Frameworks/GME/gme/Sms_Apu.cpp b/Frameworks/GME/gme/Sms_Apu.cpp old mode 100755 new mode 100644 index b41fdec41..3a50434cd --- a/Frameworks/GME/gme/Sms_Apu.cpp +++ b/Frameworks/GME/gme/Sms_Apu.cpp @@ -1,8 +1,8 @@ -// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/ +// Sms_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Sms_Apu.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 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 @@ -15,253 +15,222 @@ 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() -{ -} +int const noise_osc = 3; void Sms_Apu::volume( double vol ) { - vol *= 0.85 / (osc_count * 64 * 2); - square_synth.volume( vol ); - noise.synth.volume( vol ); + vol *= 0.85 / osc_count / 64; + norm_synth.volume( vol ); + fast_synth.volume( vol ); } -void Sms_Apu::treble_eq( const blip_eq_t& eq ) +void Sms_Apu::treble_eq( blip_eq_t const& eq ) { - square_synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); } -void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +inline int Sms_Apu::calc_output( int i ) const { - 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]; + int flags = ggstereo >> i; + return (flags >> 3 & 2) | (flags & 1); } -void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +void Sms_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( center ) + { + unsigned const divisor = 16384 * 16 * 2; + min_tone_period = ((unsigned) center->clock_rate() + divisor/2) / divisor; + } + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = NULL; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Sms_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} + +static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width ) +{ + unsigned galois = 0; + while ( --width >= 0 ) + { + galois = (galois << 1) | (fibonacci & 1); + fibonacci >>= 1; + } + return galois; } void Sms_Apu::reset( unsigned feedback, int noise_width ) { last_time = 0; - latch = 0; + latch = 0; + ggstereo = 0; + // Calculate noise feedback values if ( !feedback || !noise_width ) { - feedback = 0x0009; + feedback = 0x0009; noise_width = 16; } - // convert to "Galios configuration" looped_feedback = 1 << (noise_width - 1); - noise_feedback = 0; - while ( noise_width-- ) + noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width ); + + // Reset oscs + for ( int i = osc_count; --i >= 0; ) { - noise_feedback = (noise_feedback << 1) | (feedback & 1); - feedback >>= 1; + Osc& o = oscs [i]; + o.output = NULL; + o.last_amp = 0; + o.delay = 0; + o.phase = 0; + o.period = 0; + o.volume = 15; // silent } - squares [0].reset(); - squares [1].reset(); - squares [2].reset(); - noise.reset(); + oscs [noise_osc].phase = 0x8000; + write_ggstereo( 0, 0xFF ); +} + +Sms_Apu::Sms_Apu() +{ + min_tone_period = 7; + + // Clear outputs to NULL FIRST + ggstereo = 0; + set_output( NULL ); + + volume( 1.0 ); + reset(); } void Sms_Apu::run_until( blip_time_t end_time ) { - require( end_time >= last_time ); // end_time must not be before previous time + require( end_time >= last_time ); + if ( end_time <= last_time ) + return; - if ( end_time > last_time ) + // Synthesize each oscillator + for ( int idx = osc_count; --idx >= 0; ) { - // run oscillators - for ( int i = 0; i < osc_count; ++i ) + Osc& osc = oscs [idx]; + int vol = 0; + int amp = 0; + + // Determine what will be generated + Blip_Buffer* const out = osc.output; + if ( out ) { - Sms_Osc& osc = *oscs [i]; - if ( osc.output ) + // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) + static unsigned char const volumes [16] = { + 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0 + }; + + vol = volumes [osc.volume]; + amp = (osc.phase & 1) * vol; + + // Square freq above 16 kHz yields constant amplitude at half volume + if ( idx != noise_osc && osc.period < min_tone_period ) { - osc.output->set_modified(); - if ( i < 3 ) - squares [i].run( last_time, end_time ); - else - noise.run( last_time, end_time ); + amp = vol >> 1; + vol = 0; + } + + // Update amplitude + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + norm_synth.offset( last_time, delta, out ); + out->set_modified(); } } - last_time = end_time; + // Generate wave + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + // Calculate actual period + int period = osc.period; + if ( idx == noise_osc ) + { + period = 0x20 << (period & 3); + if ( period == 0x100 ) + period = oscs [2].period * 2; + } + period *= 0x10; + if ( !period ) + period = 0x10; + + // Maintain phase when silent + int phase = osc.phase; + if ( !vol ) + { + int count = (end_time - time + period - 1) / period; + time += count * period; + if ( idx != noise_osc ) // TODO: maintain noise LFSR phase? + phase ^= count & 1; + } + else + { + int delta = amp * 2 - vol; + + if ( idx != noise_osc ) + { + // Square + do + { + delta = -delta; + norm_synth.offset( time, delta, out ); + time += period; + } + while ( time < end_time ); + phase = (delta >= 0); + } + else + { + // Noise + unsigned const feedback = (osc.period & 4 ? noise_feedback : looped_feedback); + do + { + unsigned changed = phase + 1; + phase = ((phase & 1) * feedback) ^ (phase >> 1); + if ( changed & 2 ) // true if bits 0 and 1 differ + { + delta = -delta; + fast_synth.offset_inline( time, delta, out ); + } + time += period; + } + while ( time < end_time ); + check( phase ); + } + osc.last_amp = (phase & 1) * vol; + out->set_modified(); + } + osc.phase = phase; + } + osc.delay = 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; + last_time = end_time; } void Sms_Apu::write_ggstereo( blip_time_t time, int data ) @@ -269,31 +238,30 @@ void Sms_Apu::write_ggstereo( blip_time_t time, int data ) require( (unsigned) data <= 0xFF ); run_until( time ); + ggstereo = data; - for ( int i = 0; i < osc_count; i++ ) + for ( int i = osc_count; --i >= 0; ) { - 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 ) + Osc& osc = oscs [i]; + + Blip_Buffer* old = osc.output; + osc.output = osc.outputs [calc_output( i )]; + if ( osc.output != old ) { - if ( old_output ) + int delta = -osc.last_amp; + if ( delta ) { - old_output->set_modified(); - square_synth.offset( time, -osc.last_amp, old_output ); + osc.last_amp = 0; + if ( old ) + { + old->set_modified(); + fast_synth.offset( last_time, delta, old ); + } } - 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 ); @@ -303,28 +271,101 @@ void Sms_Apu::write_data( blip_time_t time, int data ) if ( data & 0x80 ) latch = data; - int index = (latch >> 5) & 3; + // We want the raw values written so our save state format can be + // as close to hardware as possible and unspecific to any emulator. + int idx = latch >> 5 & 3; + Osc& osc = oscs [idx]; 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); + osc.volume = data & 0x0F; } else { - int select = data & 3; - if ( select < 3 ) - noise.period = &noise_periods [select]; - else - noise.period = &squares [2].period; + if ( idx == noise_osc ) + osc.phase = 0x8000; // reset noise LFSR - noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; - noise.shifter = 0x8000; + // Replace high 6 bits/low 4 bits of register with data + int lo = osc.period; + int hi = data << 4; + if ( idx == noise_osc || (data & 0x80) ) + { + hi = lo; + lo = data; + } + osc.period = (hi & 0x3F0) | (lo & 0x00F); } } + +void Sms_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 ); +} + +#if SMS_APU_CUSTOM_STATE + #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) +#else + #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) + + static unsigned get_val( byte const p [] ) + { + return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; + } + + static void set_val( byte p [], unsigned n ) + { + p [0] = (byte) (n ); + p [1] = (byte) (n >> 8); + p [2] = (byte) (n >> 16); + p [3] = (byte) (n >> 24); + } +#endif + +inline const char* Sms_Apu::save_load( sms_apu_state_t* io, bool save ) +{ + #if !SMS_APU_CUSTOM_STATE + assert( sizeof (sms_apu_state_t) == 128 ); + #endif + + // Format of data, where later format is incompatible with earlier + int format = io->format0; + REFLECT( format, format ); + if ( format != io->format0 ) + return "Unsupported sound save state format"; + + // Version of data, where later versions just add fields to the end + int version = 0; + REFLECT( version, version ); + + REFLECT( latch, latch ); + REFLECT( ggstereo, ggstereo ); + + for ( int i = osc_count; --i >= 0; ) + { + Osc& osc = oscs [i]; + REFLECT( osc.period, periods [i] ); + REFLECT( osc.volume, volumes [i] ); + REFLECT( osc.delay, delays [i] ); + REFLECT( osc.phase, phases [i] ); + } + + return 0; +} + +void Sms_Apu::save_state( sms_apu_state_t* out ) +{ + save_load( out, true ); + #if !SMS_APU_CUSTOM_STATE + memset( out->unused, 0, sizeof out->unused ); + #endif +} + +blargg_err_t Sms_Apu::load_state( sms_apu_state_t const& in ) +{ + RETURN_ERR( save_load( CONST_CAST(sms_apu_state_t*,&in), false ) ); + write_ggstereo( 0, ggstereo ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sms_Apu.h b/Frameworks/GME/gme/Sms_Apu.h old mode 100755 new mode 100644 index 3c11a9c3c..b93e47bc8 --- a/Frameworks/GME/gme/Sms_Apu.h +++ b/Frameworks/GME/gme/Sms_Apu.h @@ -1,75 +1,128 @@ // Sega Master System SN76489 PSG sound chip emulator -// Sms_Snd_Emu 0.1.4 +// Sms_Snd_Emu $vers #ifndef SMS_APU_H #define SMS_APU_H -#include "Sms_Oscs.h" +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct sms_apu_state_t; class Sms_Apu { public: - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to Game Gear left/right assignment byte + void write_ggstereo( blip_time_t t, int data ); - // Set treble equalization - void treble_eq( const blip_eq_t& ); + // Emulates to time t, then writes data + void write_data( blip_time_t t, int data ); - // 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 + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip and sets noise feedback bits and width void reset( unsigned noise_feedback = 0, int noise_width = 0 ); - // Write GameGear left/right assignment byte - void write_ggstereo( blip_time_t, int ); + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - // Write to data port - void write_data( blip_time_t, int ); + // Sets overall volume, where 1.0 is normal + void volume( double ); - // Run all oscillators up to specified time, end current frame, then - // start a new frame at time 0. - void end_frame( blip_time_t ); + // Sets treble equalization + void treble_eq( blip_eq_t const& ); + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( sms_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( sms_apu_state_t const& in ); -public: - Sms_Apu(); - ~Sms_Apu(); private: // noncopyable Sms_Apu( const Sms_Apu& ); Sms_Apu& operator = ( const Sms_Apu& ); + +// Implementation +public: + Sms_Apu(); + ~Sms_Apu() { } + BLARGG_DISABLE_NOTHROW + + // Use set_output() instead + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ) { set_output( c, c, c ); } ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + +private: + struct Osc + { + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int last_amp; + + int volume; + int period; + int delay; + unsigned phase; + }; + + Osc oscs [osc_count]; + int ggstereo; + int latch; - 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; + int min_tone_period; unsigned noise_feedback; unsigned looped_feedback; + Blip_Synth_Fast fast_synth; + Blip_Synth_Norm norm_synth; + int calc_output( int i ) const; void run_until( blip_time_t ); + const char* save_load( sms_apu_state_t*, bool save ); + friend class Sms_Apu_Tester; }; struct sms_apu_state_t { - unsigned char regs [8] [2]; - unsigned char latch; + // If SMS_APU_CUSTOM_STATE is 1, values are stored as normal integers, + // so your code can then save and load them however it likes. Otherwise, + // they are 4-byte arrays in little-endian format, making entire + // structure suitable for direct storage on disk. + +#if SMS_APU_CUSTOM_STATE + typedef int val_t; +#else + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414D53 }; + + val_t format; + val_t version; + val_t latch; + val_t ggstereo; + val_t periods [4]; + val_t volumes [4]; + val_t delays [4]; + val_t phases [4]; + + val_t unused [12]; // for future expansion }; -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/Frameworks/GME/gme/Snes_Spc.cpp b/Frameworks/GME/gme/Snes_Spc.cpp old mode 100755 new mode 100644 index e909ea186..2efa1fd2f --- a/Frameworks/GME/gme/Snes_Spc.cpp +++ b/Frameworks/GME/gme/Snes_Spc.cpp @@ -1,10 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// SPC emulation support: init, sample buffering, reset, SPC loading + +// snes_spc $vers. http://www.slack.net/~ant/ #include "Snes_Spc.h" -#include - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* 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 @@ -17,473 +17,359 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// always in the future (CPU time can go over 0, but not by this much) -int const timer_disabled_time = 127; +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) -Snes_Spc::Snes_Spc() : dsp( mem.ram ), cpu( this, mem.ram ) +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + + +//// Init + +blargg_err_t Snes_Spc::init() { - set_tempo( 1.0 ); + memset( &m, 0, sizeof m ); + dsp.init( RAM ); + + set_sfm_queue( 0, 0 ); - // Put STOP instruction around memory to catch PC underflow/overflow. - memset( mem.padding1, 0xFF, sizeof mem.padding1 ); - memset( mem.padding2, 0xFF, sizeof mem.padding2 ); + m.tempo = tempo_unit; - // A few tracks read from the last four bytes of IPL ROM - boot_rom [sizeof boot_rom - 2] = 0xC0; - boot_rom [sizeof boot_rom - 1] = 0xFF; - memset( boot_rom, 0, sizeof boot_rom - 2 ); -} - -void Snes_Spc::set_tempo( double t ) -{ - int unit = (int) (16.0 / t + 0.5); + // 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; - timer [0].divisor = unit * 8; // 8 kHz - timer [1].divisor = unit * 8; // 8 kHz - timer [2].divisor = unit; // 64 kHz -} - -// Load - -void Snes_Spc::set_ipl_rom( void const* in ) -{ - memcpy( boot_rom, in, sizeof boot_rom ); -} - -blargg_err_t Snes_Spc::load_spc( const void* data, long size ) -{ - struct spc_file_t { - char signature [27]; - char unused [10]; - uint8_t pc [2]; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - char unused2 [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - uint8_t ipl_rom [128]; + 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 }; - assert( offsetof (spc_file_t,ipl_rom) == spc_file_size ); - const spc_file_t* spc = (spc_file_t const*) data; - - if ( size < spc_file_size ) - return "Not an SPC file"; - - if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) - return "Not an SPC file"; - - registers_t regs; - regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; - regs.a = spc->a; - regs.x = spc->x; - regs.y = spc->y; - regs.status = spc->status; - regs.sp = spc->sp; - - if ( (unsigned long) size >= sizeof *spc ) - set_ipl_rom( spc->ipl_rom ); - - const char* error = load_state( regs, spc->ram, spc->dsp ); - - echo_accessed = false; - - return error; -} - -void Snes_Spc::clear_echo() -{ - if ( !(dsp.read( 0x6C ) & 0x20) ) + // unpack cycle table + for ( int i = 0; i < 128; i++ ) { - unsigned addr = 0x100 * dsp.read( 0x6D ); - size_t size = 0x800 * dsp.read( 0x7D ); - memset( mem.ram + addr, 0xFF, min( size, sizeof mem.ram - addr ) ); + 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 blargg_ok; } -// Handle other file formats (emulator save states) in user code, not here. - -blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram, - const void* dsp_state ) +void Snes_Spc::init_rom( uint8_t const in [rom_size] ) { - // cpu - cpu.r = cpu_state; + 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 - // Allow DSP to generate one sample before code starts - // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it - // clears KON 31 cycles from starting execution. It works on the SNES - // since the SPC player adds a few extra cycles delay after restoring - // KON from the DSP registers at the end of an SPC file). - extra_cycles = 32; - - // ram - memcpy( mem.ram, new_ram, sizeof mem.ram ); - memcpy( extra_ram, mem.ram + rom_addr, sizeof extra_ram ); - - // boot rom (have to force enable_rom() to update it) - rom_enabled = !(mem.ram [0xF1] & 0x80); - enable_rom( !rom_enabled ); - - // dsp - dsp.reset(); + 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; +} + +// 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 < Spc_Dsp::register_count; i++ ) - dsp.write( i, ((uint8_t const*) dsp_state) [i] ); - - // timers for ( i = 0; i < timer_count; i++ ) { - Timer& t = timer [i]; - - t.next_tick = 0; - t.enabled = (mem.ram [0xF1] >> i) & 1; - if ( !t.enabled ) - t.next_tick = timer_disabled_time; - t.count = 0; - t.counter = mem.ram [0xFD + i] & 15; - - int p = mem.ram [0xFA + i]; - t.period = p ? p : 0x100; + 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; } - // Handle registers which already give 0 when read by setting RAM and not changing it. - // Put STOP instruction in registers which can be read, to catch attempted CPU execution. - mem.ram [0xF0] = 0; - mem.ram [0xF1] = 0; - mem.ram [0xF3] = 0xFF; - mem.ram [0xFA] = 0; - mem.ram [0xFB] = 0; - mem.ram [0xFC] = 0; - mem.ram [0xFD] = 0xFF; - mem.ram [0xFE] = 0xFF; - mem.ram [0xFF] = 0xFF; - - return 0; // success + set_tempo( m.tempo ); } -// Hardware - -// Current time starts negative and ends at 0 -inline spc_time_t Snes_Spc::time() const +// Loads registers from unified 16-byte format +void Snes_Spc::load_regs( uint8_t const in [reg_count] ) { - return -cpu.remain(); + 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; } -// Keep track of next time to run and avoid a function call if it hasn't been reached. - -// Timers - -void Snes_Spc::Timer::run_until_( spc_time_t time ) +// 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() { - if ( !enabled ) - dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); - assert( enabled ); // when disabled, next_tick should always be in the future + m.rom_enabled = 0; + load_regs( &RAM [0xF0] ); - int elapsed = ((time - next_tick) / divisor) + 1; - next_tick += elapsed * divisor; - - elapsed += count; - if ( elapsed >= period ) // avoid unnecessary division - { - int n = elapsed / period; - elapsed -= n * period; - counter = (counter + n) & 15; - } - count = elapsed; + // Put STOP instruction around memory to catch PC underflow/overflow + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); + memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); } -// DSP - -const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second - -void Snes_Spc::run_dsp_( spc_time_t time ) +// Registers were just loaded. Applies these new values. +void Snes_Spc::regs_loaded() { - int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample - sample_t* buf = sample_buf; - if ( buf ) { - sample_buf = buf + count * 2; // stereo - assert( sample_buf <= buf_end ); - } - next_dsp += count * clocks_per_sample; - dsp.run( count, buf ); + enable_rom( REGS [r_control] & 0x80 ); + timers_loaded(); } -inline void Snes_Spc::run_dsp( spc_time_t time ) +void Snes_Spc::reset_time_regs() { - if ( time >= next_dsp ) - run_dsp_( time ); -} - -// 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. -inline void Snes_Spc::check_for_echo_access( spc_addr_t addr ) -{ - if ( !echo_accessed && !(dsp.read( 0x6C ) & 0x20) ) - { - // ** If echo accesses are found that require running the DSP, cache - // the start and end address on DSP writes to speed up checking. - - unsigned start = 0x100 * dsp.read( 0x6D ); - unsigned end = start + 0x800 * dsp.read( 0x7D ); - if ( start <= addr && addr < end ) { - echo_accessed = true; - dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); - } - } -} - -// Read - -int Snes_Spc::read( spc_addr_t addr ) -{ - int result = mem.ram [addr]; + m.cpu_error = NULL; + m.echo_accessed = 0; + m.spc_time = 0; + m.dsp_time = 0; + #if SPC_LESS_ACCURATE + m.dsp_time = clocks_per_sample + 1; + #endif - if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled ) - dprintf( "Read from ROM: %04X -> %02X\n", addr, result ); - - if ( unsigned (addr - 0xF0) < 0x10 ) - { - assert( 0xF0 <= addr && addr <= 0xFF ); - - // counters - int i = addr - 0xFD; - if ( i >= 0 ) - { - Timer& t = timer [i]; - t.run_until( time() ); - int old = t.counter; - t.counter = 0; - return old; - } - - // dsp - if ( addr == 0xF3 ) - { - run_dsp( time() ); - if ( mem.ram [0xF2] >= Spc_Dsp::register_count ) - dprintf( "DSP read from $%02X\n", (int) mem.ram [0xF2] ); - return dsp.read( mem.ram [0xF2] & 0x7F ); - } - - if ( addr == 0xF0 || addr == 0xF1 || addr == 0xF8 || - addr == 0xF9 || addr == 0xFA ) - dprintf( "Read from register $%02X\n", (int) addr ); - - // Registers which always read as 0 are handled by setting mem.ram [reg] to 0 - // at startup then never changing that value. - - check(( check_for_echo_access( addr ), true )); - } - - return result; -} - - -// Write - -void Snes_Spc::enable_rom( bool enable ) -{ - if ( rom_enabled != enable ) - { - rom_enabled = enable; - memcpy( mem.ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); - // TODO: ROM can still get overwritten when DSP writes to echo buffer - } -} - -void Snes_Spc::write( spc_addr_t addr, int data ) -{ - // first page is very common - if ( addr < 0xF0 ) { - mem.ram [addr] = (uint8_t) data; - } - else switch ( addr ) - { - // RAM - default: - check(( check_for_echo_access( addr ), true )); - if ( addr < rom_addr ) { - mem.ram [addr] = (uint8_t) data; - } - else { - extra_ram [addr - rom_addr] = (uint8_t) data; - if ( !rom_enabled ) - mem.ram [addr] = (uint8_t) data; - } - break; - - // DSP - //case 0xF2: // mapped to RAM - case 0xF3: { - run_dsp( time() ); - int reg = mem.ram [0xF2]; - if ( next_dsp > 0 ) { - // skip mode - - // key press - if ( reg == 0x4C ) - keys_pressed |= data & ~dsp.read( 0x5C ); - - // key release - if ( reg == 0x5C ) { - keys_released |= data; - keys_pressed &= ~data; - } - } - if ( reg < Spc_Dsp::register_count ) { - dsp.write( reg, data ); - } - else { - dprintf( "DSP write to $%02X\n", (int) reg ); - } - break; - } - - case 0xF0: // Test register - dprintf( "Wrote $%02X to $F0\n", (int) data ); - break; - - // Config - case 0xF1: - { - // timers - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( !(data & (1 << i)) ) { - t.enabled = 0; - t.next_tick = timer_disabled_time; - } - else if ( !t.enabled ) { - // just enabled - t.enabled = 1; - t.counter = 0; - t.count = 0; - t.next_tick = time(); - } - } - - // port clears - if ( data & 0x10 ) { - mem.ram [0xF4] = 0; - mem.ram [0xF5] = 0; - } - if ( data & 0x20 ) { - mem.ram [0xF6] = 0; - mem.ram [0xF7] = 0; - } - - enable_rom( (data & 0x80) != 0 ); - - break; - } - - // Ports - case 0xF4: - case 0xF5: - case 0xF6: - case 0xF7: - // to do: handle output ports - break; - - //case 0xF8: // verified on SNES that these are read/write (RAM) - //case 0xF9: - - // Timers - case 0xFA: - case 0xFB: - case 0xFC: { - Timer& t = timer [addr - 0xFA]; - if ( (t.period & 0xFF) != data ) { - t.run_until( time() ); - t.period = data ? data : 0x100; - } - break; - } - - // Counters (cleared on write) - case 0xFD: - case 0xFE: - case 0xFF: - dprintf( "Wrote to counter $%02X\n", (int) addr ); - timer [addr - 0xFD].counter = 0; - break; - } -} - -// Play - -blargg_err_t Snes_Spc::skip( long count ) -{ - if ( count > 4 * 32000L ) - { - // don't run DSP for long durations (2-3 times faster) - - const long sync_count = 32000L * 2; - - // keep track of any keys pressed/released (and not subsequently released) - keys_pressed = 0; - keys_released = 0; - // sentinel tells play to ignore DSP - RETURN_ERR( play( count - sync_count, skip_sentinel ) ); - - // press/release keys now - dsp.write( 0x5C, keys_released & ~keys_pressed ); - dsp.write( 0x4C, keys_pressed ); - - clear_echo(); - - // play the last few seconds normally to help synchronize DSP - count = sync_count; - } - - return play( count ); -} - -blargg_err_t Snes_Spc::play( long count, sample_t* out ) -{ - require( count % 2 == 0 ); // output is always in pairs of samples - - // CPU time() runs from -duration to 0 - spc_time_t duration = (count / 2) * clocks_per_sample; - - // DSP output is made on-the-fly when the CPU reads/writes DSP registers - sample_buf = out; - buf_end = out + (out && out != skip_sentinel ? count : 0); - next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample; - - // Localize timer next_tick times and run them to the present to prevent a running - // but ignored timer's next_tick from getting too far behind and overflowing. for ( int i = 0; i < timer_count; i++ ) { - Timer& t = timer [i]; - if ( t.enabled ) - { - t.next_tick -= duration; - t.run_until( -duration ); - } + Timer* t = &m.timers [i]; + t->next_time = 1; + t->divider = 0; } - // Run CPU for duration, reduced by any extra cycles from previous run - int elapsed = cpu.run( duration - extra_cycles ); - if ( elapsed > 0 ) - { - dprintf( "Unhandled instruction $%02X, pc = $%04X\n", - (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); - return "Emulation error (illegal/unsupported instruction)"; - } - extra_cycles = -elapsed; + regs_loaded(); - // Catch DSP up to present. - run_dsp( 0 ); - if ( out ) { - assert( next_dsp == clocks_per_sample ); - assert( out == skip_sentinel || sample_buf - out == count ); - } - buf_end = 0; - - return 0; + 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 blargg_ok; +} + +void Snes_Spc::clear_echo(bool force) +{ + if ( ( force || !m.echo_cleared ) && !(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 ); + m.echo_cleared = true; + } +} + + +//// 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 = NULL; + + dsp.set_output( NULL, 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 = NULL; + return err; +} + +blargg_err_t Snes_Spc::skip( int count ) +{ + #if SPC_LESS_ACCURATE + if ( count > 2 * sample_rate * 2 ) + { + set_output( NULL, 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, NULL ); } diff --git a/Frameworks/GME/gme/Snes_Spc.h b/Frameworks/GME/gme/Snes_Spc.h old mode 100755 new mode 100644 index b558fb71d..12679a038 --- a/Frameworks/GME/gme/Snes_Spc.h +++ b/Frameworks/GME/gme/Snes_Spc.h @@ -1,121 +1,311 @@ -// Super Nintendo (SNES) SPC-700 APU Emulator +// SNES SPC-700 APU emulator -// Game_Music_Emu 0.5.2 +// snes_spc $vers #ifndef SNES_SPC_H #define SNES_SPC_H -#include "blargg_common.h" -#include "Spc_Cpu.h" #include "Spc_Dsp.h" +#include "blargg_endian.h" + +class Sfm_Emu; + +struct Snes_Spc { + friend class Sfm_Emu; -class Snes_Spc { public: + typedef BOOST::uint8_t uint8_t; - // Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true. - enum { spc_file_size = 0x10180 }; - blargg_err_t load_spc( const void* spc, long spc_size ); + // Must be called once before using + blargg_err_t init(); - // Generate 'count' samples and optionally write to 'buf'. Count must be even. - // Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first. + // 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; - blargg_err_t play( long count, sample_t* buf = NULL ); + 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 }; -// Optional functionality + // 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 ); - // Load copy of state into emulator. - typedef Spc_Cpu::registers_t registers_t; - blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k, - const void* dsp_regs_128 ); +// Sound control - // Clear echo buffer, useful because many tracks have junk in the buffer. - void clear_echo(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { voice_count = Spc_Dsp::voice_count }; + // 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 ); - // Skip forward by the specified number of samples (64000 samples = 1 second) - blargg_err_t skip( long count ); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the - // 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated + // If true, prevents channels and global volumes from being phase-negated. void disable_surround( bool disable = true ); + + // If true, enables cubic interpolation + void interpolation_level( int level = 0 ); - // Set 128 bytes to use for IPL boot ROM. Makes copy. Default is zero filled, - // to avoid including copyrighted code from the SPC-700. - void set_ipl_rom( const void* ); + // 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 ); - void set_tempo( double ); + // Clears echo region. Useful after loading an SPC as many have garbage in echo. + void clear_echo(bool force = false); + + // 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 ); + + // blah + Spc_Dsp const* get_dsp() const; + Spc_Dsp * get_dsp(); + + // SFM Queue + void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); + +// State save/load (only available with accurate DSP) + +#if !SPC_NO_COPY_STATE_FUNCS + // Saves/loads state + enum { state_size = 67 * 1024 }; // 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: - Snes_Spc(); - typedef BOOST::uint8_t uint8_t; -private: - // timers + // TODO: document + struct regs_t + { + int pc; + int a; + int x; + int y; + int psw; + int 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 + + typedef BOOST::uint16_t uint16_t; + + // 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 { - spc_time_t next_tick; + rel_time_t next_time; // time of next event + int prescaler; int period; - int count; - int divisor; + int divider; int enabled; int counter; - - void run_until_( spc_time_t ); - void run_until( spc_time_t time ) - { - if ( time >= next_tick ) - run_until_( time ); - } }; + enum { reg_count = 0x10 }; enum { timer_count = 3 }; - Timer timer [timer_count]; - - // hardware - int extra_cycles; - spc_time_t time() const; - int read( spc_addr_t ); - void write( spc_addr_t, int ); - friend class Spc_Cpu; + enum { extra_size = Spc_Dsp::extra_size }; - // dsp - sample_t* sample_buf; - sample_t* buf_end; // to do: remove this once possible bug resolved - spc_time_t next_dsp; + enum { signature_size = 35 }; + +private: Spc_Dsp dsp; - int keys_pressed; - int keys_released; - sample_t skip_sentinel [1]; // special value for play() passed by skip() - void run_dsp( spc_time_t ); - void run_dsp_( spc_time_t ); - bool echo_accessed; - void check_for_echo_access( spc_addr_t ); - // boot rom - enum { rom_size = 64 }; + #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; + bool echo_cleared; + + 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]; + + uint8_t const* sfm_queue; + uint8_t const* sfm_queue_end; + + unsigned char cycle_table [256]; + + struct + { + // padding to neutralize address overflow + union { + uint8_t padding1 [0x100]; + uint16_t align; // makes compiler align data for 16-bit access + } padding1 [1]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; + } ram; + }; + state_t m; + enum { rom_addr = 0xFFC0 }; - bool rom_enabled; - void enable_rom( bool ); - // CPU and RAM (at end because it's large) - Spc_Cpu cpu; - uint8_t extra_ram [rom_size]; - struct { - // padding to catch jumps before beginning or past end - uint8_t padding1 [0x100]; + 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, int addr ); + void cpu_write_smp_reg ( int data, rel_time_t, int addr ); + void cpu_write_high ( int data, int i, rel_time_t ); + void cpu_write ( int data, int addr, rel_time_t ); + int cpu_read_smp_reg ( int i, rel_time_t ); + int cpu_read ( int addr, rel_time_t ); + unsigned CPU_mem_bit ( uint8_t const* 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 padding2 [0x100]; - } mem; - uint8_t boot_rom [rom_size]; + 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] ); }; -inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } +#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 ); } -inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); } +inline void Snes_Spc::interpolation_level( int level ) { dsp.interpolation_level( level ); } + +inline Spc_Dsp const* Snes_Spc::get_dsp() const { return &dsp; } +inline Spc_Dsp * Snes_Spc::get_dsp() { return &dsp; } + +inline void Snes_Spc::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { m.sfm_queue = queue; m.sfm_queue_end = queue_end; } + +#if !SPC_NO_COPY_STATE_FUNCS +inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } +#endif #endif diff --git a/Frameworks/GME/gme/Spc_Cpu.cpp b/Frameworks/GME/gme/Spc_Cpu.cpp old mode 100755 new mode 100644 index fb9983b83..8622af2ce --- a/Frameworks/GME/gme/Spc_Cpu.cpp +++ b/Frameworks/GME/gme/Spc_Cpu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Core SPC emulation: CPU, timers, SMP registers, memory -#include "Spc_Cpu.h" +// snes_spc $vers. http://www.slack.net/~ant/ -#include "blargg_endian.h" #include "Snes_Spc.h" -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* 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 @@ -18,1045 +17,558 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// Several instructions are commented out (or not even implemented). These aren't -// used by the SPC files tested. +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) -// Optimize performance for the most common instructions, and size for the rest: -// -// 15% 0xF0 BEQ rel -// 8% 0xE4 MOV A,dp -// 4% 0xF5 MOV A,abs+X -// 4% 0xD0 BNE rel -// 4% 0x6F RET -// 4% 0x3F CALL addr -// 4% 0xF4 MOV A,dp+X -// 3% 0xC4 MOV dp,A -// 2% 0xEB MOV Y,dp -// 2% 0x3D INC X -// 2% 0xF6 MOV A,abs+Y -// (1% and below not shown) +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) -Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) +// 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 + + +//// Timers + +#define TIMER_DIV( t, n ) ((n) / t->prescaler) +#define TIMER_MUL( t, n ) ((n) * t->prescaler) + +Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time ) { - remain_ = 0; - assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int - blargg_verify_byte_order(); -} - -#define READ( addr ) (emu.read( addr )) -#define WRITE( addr, value ) (emu.write( addr, value )) - -#define READ_DP( addr ) READ( (addr) + dp ) -#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) - -#define READ_PROG( addr ) (ram [addr]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -int Spc_Cpu::read( spc_addr_t addr ) -{ - return READ( addr ); -} - -void Spc_Cpu::write( spc_addr_t addr, int data ) -{ - WRITE( addr, data ); -} - -// Cycle table derived from text copy of SPC-700 manual (using regular expressions) -static unsigned char const cycle_table [0x100] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 - 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 - 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 - 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B - 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C - 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D - 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E - 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F -}; - -// The C,mem instructions are hardly used, so a non-inline function is used for -// the common access code. -unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) -{ - unsigned addr = READ_PROG16( pc ); - unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); - return (t << 8) & 0x100; -} - -spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) -{ - remain_ = cycle_count; + int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; + t->next_time += TIMER_MUL( t, elapsed ); - uint8_t* const ram = this->ram; // cache - - // Stack pointer is kept one greater than usual SPC stack pointer to allow - // common pre-decrement and post-increment memory instructions that some - // processors have. Address wrap-around isn't supported. - #define PUSH( v ) (*--sp = uint8_t (v)) - #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) - #define POP() (*sp++) - #define SET_SP( v ) (sp = ram + 0x101 + (v)) - #define GET_SP() (sp - 0x101 - ram) - - uint8_t* sp; - SET_SP( r.sp ); - - // registers - unsigned pc = (unsigned) r.pc; - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - const int st_n = 0x80; - const int st_v = 0x40; - const int st_p = 0x20; - const int st_b = 0x10; - const int st_h = 0x08; - const int st_i = 0x04; - const int st_z = 0x02; - const int st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do {\ - out = status & ~(st_n | st_z | st_c);\ - out |= (c >> 8) & st_c;\ - out |= (dp >> 3) & st_p;\ - if ( IS_NEG ) out |= st_n;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & ~(st_n | st_z | st_c | st_p);\ - c = in << 8;\ - nz = (in << 4) & 0x800;\ - nz |= ~in & st_z;\ - dp = (in << 3) & 0x100;\ - } while ( 0 ) - - int status; - int c; // store C as 'c' & 0x100. - int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 - unsigned dp; // direct page base + if ( t->enabled ) { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; - - unsigned data; // first operand of instruction and temporary across function calls - - // Common endings for instructions -cbranch_taken_loop: // compare and branch - pc += (BOOST::int8_t) READ_PROG( pc ); - remain_ -= 2; -inc_pc_loop: // end of instruction with an operand - pc++; -loop: - - check( (unsigned) pc < 0x10000 ); - check( (unsigned) GET_SP() < 0x100 ); - - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - unsigned opcode = READ_PROG( pc ); - pc++; - // to do: if pc is at end of memory, this will get wrong byte - data = READ_PROG( pc ); - - if ( remain_ <= 0 ) - goto stop; - - remain_ -= cycle_table [opcode]; - - // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise - // use a local temporary. - switch ( opcode ) - { - - #define BRANCH( cond ) {\ - pc++;\ - int offset = (BOOST::int8_t) data;\ - if ( cond ) {\ - pc += offset;\ - remain_ -= 2;\ - }\ - goto loop;\ - } - -// Most-Common - - case 0xF0: // BEQ (most common) - BRANCH( !(uint8_t) nz ) - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F: // CALL - PUSH16( pc + 2 ); - pc = READ_PROG16( pc ); - goto loop; - - case 0x6F: // RET - pc = POP(); - pc += POP() * 0x100; - 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:\ - pc++;\ - data += 0x100 * READ_PROG( pc );\ - goto end_##op;\ - CASE( op + 0x0C ) /* dp+X */\ - data = uint8_t (data + x);\ - CASE( op - 0x04 ) /* dp */\ - data += dp;\ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES( 0xE8 ) // MOV A,addr - // case 0xE4: // MOV a,dp (most common) - mov_a_addr: - a = nz = READ( data ); - goto inc_pc_loop; - case 0xBF: // MOV A,(X)+ - data = x + dp; - x = uint8_t (x + 1); - pc--; - goto mov_a_addr; - - 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 - data += dp; - goto mov_x_addr; - case 0xE9: // MOV X,abs - data = READ_PROG16( pc ); - pc++; - mov_x_addr: - data = READ( 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 - data += dp; - goto mov_y_addr; - case 0xEC: // MOV Y,abs - data = READ_PROG16( pc ); - pc++; - mov_y_addr: - data = READ( data ); - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES( 0xC8 ) // MOV addr,A - WRITE( 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( READ_PROG16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = uint8_t (data + y); - case 0xD8: // MOV dp,X - WRITE( data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = uint8_t (data + x); - case 0xCB: // MOV dp,Y - WRITE( data + dp, y ); - goto inc_pc_loop; - - case 0xFA: // MOV dp,dp - data = READ( data + dp ); - case 0x8F: // MOV dp,#imm - pc++; - WRITE_DP( READ_PROG( pc ), data ); - 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( x, a ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func )\ - ADDR_MODES( op ) /* addr */\ - data = READ( data );\ - case op: /* imm */\ - nz = a func##= data;\ - goto inc_pc_loop;\ - { unsigned addr;\ - case op + 0x11: /* X,Y */\ - data = READ_DP( y );\ - addr = x + dp;\ - pc--;\ - goto addr_##op;\ - case op + 0x01: /* dp,dp */\ - data = READ_DP( data );\ - case op + 0x10: /*dp,imm*/\ - pc++;\ - addr = READ_PROG( pc ) + dp;\ - addr_##op:\ - nz = data func READ( addr );\ - WRITE( addr, nz );\ - goto inc_pc_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( data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( x ); - nz = data - READ_DP( y ); - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x69: // CMP (dp),(dp) - data = READ_DP( data ); - case 0x78: // CMP dp,imm - pc++; - nz = READ_DP( READ_PROG( 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_PROG16( pc ); - pc++; - cmp_x_addr: - data = READ( 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_PROG16( pc ); - pc++; - cmp_y_addr: - data = READ( 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( x ); - addr = y + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - pc++; - addr = READ_PROG( pc ) + dp; - adc_addr: - nz = READ( 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( data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - if ( opcode & 0x20 ) - data ^= 0xFF; // SBC - int carry = (c >> 8) & 1; - int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - int hc = (nz & 15) + carry; - c = nz += data + carry; - hc = (nz & 15) - hc; - status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); - if ( addr < 0 ) { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( addr, (uint8_t) nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, n )\ - nz = reg + n;\ - 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_PROG16( pc ); - pc++; - inc_abs: - nz = ((opcode >> 4) & 2) - 1; - nz += READ( data ); - WRITE( 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_PROG16( pc ); - pc++; - rol_mem: - nz = (c >> 8) & 1; - nz |= (c = READ( data ) << 1); - WRITE( 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_PROG16( pc ); - pc++; - ror_mem: { - int temp = READ( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( 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( data ); - nz = (a & 0x7F) | (a >> 1); - y = READ_DP( uint8_t (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( data, a ); - WRITE_DP( uint8_t (data + 1), y ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - data += dp; - - // low byte - int temp = READ( data ); - temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7F; - WRITE( data, (uint8_t) temp ); - - // high byte - data = uint8_t (data + 1) + dp; - temp >>= 8; - temp = uint8_t (temp + READ( data )); - nz |= temp; - WRITE( data, temp ); - - goto inc_pc_loop; - } - - case 0x9A: // SUBW YA,dp - case 0x7A: // ADDW YA,dp - { - // read 16-bit addend - int temp = READ_DP( data ); - int sign = READ_DP( uint8_t (data + 1) ); - temp += 0x100 * sign; - status &= ~(st_v | st_h); - - // to do: fix half-carry for SUBW (it's probably wrong) - - // for SUBW, negate and truncate to 16 bits - if ( opcode & 0x80 ) { - temp = (temp ^ 0xFFFF) + 1; - sign = temp >> 8; - } - - // add low byte (A) - temp += a; - a = (uint8_t) temp; - nz = (temp | (temp >> 1)) & 0x7F; - - // add high byte (Y) - temp >>= 8; - c = y + temp; - nz = (nz | c) & 0xFF; - - // half-carry (temporary avoids CodeWarrior optimizer bug) - unsigned hc = (c & 15) - (y & 15); - status |= (hc >> 4) & st_h; - - // overflow if sign of YA changed when previous sign and addend sign were same - status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; - - y = (uint8_t) c; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( data ); - nz = ((temp >> 1) | temp) & 0x7F; - temp = y + (temp >> 8); - temp -= READ_DP( 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 = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - // behavior based on SPC CPU tests - - status &= ~(st_h | st_v); - - if ( (y & 15) >= (x & 15) ) - status |= st_h; - - if ( y >= x ) - status |= st_v; - - unsigned ya = y * 0x100 + a; - if ( y < x * 2 ) + int remain = IF_0_THEN_256( t->period - t->divider ); + int divider = t->divider + elapsed; + int over = elapsed - remain; + if ( over >= 0 ) { - a = ya / x; - y = ya - a * x; + 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; + + /* Fast DSP only runs every 32nd clock. By adjusting the end time based + on which register is being accessed, in most cases the register access + is emulated at the precise time. */ + 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 ) + { + if ( REGS [r_dspaddr] != Spc_Dsp::r_flg ) + dsp.write( REGS [r_dspaddr], data ); else { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); + int prev = dsp.read( Spc_Dsp::r_flg ); + dsp.write( Spc_Dsp::r_flg, data ); + if ( ( data & 0x20 ) == ( ( data ^ prev ) & 0x20 ) ) clear_echo(); + } + } + else if ( !SPC_MORE_ACCURACY ) + dprintf( "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, int 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 ) + { + //dprintf( "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 ) + dprintf( "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 ) + dprintf( "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; } - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; + // 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; } - -// 11. DECIMAL COMPENSATION COMMANDS - - // seem unused - // case 0xDF: // DAA - // case 0xBE: // DAS - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - pc++; - if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - pc++; - if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xDE: // CBNE dp+X,rel - data = uint8_t (data + x); - // fall through - case 0x2E: // CBNE dp,rel - pc++; - if ( READ_DP( data ) != a ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xFE: // DBNZ Y,rel - y = uint8_t (y - 1); - BRANCH( y ) - - case 0x6E: { // DBNZ dp,rel - pc++; - unsigned temp = READ_DP( data ) - 1; - WRITE_DP( (uint8_t) data, (uint8_t) temp ); - if ( temp ) - goto cbranch_taken_loop; - goto inc_pc_loop; - } - - case 0x1F: // JMP (abs+X) - pc = READ_PROG16( pc ) + x; - // fall through - case 0x5F: // JMP abs - pc = READ_PROG16( pc ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F:{// BRK - check( false ); // untested - PUSH16( pc + 1 ); - pc = READ_PROG16( 0xFFDE ); // vector address verified - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - status = (status | st_b) & ~st_i; - goto loop; - } - - case 0x4F: // PCALL offset - pc++; - PUSH16( pc ); - pc = 0xFF00 + data; - 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: - PUSH16( pc ); - pc = READ_PROG16( 0xFFDE - (opcode >> 3) ); - goto loop; - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = POP(); - pc = POP(); - pc |= POP() << 8; - goto set_status; - case 0x8E: // POP PSW - temp = POP(); - set_status: - SET_STATUS( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - CALC_STATUS( 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 - a = POP(); - goto loop; - - case 0xCE: // POP X - x = POP(); - goto loop; - - case 0xEE: // POP Y - y = POP(); - 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: { - data += dp; - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - WRITE( data, (READ( data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E:{// TCLR1 abs - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data ); - nz = temp & a; - temp &= ~a; - if ( !(opcode & 0x40) ) - temp |= a; - WRITE( data, temp ); - goto loop; - } - - case 0x4A: // AND1 C,mem.bit - c &= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - check( false ); // untested - c &= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - check( false ); // untested - c |= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - check( false ); // untested - c |= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= mem_bit( pc ); - pc += 2; - goto loop; - - case 0xEA: { // NOT1 mem.bit - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - temp ^= 1 << (data >> 13); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xCA: { // MOV1 mem.bit,C - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xAA: // MOV1 C,mem.bit - c = mem_bit( pc ); - pc += 2; - goto loop; - -// 16. PROGRAM STATUS 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 - status &= ~(st_v | st_h); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - check( false ); // untested - status |= st_i; - goto loop; - - case 0xC0: // DI - check( false ); // untested - status &= ~st_i; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - //case 0xEF: // SLEEP - //case 0xFF: // STOP - - } // switch - - // unhandled instructions fall out of switch so emulator can catch them - -stop: - pc--; - - { - int temp; - CALC_STATUS( temp ); - r.status = (uint8_t) temp; - } - - r.pc = pc; - r.sp = (uint8_t) GET_SP(); - r.a = (uint8_t) a; - r.x = (uint8_t) x; - r.y = (uint8_t) y; - - return remain_; } + +void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int 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, int i, rel_time_t time ) +{ + if ( i < rom_size ) + { + m.hi_ram [i] = (uint8_t) data; + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM + } + else + { + assert( RAM [i + rom_addr] == (uint8_t) data ); + RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding + cpu_write( data, i + rom_addr - 0x10000, time ); + } +} + +int const bits_in_int = CHAR_BIT * sizeof (int); + +void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + RAM [addr] = (uint8_t) data; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 64% + { + // $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 != 5 && reg != 6 && reg != 7 ) + // TODO: this is a bit on the fragile side + if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% + cpu_write_smp_reg( data, time, reg ); + } + // High mem/address wrap-around + else + { + reg -= rom_addr - 0xF0; + if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around + cpu_write_high( data, reg, time ); + } + } +} + + +//// 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 + } + reg -= r_cpuio0 - r_dspaddr; + if ( (unsigned) reg <= 3 ) + { + if ( m.sfm_queue && m.sfm_queue < m.sfm_queue_end ) + { + result = *m.sfm_queue++; + } + } + return result; +} + +int Snes_Spc::cpu_read( int 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 \ +BOOST::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/Frameworks/GME/gme/Spc_Cpu.h b/Frameworks/GME/gme/Spc_Cpu.h old mode 100755 new mode 100644 index 2252663bd..dbd1bc2f8 --- a/Frameworks/GME/gme/Spc_Cpu.h +++ b/Frameworks/GME/gme/Spc_Cpu.h @@ -1,57 +1,1225 @@ -// Super Nintendo (SNES) SPC-700 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_CPU_H -#define SPC_CPU_H - -#include "blargg_common.h" - -typedef unsigned spc_addr_t; -typedef blargg_long spc_time_t; - -class Snes_Spc; - -class Spc_Cpu { - typedef BOOST::uint8_t uint8_t; - uint8_t* const ram; -public: - // Keeps pointer to 64K RAM - Spc_Cpu( Snes_Spc* spc, uint8_t* ram ); - - // SPC-700 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - } r; - - // Run CPU for at least 'count' cycles. Return the number of cycles remaining - // when emulation stopped (negative if extra cycles were emulated). Emulation - // stops when there are no more remaining cycles or an unhandled instruction - // is encountered (STOP, SLEEP, and any others not yet implemented). In the - // latter case, the return value is greater than zero. - spc_time_t run( spc_time_t count ); - - // Number of clock cycles remaining for current run() call - spc_time_t remain() const; - - // Access memory as the emulated CPU does - int read ( spc_addr_t ); - void write( spc_addr_t, int ); - -private: - // noncopyable - Spc_Cpu( const Spc_Cpu& ); - Spc_Cpu& operator = ( const Spc_Cpu& ); - unsigned mem_bit( spc_addr_t ); - - spc_time_t remain_; - Snes_Spc& emu; -}; - -inline spc_time_t Spc_Cpu::remain() const { return remain_; } - -#endif +// snes_spc $vers. http://www.slack.net/~ant/ + +/* 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 ) dprintf( "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 ) GET_LE16( ram + (addr) ) + +#define SET_PC( n ) (pc = ram + (n)) +#define GET_PC() (pc - ram) +#define READ_PC( pc ) (*(pc)) +#define READ_PC16( pc ) GET_LE16( pc ) + +// TODO: remove non-wrapping versions? +#define SPC_NO_SP_WRAPAROUND 0 + +#define SET_SP( v ) (sp = ram + 0x101 + (v)) +#define GET_SP() (sp - 0x101 - ram) + +#if SPC_NO_SP_WRAPAROUND +#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) +#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) +#define POP( out ) (void) ((out) = *sp++) + +#else +#define PUSH16( data )\ +{\ + int addr = (sp -= 2) - ram;\ + if ( addr > 0x100 )\ + {\ + SET_LE16( sp, data );\ + }\ + else\ + {\ + ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ + sp [1] = (uint8_t) (data >> 8);\ + sp += 0x100;\ + }\ +} + +#define PUSH( data )\ +{\ + *--sp = (uint8_t) (data);\ + if ( sp - ram == 0x100 )\ + sp += 0x100;\ +} + +#define POP( out )\ +{\ + out = *sp++;\ + if ( sp - ram == 0x201 )\ + {\ + out = sp [-0x101];\ + sp -= 0x100;\ + }\ +} + +#endif + +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) + +unsigned Snes_Spc::CPU_mem_bit( uint8_t const* 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 + +// Flags with hex value for clarity when used as mask. +// 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; + int a = m.cpu_regs.a; + int x = m.cpu_regs.x; + int y = m.cpu_regs.y; + uint8_t const* 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 += *(BOOST::int8_t const*) pc; +inc_pc_loop: + pc++; +loop: +{ + unsigned opcode; + unsigned data; + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + opcode = *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 + + #ifdef CPU_INSTR_HOOK + CPU_INSTR_HOOK( GET_PC(), pc, a, x, y, GET_SP(), rel_time ); + #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) + data = *++pc; + switch ( opcode ) + { + +// Common instructions + +#define BRANCH( cond )\ +{\ + pc++;\ + pc += (BOOST::int8_t) data;\ + if ( cond )\ + goto loop;\ + pc -= (BOOST::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 + #if SPC_NO_SP_WRAPAROUND + { + SET_PC( GET_LE16( sp ) ); + sp += 2; + } + #else + { + int addr = sp - ram; + SET_PC( GET_LE16( sp ) ); + sp += 2; + if ( addr < 0x1FF ) + goto loop; + + SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); + sp -= 0x100; + } + #endif + 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 != 5 && i != 6 && i != 7 ) + if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 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++; + 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*/\ + uint8_t const* 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 = 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; + + 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 += (BOOST::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; + case 0x7F: // RET1 + temp = *sp; + SET_PC( GET_LE16( sp + 1 ) ); + sp += 3; + 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 + unsigned addr = GET_PC() - 1; + if ( addr >= 0x10000 ) + { + addr &= 0xFFFF; + SET_PC( addr ); + dprintf( "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 [*pc]; // undo partial execution of opcode +stop: + + // Uncache registers + if ( GET_PC() >= 0x10000 ) + dprintf( "SPC: PC wrapped around\n" ); + 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/Frameworks/GME/gme/Spc_Dsp.cpp b/Frameworks/GME/gme/Spc_Dsp.cpp old mode 100755 new mode 100644 index 3d934f637..5a6f98d88 --- a/Frameworks/GME/gme/Spc_Dsp.cpp +++ b/Frameworks/GME/gme/Spc_Dsp.cpp @@ -1,666 +1,1419 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -// Based on Brad Martin's OpenSPC DSP emulator - -#include "Spc_Dsp.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2002 Brad Martin */ -/* 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" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) -{ - set_gain( 1.0 ); - mute_voices( 0 ); - disable_surround( false ); - - assert( offsetof (globals_t,unused9 [2]) == register_count ); - assert( sizeof (voice) == register_count ); - blargg_verify_byte_order(); -} - -void Spc_Dsp::mute_voices( int mask ) -{ - for ( int i = 0; i < voice_count; i++ ) - voice_state [i].enabled = (mask >> i & 1) ? 31 : 7; -} - -void Spc_Dsp::reset() -{ - keys = 0; - echo_ptr = 0; - noise_count = 0; - noise = 1; - fir_offset = 0; - - g.flags = 0xE0; // reset, mute, echo off - g.key_ons = 0; - - for ( int i = 0; i < voice_count; i++ ) - { - voice_t& v = voice_state [i]; - v.on_cnt = 0; - v.volume [0] = 0; - v.volume [1] = 0; - v.envstate = state_release; - } - - memset( fir_buf, 0, sizeof fir_buf ); -} - -void Spc_Dsp::write( int i, int data ) -{ - require( (unsigned) i < register_count ); - - reg [i] = data; - int high = i >> 4; - switch ( i & 0x0F ) - { - // voice volume - case 0: - case 1: { - short* volume = voice_state [high].volume; - int left = (int8_t) reg [i & ~1]; - int right = (int8_t) reg [i | 1]; - volume [0] = left; - volume [1] = right; - // kill surround only if enabled and signs of volumes differ - if ( left * right < surround_threshold ) - { - if ( left < 0 ) - volume [0] = -left; - else - volume [1] = -right; - } - break; - } - - // fir coefficients - case 0x0F: - fir_coeff [high] = (int8_t) data; // sign-extend - break; - } -} - -// This table is for envelope timing. It represents the number of counts -// that should be subtracted from the counter each sample period (32kHz). -// The counter starts at 30720 (0x7800). Each count divides exactly into -// 0x7800 without remainder. -const int env_rate_init = 0x7800; -static short const env_rates [0x20] = -{ - 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, - 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, - 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, - 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 -}; - -const int env_range = 0x800; - -inline int Spc_Dsp::clock_envelope( int v ) -{ /* Return value is current - * ENVX */ - raw_voice_t& raw_voice = this->voice [v]; - voice_t& voice = voice_state [v]; - - int envx = voice.envx; - if ( voice.envstate == state_release ) - { - /* - * Docs: "When in the state of "key off". the "click" sound is - * prevented by the addition of the fixed value 1/256" WTF??? - * Alright, I'm going to choose to interpret that this way: - * When a note is keyed off, start the RELEASE state, which - * subtracts 1/256th each sample period (32kHz). Note there's - * no need for a count because it always happens every update. - */ - envx -= env_range / 256; - if ( envx <= 0 ) - { - envx = 0; - keys &= ~(1 << v); - return -1; - } - voice.envx = envx; - raw_voice.envx = envx >> 8; - return envx; - } - - int cnt = voice.envcnt; - int adsr1 = raw_voice.adsr [0]; - if ( adsr1 & 0x80 ) - { - switch ( voice.envstate ) - { - case state_attack: { - // increase envelope by 1/64 each step - int t = adsr1 & 15; - if ( t == 15 ) - { - envx += env_range / 2; - } - else - { - cnt -= env_rates [t * 2 + 1]; - if ( cnt > 0 ) - break; - envx += env_range / 64; - cnt = env_rate_init; - } - if ( envx >= env_range ) - { - envx = env_range - 1; - voice.envstate = state_decay; - } - voice.envx = envx; - break; - } - - case state_decay: { - // Docs: "DR... [is multiplied] by the fixed value - // 1-1/256." Well, at least that makes some sense. - // Multiplying ENVX by 255/256 every time DECAY is - // updated. - cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - int sustain_level = raw_voice.adsr [1] >> 5; - - if ( envx <= (sustain_level + 1) * 0x100 ) - voice.envstate = state_sustain; - break; - } - - case state_sustain: - // Docs: "SR [is multiplied] by the fixed value 1-1/256." - // Multiplying ENVX by 255/256 every time SUSTAIN is - // updated. - cnt -= env_rates [raw_voice.adsr [1] & 0x1F]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - break; - - case state_release: - // handled above - break; - } - } - else - { /* GAIN mode is set */ - /* - * Note: if the game switches between ADSR and GAIN modes - * partway through, should the count be reset, or should it - * continue from where it was? Does the DSP actually watch for - * that bit to change, or does it just go along with whatever - * it sees when it performs the update? I'm going to assume - * the latter and not update the count, unless I see a game - * that obviously wants the other behavior. The effect would - * be pretty subtle, in any case. - */ - int t = raw_voice.gain; - if (t < 0x80) - { - envx = voice.envx = t << 4; - } - else switch (t >> 5) - { - case 4: /* Docs: "Decrease (linear): Subtraction - * of the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= env_range / 64; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 5: /* Docs: "Drecrease (exponential): - * Multiplication by the fixed value - * 1-1/256." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 6: /* Docs: "Increase (linear): Addition of - * the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx += env_range / 64; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - case 7: /* Docs: "Increase (bent line): Addition - * of the constant 1/64 up to .75 of the - * constaint 1/256 from .75 to 1." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - if ( envx < env_range * 3 / 4 ) - envx += env_range / 64; - else - envx += env_range / 256; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - } - } - voice.envcnt = cnt; - raw_voice.envx = envx >> 4; - return envx; -} - -// Clamp n into range -32768 <= n <= 32767 -inline int clamp_16( int n ) -{ - if ( (BOOST::int16_t) n != n ) - n = BOOST::int16_t (0x7FFF - (n >> 31)); - return n; -} - -void Spc_Dsp::run( long count, short* out_buf ) -{ - // to do: make clock_envelope() inline so that this becomes a leaf function? - - // Should we just fill the buffer with silence? Flags won't be cleared - // during this run so it seems it should keep resetting every sample. - if ( g.flags & 0x80 ) - reset(); - - struct src_dir { - char start [2]; - char loop [2]; - }; - - const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; - - int left_volume = g.left_volume; - int right_volume = g.right_volume; - if ( left_volume * right_volume < surround_threshold ) - right_volume = -right_volume; // kill global surround - left_volume *= emu_gain; - right_volume *= emu_gain; - - while ( --count >= 0 ) - { - // Here we check for keys on/off. Docs say that successive writes - // to KON/KOF must be separated by at least 2 Ts periods or risk - // being neglected. Therefore DSP only looks at these during an - // update, and not at the time of the write. Only need to do this - // once however, since the regs haven't changed over the whole - // period we need to catch up with. - - g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. - - if ( g.noise_enables ) - { - noise_count -= env_rates [g.flags & 0x1F]; - if ( noise_count <= 0 ) - { - noise_count = env_rate_init; - - noise_amp = BOOST::int16_t (noise * 2); - - // TODO: switch to Galios style - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - - // What is the expected behavior when pitch modulation is enabled on - // voice 0? Jurassic Park 2 does this. Assume 0 for now. - blargg_long prev_outx = 0; - - int echol = 0; - int echor = 0; - int left = 0; - int right = 0; - for ( int vidx = 0; vidx < voice_count; vidx++ ) - { - const int vbit = 1 << vidx; - raw_voice_t& raw_voice = voice [vidx]; - voice_t& voice = voice_state [vidx]; - - if ( voice.on_cnt && !--voice.on_cnt ) - { - // key on - keys |= vbit; - voice.addr = GET_LE16( sd [raw_voice.waveform].start ); - voice.block_remain = 1; - voice.envx = 0; - voice.block_header = 0; - voice.fraction = 0x3FFF; // decode three samples immediately - voice.interp0 = 0; // BRR decoder filter uses previous two samples - voice.interp1 = 0; - - // NOTE: Real SNES does *not* appear to initialize the - // envelope counter to anything in particular. The first - // cycle always seems to come at a random time sooner than - // expected; as yet, I have been unable to find any - // pattern. I doubt it will matter though, so we'll go - // ahead and do the full time for now. - voice.envcnt = env_rate_init; - voice.envstate = state_attack; - } - - if ( g.key_ons & vbit & ~g.key_offs ) - { - // voice doesn't come on if key off is set - g.key_ons &= ~vbit; - voice.on_cnt = 8; - } - - if ( keys & g.key_offs & vbit ) - { - // key off - voice.envstate = state_release; - voice.on_cnt = 0; - } - - int envx; - if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) - { - raw_voice.envx = 0; - raw_voice.outx = 0; - prev_outx = 0; - continue; - } - - // Decode samples when fraction >= 1.0 (0x1000) - for ( int n = voice.fraction >> 12; --n >= 0; ) - { - if ( !--voice.block_remain ) - { - if ( voice.block_header & 1 ) - { - g.wave_ended |= vbit; - - if ( voice.block_header & 2 ) - { - // verified (played endless looping sample and ENDX was set) - voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); - } - else - { - // first block was end block; don't play anything (verified) - goto sample_ended; // to do: find alternative to goto - } - } - - voice.block_header = ram [voice.addr++]; - voice.block_remain = 16; // nybbles - } - - // if next block has end flag set, *this* block ends *early* (verified) - if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && - (voice.block_header & 3) != 3 ) - { - sample_ended: - g.wave_ended |= vbit; - keys &= ~vbit; - raw_voice.envx = 0; - voice.envx = 0; - // add silence samples to interpolation buffer - do - { - voice.interp3 = voice.interp2; - voice.interp2 = voice.interp1; - voice.interp1 = voice.interp0; - voice.interp0 = 0; - } - while ( --n >= 0 ); - break; - } - - int delta = ram [voice.addr]; - if ( voice.block_remain & 1 ) - { - delta <<= 4; // use lower nybble - voice.addr++; - } - - // Use sign-extended upper nybble - delta = int8_t (delta) >> 4; - - // For invalid ranges (D,E,F): if the nybble is negative, - // the result is F000. If positive, 0000. Nothing else - // like previous range, etc seems to have any effect. If - // range is valid, do the shift normally. Note these are - // both shifted right once to do the filters properly, but - // the output will be shifted back again at the end. - int shift = voice.block_header >> 4; - delta = (delta << shift) >> 1; - if ( shift > 0x0C ) - delta = (delta >> 14) & ~0x7FF; - - // One, two and three point IIR filters - int smp1 = voice.interp0; - int smp2 = voice.interp1; - if ( voice.block_header & 8 ) - { - delta += smp1; - delta -= smp2 >> 1; - if ( !(voice.block_header & 4) ) - { - delta += (-smp1 - (smp1 >> 1)) >> 5; - delta += smp2 >> 5; - } - else - { - delta += (-smp1 * 13) >> 7; - delta += (smp2 + (smp2 >> 1)) >> 4; - } - } - else if ( voice.block_header & 4 ) - { - delta += smp1 >> 1; - delta += (-smp1) >> 5; - } - - voice.interp3 = voice.interp2; - voice.interp2 = smp2; - voice.interp1 = smp1; - voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend - } - - // rate (with possible modulation) - int rate = GET_LE16( raw_voice.rate ) & 0x3FFF; - if ( g.pitch_mods & vbit ) - rate = (rate * (prev_outx + 32768)) >> 15; - - // Gaussian interpolation using most recent 4 samples - int index = voice.fraction >> 2 & 0x3FC; - voice.fraction = (voice.fraction & 0x0FFF) + rate; - const BOOST::int16_t* table = (BOOST::int16_t const*) ((char const*) gauss + index); - const BOOST::int16_t* table2 = (BOOST::int16_t const*) ((char const*) gauss + (255*4 - index)); - int s = ((table [0] * voice.interp3) >> 12) + - ((table [1] * voice.interp2) >> 12) + - ((table2 [1] * voice.interp1) >> 12); - s = (BOOST::int16_t) (s * 2); - s += (table2 [0] * voice.interp0) >> 11 & ~1; - int output = clamp_16( s ); - if ( g.noise_enables & vbit ) - output = noise_amp; - - // scale output and set outx values - output = (output * envx) >> 11 & ~1; - - // output and apply muting (by setting voice.enabled to 31) - // if voice is externally disabled (not a SNES feature) - int l = (voice.volume [0] * output) >> voice.enabled; - int r = (voice.volume [1] * output) >> voice.enabled; - prev_outx = output; - raw_voice.outx = int8_t (output >> 8); - if ( g.echo_ons & vbit ) - { - echol += l; - echor += r; - } - left += l; - right += r; - } - // end of channel loop - - // main volume control - left = (left * left_volume ) >> (7 + emu_gain_bits); - right = (right * right_volume) >> (7 + emu_gain_bits); - - // Echo FIR filter - - // read feedback from echo buffer - int echo_ptr = this->echo_ptr; - uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; - echo_ptr += 4; - if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) - echo_ptr = 0; - int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend - int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend - this->echo_ptr = echo_ptr; - - // put samples in history ring buffer - const int fir_offset = this->fir_offset; - short (*fir_pos) [2] = &fir_buf [fir_offset]; - this->fir_offset = (fir_offset + 7) & 7; // move backwards one step - fir_pos [0] [0] = (short) fb_left; - fir_pos [0] [1] = (short) fb_right; - fir_pos [8] [0] = (short) fb_left; // duplicate at +8 eliminates wrap checking below - fir_pos [8] [1] = (short) fb_right; - - // FIR - fb_left = fb_left * fir_coeff [7] + - fir_pos [1] [0] * fir_coeff [6] + - fir_pos [2] [0] * fir_coeff [5] + - fir_pos [3] [0] * fir_coeff [4] + - fir_pos [4] [0] * fir_coeff [3] + - fir_pos [5] [0] * fir_coeff [2] + - fir_pos [6] [0] * fir_coeff [1] + - fir_pos [7] [0] * fir_coeff [0]; - - fb_right = fb_right * fir_coeff [7] + - fir_pos [1] [1] * fir_coeff [6] + - fir_pos [2] [1] * fir_coeff [5] + - fir_pos [3] [1] * fir_coeff [4] + - fir_pos [4] [1] * fir_coeff [3] + - fir_pos [5] [1] * fir_coeff [2] + - fir_pos [6] [1] * fir_coeff [1] + - fir_pos [7] [1] * fir_coeff [0]; - - left += (fb_left * g.left_echo_volume ) >> 14; - right += (fb_right * g.right_echo_volume) >> 14; - - // echo buffer feedback - if ( !(g.flags & 0x20) ) - { - echol += (fb_left * g.echo_feedback) >> 14; - echor += (fb_right * g.echo_feedback) >> 14; - SET_LE16( echo_buf , clamp_16( echol ) ); - SET_LE16( echo_buf + 2, clamp_16( echor ) ); - } - - if ( out_buf ) - { - // write final samples - - left = clamp_16( left ); - right = clamp_16( right ); - - int mute = g.flags & 0x40; - - out_buf [0] = (short) left; - out_buf [1] = (short) right; - out_buf += 2; - - // muting - if ( mute ) - { - out_buf [-2] = 0; - out_buf [-1] = 0; - } - } - } -} - -// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry): -// int normal_gauss [512]; -// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45 - -// Interleved gauss table (to improve cache coherency). -// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] -const BOOST::int16_t Spc_Dsp::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, -}; +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#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 ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::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. + +// Gaussian interpolation + +static short const gauss [512] = +{ + 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, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, +}; + +static short const cubic [514] = +{ + 0, -4, -8, -12, -16, -20, -23, -27, -30, -34, -37, -41, -44, -47, -50, -53, + -56, -59, -62, -65, -68, -71, -73, -76, -78, -81, -84, -87, -89, -91, -93, -95, + -98,-100,-102,-104,-106,-109,-110,-112,-113,-116,-117,-119,-121,-122,-123,-125, +-126,-128,-129,-131,-132,-134,-134,-136,-136,-138,-138,-140,-141,-141,-142,-143, +-144,-144,-145,-146,-147,-148,-147,-148,-148,-149,-149,-150,-150,-150,-150,-151, +-151,-151,-151,-151,-152,-152,-151,-152,-151,-152,-151,-151,-151,-151,-150,-150, +-150,-149,-149,-149,-149,-148,-147,-147,-146,-146,-145,-145,-144,-144,-143,-142, +-141,-141,-140,-139,-139,-138,-137,-136,-135,-135,-133,-133,-132,-131,-130,-129, +-128,-127,-126,-125,-124,-123,-121,-121,-119,-118,-117,-116,-115,-114,-112,-111, +-110,-109,-107,-106,-105,-104,-102,-102,-100, -99, -97, -97, -95, -94, -92, -91, + -90, -88, -87, -86, -85, -84, -82, -81, -79, -78, -76, -76, -74, -73, -71, -70, + -68, -67, -66, -65, -63, -62, -60, -60, -58, -57, -55, -55, -53, -52, -50, -49, + -48, -46, -45, -44, -43, -42, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, + -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -19, -17, -16, -15, -14, + -14, -13, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -6, -5, -4, -4, + -3, -3, -3, -2, -2, -2, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, + 0, +2048,2048,2048,2048,2047,2047,2046,2045,2043,2042,2041,2039,2037,2035,2033,2031, +2028,2026,2024,2021,2018,2015,2012,2009,2005,2002,1999,1995,1991,1987,1982,1978, +1974,1969,1965,1960,1955,1951,1946,1940,1934,1929,1924,1918,1912,1906,1900,1895, +1888,1882,1875,1869,1862,1856,1849,1842,1835,1828,1821,1814,1806,1799,1791,1783, +1776,1768,1760,1753,1744,1737,1728,1720,1711,1703,1695,1686,1677,1668,1659,1651, +1641,1633,1623,1614,1605,1596,1587,1577,1567,1559,1549,1539,1529,1520,1510,1499, +1490,1480,1470,1460,1450,1440,1430,1420,1408,1398,1389,1378,1367,1357,1346,1336, +1325,1315,1304,1293,1282,1272,1261,1250,1239,1229,1218,1207,1196,1185,1174,1163, +1152,1141,1130,1119,1108,1097,1086,1075,1063,1052,1042,1030,1019,1008, 997, 986, + 974, 964, 952, 941, 930, 919, 908, 897, 886, 875, 864, 853, 842, 831, 820, 809, + 798, 787, 776, 765, 754, 744, 733, 722, 711, 700, 690, 679, 668, 658, 647, 637, + 626, 616, 605, 595, 584, 574, 564, 554, 543, 534, 524, 514, 503, 494, 483, 473, + 464, 454, 444, 435, 425, 416, 407, 397, 387, 378, 370, 360, 351, 342, 333, 325, + 315, 307, 298, 290, 281, 273, 265, 256, 248, 241, 233, 225, 216, 209, 201, 193, + 186, 178, 171, 164, 157, 150, 143, 137, 129, 123, 117, 110, 103, 97, 91, 85, + 79, 74, 68, 62, 56, 51, 46, 41, 35, 31, 27, 22, 17, 13, 8, 4, + 0 +}; + +static short const sinc [2048] = +{ + 39, -315, 666, 15642, 666, -315, 39, -38, + 38, -302, 613, 15642, 718, -328, 41, -38, + 36, -288, 561, 15641, 772, -342, 42, -38, + 35, -275, 510, 15639, 826, -355, 44, -38, + 33, -263, 459, 15636, 880, -369, 46, -38, + 32, -250, 408, 15632, 935, -383, 47, -38, + 31, -237, 358, 15628, 990, -396, 49, -38, + 29, -224, 309, 15622, 1046, -410, 51, -38, + 28, -212, 259, 15616, 1103, -425, 53, -38, + 27, -200, 211, 15609, 1159, -439, 54, -38, + 25, -188, 163, 15601, 1216, -453, 56, -38, + 24, -175, 115, 15593, 1274, -467, 58, -38, + 23, -164, 68, 15583, 1332, -482, 60, -38, + 22, -152, 22, 15573, 1391, -496, 62, -37, + 21, -140, -24, 15562, 1450, -511, 64, -37, + 19, -128, -70, 15550, 1509, -526, 66, -37, + 18, -117, -115, 15538, 1569, -540, 68, -37, + 17, -106, -159, 15524, 1629, -555, 70, -37, + 16, -94, -203, 15510, 1690, -570, 72, -36, + 15, -83, -247, 15495, 1751, -585, 74, -36, + 14, -72, -289, 15479, 1813, -600, 76, -36, + 13, -62, -332, 15462, 1875, -616, 79, -36, + 12, -51, -374, 15445, 1937, -631, 81, -35, + 11, -40, -415, 15426, 2000, -646, 83, -35, + 11, -30, -456, 15407, 2063, -662, 85, -35, + 10, -20, -496, 15387, 2127, -677, 88, -34, + 9, -9, -536, 15366, 2191, -693, 90, -34, + 8, 1, -576, 15345, 2256, -708, 92, -34, + 7, 10, -614, 15323, 2321, -724, 95, -33, + 7, 20, -653, 15300, 2386, -740, 97, -33, + 6, 30, -690, 15276, 2451, -755, 99, -33, + 5, 39, -728, 15251, 2517, -771, 102, -32, + 5, 49, -764, 15226, 2584, -787, 104, -32, + 4, 58, -801, 15200, 2651, -803, 107, -32, + 3, 67, -836, 15173, 2718, -819, 109, -31, + 3, 76, -871, 15145, 2785, -835, 112, -31, + 2, 85, -906, 15117, 2853, -851, 115, -30, + 2, 93, -940, 15087, 2921, -867, 117, -30, + 1, 102, -974, 15057, 2990, -883, 120, -29, + 1, 110, -1007, 15027, 3059, -899, 122, -29, + 0, 118, -1039, 14995, 3128, -915, 125, -29, + 0, 127, -1071, 14963, 3198, -931, 128, -28, + -1, 135, -1103, 14930, 3268, -948, 131, -28, + -1, 142, -1134, 14896, 3338, -964, 133, -27, + -1, 150, -1164, 14862, 3409, -980, 136, -27, + -2, 158, -1194, 14827, 3480, -996, 139, -26, + -2, 165, -1224, 14791, 3551, -1013, 142, -26, + -3, 172, -1253, 14754, 3622, -1029, 144, -25, + -3, 179, -1281, 14717, 3694, -1045, 147, -25, + -3, 187, -1309, 14679, 3766, -1062, 150, -24, + -3, 193, -1337, 14640, 3839, -1078, 153, -24, + -4, 200, -1363, 14601, 3912, -1094, 156, -23, + -4, 207, -1390, 14561, 3985, -1110, 159, -23, + -4, 213, -1416, 14520, 4058, -1127, 162, -22, + -4, 220, -1441, 14479, 4131, -1143, 165, -22, + -4, 226, -1466, 14437, 4205, -1159, 168, -22, + -5, 232, -1490, 14394, 4279, -1175, 171, -21, + -5, 238, -1514, 14350, 4354, -1192, 174, -21, + -5, 244, -1537, 14306, 4428, -1208, 177, -20, + -5, 249, -1560, 14261, 4503, -1224, 180, -20, + -5, 255, -1583, 14216, 4578, -1240, 183, -19, + -5, 260, -1604, 14169, 4653, -1256, 186, -19, + -5, 265, -1626, 14123, 4729, -1272, 189, -18, + -5, 271, -1647, 14075, 4805, -1288, 192, -18, + -5, 276, -1667, 14027, 4881, -1304, 195, -17, + -6, 280, -1687, 13978, 4957, -1320, 198, -17, + -6, 285, -1706, 13929, 5033, -1336, 201, -16, + -6, 290, -1725, 13879, 5110, -1352, 204, -16, + -6, 294, -1744, 13829, 5186, -1368, 207, -15, + -6, 299, -1762, 13777, 5263, -1383, 210, -15, + -6, 303, -1779, 13726, 5340, -1399, 213, -14, + -6, 307, -1796, 13673, 5418, -1414, 216, -14, + -6, 311, -1813, 13620, 5495, -1430, 219, -13, + -5, 315, -1829, 13567, 5573, -1445, 222, -13, + -5, 319, -1844, 13512, 5651, -1461, 225, -13, + -5, 322, -1859, 13458, 5728, -1476, 229, -12, + -5, 326, -1874, 13402, 5806, -1491, 232, -12, + -5, 329, -1888, 13347, 5885, -1506, 235, -11, + -5, 332, -1902, 13290, 5963, -1521, 238, -11, + -5, 335, -1915, 13233, 6041, -1536, 241, -10, + -5, 338, -1928, 13176, 6120, -1551, 244, -10, + -5, 341, -1940, 13118, 6199, -1566, 247, -10, + -5, 344, -1952, 13059, 6277, -1580, 250, -9, + -5, 347, -1964, 13000, 6356, -1595, 253, -9, + -5, 349, -1975, 12940, 6435, -1609, 256, -8, + -4, 352, -1986, 12880, 6514, -1623, 259, -8, + -4, 354, -1996, 12819, 6594, -1637, 262, -8, + -4, 356, -2005, 12758, 6673, -1651, 265, -7, + -4, 358, -2015, 12696, 6752, -1665, 268, -7, + -4, 360, -2024, 12634, 6831, -1679, 271, -7, + -4, 362, -2032, 12572, 6911, -1693, 274, -6, + -4, 364, -2040, 12509, 6990, -1706, 277, -6, + -4, 366, -2048, 12445, 7070, -1719, 280, -6, + -3, 367, -2055, 12381, 7149, -1732, 283, -5, + -3, 369, -2062, 12316, 7229, -1745, 286, -5, + -3, 370, -2068, 12251, 7308, -1758, 289, -5, + -3, 371, -2074, 12186, 7388, -1771, 291, -4, + -3, 372, -2079, 12120, 7467, -1784, 294, -4, + -3, 373, -2084, 12054, 7547, -1796, 297, -4, + -3, 374, -2089, 11987, 7626, -1808, 300, -4, + -2, 375, -2094, 11920, 7706, -1820, 303, -3, + -2, 376, -2098, 11852, 7785, -1832, 305, -3, + -2, 376, -2101, 11785, 7865, -1844, 308, -3, + -2, 377, -2104, 11716, 7944, -1855, 311, -3, + -2, 377, -2107, 11647, 8024, -1866, 313, -2, + -2, 378, -2110, 11578, 8103, -1877, 316, -2, + -2, 378, -2112, 11509, 8182, -1888, 318, -2, + -1, 378, -2113, 11439, 8262, -1899, 321, -2, + -1, 378, -2115, 11369, 8341, -1909, 323, -2, + -1, 378, -2116, 11298, 8420, -1920, 326, -2, + -1, 378, -2116, 11227, 8499, -1930, 328, -1, + -1, 378, -2116, 11156, 8578, -1940, 331, -1, + -1, 378, -2116, 11084, 8656, -1949, 333, -1, + -1, 377, -2116, 11012, 8735, -1959, 335, -1, + -1, 377, -2115, 10940, 8814, -1968, 337, -1, + -1, 377, -2114, 10867, 8892, -1977, 340, -1, + -1, 376, -2112, 10795, 8971, -1985, 342, -1, + 0, 375, -2111, 10721, 9049, -1994, 344, -1, + 0, 375, -2108, 10648, 9127, -2002, 346, 0, + 0, 374, -2106, 10574, 9205, -2010, 348, 0, + 0, 373, -2103, 10500, 9283, -2018, 350, 0, + 0, 372, -2100, 10426, 9360, -2025, 352, 0, + 0, 371, -2097, 10351, 9438, -2032, 354, 0, + 0, 370, -2093, 10276, 9515, -2039, 355, 0, + 0, 369, -2089, 10201, 9592, -2046, 357, 0, + 0, 367, -2084, 10126, 9669, -2052, 359, 0, + 0, 366, -2080, 10050, 9745, -2058, 360, 0, + 0, 365, -2075, 9974, 9822, -2064, 362, 0, + 0, 363, -2070, 9898, 9898, -2070, 363, 0, + 0, 362, -2064, 9822, 9974, -2075, 365, 0, + 0, 360, -2058, 9745, 10050, -2080, 366, 0, + 0, 359, -2052, 9669, 10126, -2084, 367, 0, + 0, 357, -2046, 9592, 10201, -2089, 369, 0, + 0, 355, -2039, 9515, 10276, -2093, 370, 0, + 0, 354, -2032, 9438, 10351, -2097, 371, 0, + 0, 352, -2025, 9360, 10426, -2100, 372, 0, + 0, 350, -2018, 9283, 10500, -2103, 373, 0, + 0, 348, -2010, 9205, 10574, -2106, 374, 0, + 0, 346, -2002, 9127, 10648, -2108, 375, 0, + -1, 344, -1994, 9049, 10721, -2111, 375, 0, + -1, 342, -1985, 8971, 10795, -2112, 376, -1, + -1, 340, -1977, 8892, 10867, -2114, 377, -1, + -1, 337, -1968, 8814, 10940, -2115, 377, -1, + -1, 335, -1959, 8735, 11012, -2116, 377, -1, + -1, 333, -1949, 8656, 11084, -2116, 378, -1, + -1, 331, -1940, 8578, 11156, -2116, 378, -1, + -1, 328, -1930, 8499, 11227, -2116, 378, -1, + -2, 326, -1920, 8420, 11298, -2116, 378, -1, + -2, 323, -1909, 8341, 11369, -2115, 378, -1, + -2, 321, -1899, 8262, 11439, -2113, 378, -1, + -2, 318, -1888, 8182, 11509, -2112, 378, -2, + -2, 316, -1877, 8103, 11578, -2110, 378, -2, + -2, 313, -1866, 8024, 11647, -2107, 377, -2, + -3, 311, -1855, 7944, 11716, -2104, 377, -2, + -3, 308, -1844, 7865, 11785, -2101, 376, -2, + -3, 305, -1832, 7785, 11852, -2098, 376, -2, + -3, 303, -1820, 7706, 11920, -2094, 375, -2, + -4, 300, -1808, 7626, 11987, -2089, 374, -3, + -4, 297, -1796, 7547, 12054, -2084, 373, -3, + -4, 294, -1784, 7467, 12120, -2079, 372, -3, + -4, 291, -1771, 7388, 12186, -2074, 371, -3, + -5, 289, -1758, 7308, 12251, -2068, 370, -3, + -5, 286, -1745, 7229, 12316, -2062, 369, -3, + -5, 283, -1732, 7149, 12381, -2055, 367, -3, + -6, 280, -1719, 7070, 12445, -2048, 366, -4, + -6, 277, -1706, 6990, 12509, -2040, 364, -4, + -6, 274, -1693, 6911, 12572, -2032, 362, -4, + -7, 271, -1679, 6831, 12634, -2024, 360, -4, + -7, 268, -1665, 6752, 12696, -2015, 358, -4, + -7, 265, -1651, 6673, 12758, -2005, 356, -4, + -8, 262, -1637, 6594, 12819, -1996, 354, -4, + -8, 259, -1623, 6514, 12880, -1986, 352, -4, + -8, 256, -1609, 6435, 12940, -1975, 349, -5, + -9, 253, -1595, 6356, 13000, -1964, 347, -5, + -9, 250, -1580, 6277, 13059, -1952, 344, -5, + -10, 247, -1566, 6199, 13118, -1940, 341, -5, + -10, 244, -1551, 6120, 13176, -1928, 338, -5, + -10, 241, -1536, 6041, 13233, -1915, 335, -5, + -11, 238, -1521, 5963, 13290, -1902, 332, -5, + -11, 235, -1506, 5885, 13347, -1888, 329, -5, + -12, 232, -1491, 5806, 13402, -1874, 326, -5, + -12, 229, -1476, 5728, 13458, -1859, 322, -5, + -13, 225, -1461, 5651, 13512, -1844, 319, -5, + -13, 222, -1445, 5573, 13567, -1829, 315, -5, + -13, 219, -1430, 5495, 13620, -1813, 311, -6, + -14, 216, -1414, 5418, 13673, -1796, 307, -6, + -14, 213, -1399, 5340, 13726, -1779, 303, -6, + -15, 210, -1383, 5263, 13777, -1762, 299, -6, + -15, 207, -1368, 5186, 13829, -1744, 294, -6, + -16, 204, -1352, 5110, 13879, -1725, 290, -6, + -16, 201, -1336, 5033, 13929, -1706, 285, -6, + -17, 198, -1320, 4957, 13978, -1687, 280, -6, + -17, 195, -1304, 4881, 14027, -1667, 276, -5, + -18, 192, -1288, 4805, 14075, -1647, 271, -5, + -18, 189, -1272, 4729, 14123, -1626, 265, -5, + -19, 186, -1256, 4653, 14169, -1604, 260, -5, + -19, 183, -1240, 4578, 14216, -1583, 255, -5, + -20, 180, -1224, 4503, 14261, -1560, 249, -5, + -20, 177, -1208, 4428, 14306, -1537, 244, -5, + -21, 174, -1192, 4354, 14350, -1514, 238, -5, + -21, 171, -1175, 4279, 14394, -1490, 232, -5, + -22, 168, -1159, 4205, 14437, -1466, 226, -4, + -22, 165, -1143, 4131, 14479, -1441, 220, -4, + -22, 162, -1127, 4058, 14520, -1416, 213, -4, + -23, 159, -1110, 3985, 14561, -1390, 207, -4, + -23, 156, -1094, 3912, 14601, -1363, 200, -4, + -24, 153, -1078, 3839, 14640, -1337, 193, -3, + -24, 150, -1062, 3766, 14679, -1309, 187, -3, + -25, 147, -1045, 3694, 14717, -1281, 179, -3, + -25, 144, -1029, 3622, 14754, -1253, 172, -3, + -26, 142, -1013, 3551, 14791, -1224, 165, -2, + -26, 139, -996, 3480, 14827, -1194, 158, -2, + -27, 136, -980, 3409, 14862, -1164, 150, -1, + -27, 133, -964, 3338, 14896, -1134, 142, -1, + -28, 131, -948, 3268, 14930, -1103, 135, -1, + -28, 128, -931, 3198, 14963, -1071, 127, 0, + -29, 125, -915, 3128, 14995, -1039, 118, 0, + -29, 122, -899, 3059, 15027, -1007, 110, 1, + -29, 120, -883, 2990, 15057, -974, 102, 1, + -30, 117, -867, 2921, 15087, -940, 93, 2, + -30, 115, -851, 2853, 15117, -906, 85, 2, + -31, 112, -835, 2785, 15145, -871, 76, 3, + -31, 109, -819, 2718, 15173, -836, 67, 3, + -32, 107, -803, 2651, 15200, -801, 58, 4, + -32, 104, -787, 2584, 15226, -764, 49, 5, + -32, 102, -771, 2517, 15251, -728, 39, 5, + -33, 99, -755, 2451, 15276, -690, 30, 6, + -33, 97, -740, 2386, 15300, -653, 20, 7, + -33, 95, -724, 2321, 15323, -614, 10, 7, + -34, 92, -708, 2256, 15345, -576, 1, 8, + -34, 90, -693, 2191, 15366, -536, -9, 9, + -34, 88, -677, 2127, 15387, -496, -20, 10, + -35, 85, -662, 2063, 15407, -456, -30, 11, + -35, 83, -646, 2000, 15426, -415, -40, 11, + -35, 81, -631, 1937, 15445, -374, -51, 12, + -36, 79, -616, 1875, 15462, -332, -62, 13, + -36, 76, -600, 1813, 15479, -289, -72, 14, + -36, 74, -585, 1751, 15495, -247, -83, 15, + -36, 72, -570, 1690, 15510, -203, -94, 16, + -37, 70, -555, 1629, 15524, -159, -106, 17, + -37, 68, -540, 1569, 15538, -115, -117, 18, + -37, 66, -526, 1509, 15550, -70, -128, 19, + -37, 64, -511, 1450, 15562, -24, -140, 21, + -37, 62, -496, 1391, 15573, 22, -152, 22, + -38, 60, -482, 1332, 15583, 68, -164, 23, + -38, 58, -467, 1274, 15593, 115, -175, 24, + -38, 56, -453, 1216, 15601, 163, -188, 25, + -38, 54, -439, 1159, 15609, 211, -200, 27, + -38, 53, -425, 1103, 15616, 259, -212, 28, + -38, 51, -410, 1046, 15622, 309, -224, 29, + -38, 49, -396, 990, 15628, 358, -237, 31, + -38, 47, -383, 935, 15632, 408, -250, 32, + -38, 46, -369, 880, 15636, 459, -263, 33, + -38, 44, -355, 826, 15639, 510, -275, 35, + -38, 42, -342, 772, 15641, 561, -288, 36, + -38, 41, -328, 718, 15642, 613, -302, 38, +}; + +inline int Spc_Dsp::interpolate( voice_t const* v ) +{ + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_cubic( voice_t const* v ) +{ + // Make pointers into cubic based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = cubic + offset; + short const* rev = cubic + 256 - offset; // mirror left half of cubic + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = fwd [ 0] * in [0]; + out += fwd [257] * in [1]; + out += rev [257] * in [2]; + out += rev [ 0] * in [3]; + out >>= 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_sinc( voice_t const* v ) +{ + // Make pointers into cubic based on fractional position between samples + int offset = v->interp_pos & 0xFF0; + short const* filt = (short const*) (((char const*)sinc) + offset); + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = filt [0] * in [0]; + out += filt [1] * in [1]; + out += filt [2] * in [2]; + out += filt [3] * in [3]; + out += filt [4] * in [4]; + out += filt [5] * in [5]; + out += filt [6] * in [6]; + out += filt [7] * in [7]; + out >>= 14; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_linear( voice_t const* v ) +{ + int fract = v->interp_pos & 0xFFF; + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (0x1000 - fract) * in [0]; + out += fract * in [1]; + out >>= 12; + + // no need to clamp + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_nearest( voice_t const* v ) +{ + return v->buf [(v->interp_pos >> 12) + v->buf_pos] & ~1; +} + +//// Counters + +int const simple_counter_range = 2048 * 5 * 3; // 30720 + +static unsigned const counter_rates [32] = +{ + simple_counter_range + 1, // never fires + 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1 +}; + +static unsigned const counter_offsets [32] = +{ + 1, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0 +}; + +inline void Spc_Dsp::init_counter() +{ + m.counter = 0; +} + +inline void Spc_Dsp::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned Spc_Dsp::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void Spc_Dsp::run_envelope( voice_t* const v ) +{ + int env = v->env; + if ( v->env_mode == env_release ) // 60% + { + if ( (env -= 0x8) < 0 ) + env = 0; + v->env = env; + } + else + { + int rate; + int env_data = VREG(v->regs,adsr1); + if ( m.t_adsr0 & 0x80 ) // 99% ADSR + { + if ( v->env_mode >= env_decay ) // 99% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + if ( v->env_mode == env_decay ) // 1% + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (m.t_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 + } +} + + +//// BRR Decoding + +inline void Spc_Dsp::decode_brr( voice_t* v ) +{ + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + int const header = m.t_brr_header; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // Apply IIR filter (8 is the most commonly used) + int const filter = 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 + } +} + + +//// Misc + +#define MISC_CLOCK( n ) inline void Spc_Dsp::misc_##n() + +MISC_CLOCK( 27 ) +{ + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON +} +MISC_CLOCK( 28 ) +{ + m.t_non = REG(non); + m.t_eon = REG(eon); + m.t_dir = REG(dir); +} +MISC_CLOCK( 29 ) +{ + if ( (m.every_other_sample ^= 1) != 0 ) + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read +} +MISC_CLOCK( 30 ) +{ + if ( m.every_other_sample ) + { + m.kon = m.new_kon; + m.t_koff = REG(koff) | m.mute_mask; + } + + run_counters(); + + // Noise + if ( !read_counter( REG(flg) & 0x1F ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } +} + + +//// Voices + +#define VOICE_CLOCK( n ) void Spc_Dsp::voice_##n( voice_t* const v ) + +inline VOICE_CLOCK( V1 ) +{ + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; + m.t_srcn = VREG(v->regs,srcn); +} +inline VOICE_CLOCK( V2 ) +{ + // Read sample pointer (ignored if not needed) + uint8_t const* entry = &m.ram [m.t_dir_addr]; + if ( !v->kon_delay ) + entry += 2; + m.t_brr_next_addr = GET_LE16A( entry ); + + m.t_adsr0 = VREG(v->regs,adsr0); + + // Read pitch, spread over two clocks + m.t_pitch = VREG(v->regs,pitchl); +} +inline VOICE_CLOCK( V3a ) +{ + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; +} +inline VOICE_CLOCK( V3b ) +{ + // Read BRR header and byte + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking +} +VOICE_CLOCK( V3c ) +{ + // Pitch modulation using previous voice's output + if ( m.t_pmon & v->vbit ) + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; + + if ( v->kon_delay ) + { + // Get ready to start BRR decoding on next sample + if ( v->kon_delay == 5 ) + { + v->brr_addr = m.t_brr_next_addr; + v->brr_offset = 1; + v->buf_pos = 0; + m.t_brr_header = 0; // header is ignored on this sample + m.kon_check = true; + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = 0; + if ( --v->kon_delay & 3 ) + v->interp_pos = 0x4000; + + // Pitch is never added during KON + m.t_pitch = 0; + } + + // Gaussian interpolation + { + int output; + + switch ( m.interpolation_level ) + { + case 0: + default: + output = interpolate( v ); + break; + + case 1: + output = interpolate_cubic( v ); + break; + + case 2: + output = interpolate_sinc( v ); + break; + + case -1: + output = interpolate_linear( v ); + break; + + case -2: + output = interpolate_nearest( v ); + break; + } + + // Noise + if ( m.t_non & v->vbit ) + output = (int16_t) (m.noise * 2); + + // Apply envelope + m.t_output = (output * v->env) >> 11 & ~1; + v->t_envx_out = (uint8_t) (v->env >> 4); + } + + // Immediate silence due to end of sample or soft reset + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) + { + v->env_mode = env_release; + v->env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & v->vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & v->vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + } + } + + // Run envelope for next sample + if ( !v->kon_delay ) + run_envelope( v ); +} +inline void Spc_Dsp::voice_output( voice_t const* v, int ch ) +{ + // Check surround removal + int vol = (int8_t) VREG(v->regs,voll + ch); + int voln = (int8_t) VREG(v->regs,voll + ch ^ 1); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + // Apply left/right volume + int amp = (m.t_output * vol) >> 7; + + int abs_amp = abs( amp ); + if ( abs_amp > m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] ) + m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] = abs_amp; + + // Add to output total + m.t_main_out [ch] += amp; + CLAMP16( m.t_main_out [ch] ); + + // Optionally add to echo total + if ( m.t_eon & v->vbit ) + { + m.t_echo_out [ch] += amp; + CLAMP16( m.t_echo_out [ch] ); + } +} +VOICE_CLOCK( V4 ) +{ + // Decode BRR + m.t_looped = 0; + if ( v->interp_pos >= 0x4000 ) + { + decode_brr( v ); + + if ( (v->brr_offset += 2) >= brr_block_size ) + { + // Start decoding next BRR block + assert( v->brr_offset == brr_block_size ); + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + if ( m.t_brr_header & 1 ) + { + v->brr_addr = m.t_brr_next_addr; + m.t_looped = v->vbit; + } + v->brr_offset = 1; + } + } + + // Apply pitch + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; + + // Keep from getting too far ahead (when using pitch modulation) + if ( v->interp_pos > 0x7FFF ) + v->interp_pos = 0x7FFF; + + // Output left + voice_output( v, 0 ); +} +inline VOICE_CLOCK( V5 ) +{ + // Output right + voice_output( v, 1 ); + + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier + int endx_buf = REG(endx) | m.t_looped; + + // Clear bit in ENDX if KON just began + if ( v->kon_delay == 5 ) + endx_buf &= ~v->vbit; + m.endx_buf = (uint8_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (m.t_output >> 8); +} +inline VOICE_CLOCK( V7 ) +{ + // Update ENDX + REG(endx) = m.endx_buf; + + m.envx_buf = v->t_envx_out; +} +inline VOICE_CLOCK( V8 ) +{ + // Update OUTX + VREG(v->regs,outx) = m.outx_buf; +} +inline VOICE_CLOCK( V9 ) +{ + // Update ENVX + VREG(v->regs,envx) = m.envx_buf; +} + +// Most voices do all these in one clock, so make a handy composite +inline VOICE_CLOCK( V3 ) +{ + voice_V3a( v ); + voice_V3b( v ); + voice_V3c( v ); +} + +// Common combinations of voice steps on different voices. This greatly reduces +// code size and allows everything to be inlined in these functions. +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } + + +//// Echo + +// Current echo buffer pointer for left/right channel +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) + +// Sample in echo history buffer, where 0 is the oldest +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) + +// Calculate FIR point for left/right channel +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) + +#define ECHO_CLOCK( n ) inline void Spc_Dsp::echo_##n() + +inline void Spc_Dsp::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // second copy simplifies wrap-around handling + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; +} + +ECHO_CLOCK( 22 ) +{ + // History + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + m.echo_hist_pos = m.echo_hist; + + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; + echo_read( 0 ); + + // FIR (using l and r temporaries below helps compiler optimize) + int l = CALC_FIR( 0, 0 ); + int r = CALC_FIR( 0, 1 ); + + m.t_echo_in [0] = l; + m.t_echo_in [1] = r; +} +ECHO_CLOCK( 23 ) +{ + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; + + echo_read( 1 ); +} +ECHO_CLOCK( 24 ) +{ + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; +} +ECHO_CLOCK( 25 ) +{ + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); + + l = (int16_t) l; + r = (int16_t) r; + + l += (int16_t) CALC_FIR( 7, 0 ); + r += (int16_t) CALC_FIR( 7, 1 ); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_in [0] = l & ~1; + m.t_echo_in [1] = r & ~1; +} +inline int Spc_Dsp::echo_output( int ch ) +{ + // Check surround removal + int vol = (int8_t) REG(mvoll + ch * 0x10); + int voln = (int8_t) REG(mvoll + ch * 0x10 ^ 0x10); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) + + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); + CLAMP16( out ); + return out; +} +ECHO_CLOCK( 26 ) +{ + // Left output volumes + // (save sample for next clock so we can output both together) + m.t_main_out [0] = echo_output( 0 ); + + // Echo feedback + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_out [0] = l & ~1; + m.t_echo_out [1] = r & ~1; +} +ECHO_CLOCK( 27 ) +{ + // Output + int l = m.t_main_out [0]; + int r = echo_output( 1 ); + m.t_main_out [0] = 0; + m.t_main_out [1] = 0; + + // TODO: global muting isn't this simple (turns DAC on and off + // or something, causing small ~37-sample pulse when first muted) + if ( REG(flg) & 0x40 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void Spc_Dsp::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + m.t_echo_out [ch] = 0; +} +ECHO_CLOCK( 29 ) +{ + m.t_esa = REG(esa); + + if ( !m.echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + + m.echo_offset += 4; + if ( m.echo_offset >= m.echo_length ) + m.echo_offset = 0; + + // Write left echo + echo_write( 0 ); + + m.t_echo_enabled = REG(flg); +} +ECHO_CLOCK( 30 ) +{ + // Write right echo + echo_write( 1 ); +} + + +//// Timing + +// Execute clock for a particular voice +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); + +/* The most common sequence of clocks uses composite operations +for efficiency. For example, the following are equivalent to the +individual steps on the right: + +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void Spc_Dsp::run( int clocks_remain ) +{ + require( clocks_remain > 0 ); + + int const phase = m.phase; + m.phase = (phase + clocks_remain) & 31; + switch ( phase ) + { + loop: + + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void Spc_Dsp::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + interpolation_level( 0 ); + 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 + for ( int i = voice_count; --i >= 0; ) + { + voice_t* v = &m.voices [i]; + v->brr_offset = 1; + v->vbit = 1 << i; + v->regs = &m.regs [i * 0x10]; + } + m.new_kon = REG(kon); + m.t_dir = REG(dir); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void Spc_Dsp::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} +#endif diff --git a/Frameworks/GME/gme/Spc_Dsp.h b/Frameworks/GME/gme/Spc_Dsp.h old mode 100755 new mode 100644 index 36492275d..d53c5ebe7 --- a/Frameworks/GME/gme/Spc_Dsp.h +++ b/Frameworks/GME/gme/Spc_Dsp.h @@ -1,152 +1,341 @@ -// Super Nintendo (SNES) SPC DSP emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include "blargg_common.h" - -class Spc_Dsp { - typedef BOOST::int8_t int8_t; - typedef BOOST::uint8_t uint8_t; -public: - - // Keeps pointer to 64K ram - Spc_Dsp( uint8_t* ram ); - - // Mute voice n if bit n (1 << n) of mask is clear. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // Clear state and silence everything. - void reset(); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to - // the 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable ); - - // Read/write register 'n', where n ranges from 0 to register_count - 1. - enum { register_count = 128 }; - int read ( int n ); - void write( int n, int ); - - // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. - void run( long count, short* buf = NULL ); - - -// End of public interface -private: - - struct raw_voice_t { - int8_t left_vol; - int8_t right_vol; - uint8_t rate [2]; - uint8_t waveform; - uint8_t adsr [2]; // envelope rates for attack, decay, and sustain - uint8_t gain; // envelope gain (if not using ADSR) - int8_t envx; // current envelope level - int8_t outx; // current sample - int8_t unused [6]; - }; - - struct globals_t { - int8_t unused1 [12]; - int8_t left_volume; // 0C Main Volume Left (-.7) - int8_t echo_feedback; // 0D Echo Feedback (-.7) - int8_t unused2 [14]; - int8_t right_volume; // 1C Main Volume Right (-.7) - int8_t unused3 [15]; - int8_t left_echo_volume; // 2C Echo Volume Left (-.7) - uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice - int8_t unused4 [14]; - int8_t right_echo_volume; // 3C Echo Volume Right (-.7) - uint8_t noise_enables; // 3D Noise output on/off for each voice - int8_t unused5 [14]; - uint8_t key_ons; // 4C Key On for each voice - uint8_t echo_ons; // 4D Echo on/off for each voice - int8_t unused6 [14]; - uint8_t key_offs; // 5C key off for each voice (instantiates release mode) - uint8_t wave_page; // 5D source directory (wave table offsets) - int8_t unused7 [14]; - uint8_t flags; // 6C flags and noise freq - uint8_t echo_page; // 6D - int8_t unused8 [14]; - uint8_t wave_ended; // 7C - uint8_t echo_delay; // 7D ms >> 4 - char unused9 [2]; - }; - - union { - raw_voice_t voice [voice_count]; - uint8_t reg [register_count]; - globals_t g; - }; - - uint8_t* const ram; - - // Cache of echo FIR values for faster access - short fir_coeff [voice_count]; - - // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code - short fir_buf [16] [2]; - int fir_offset; // (0 to 7) - - enum { emu_gain_bits = 8 }; - int emu_gain; - - int keyed_on; // 8-bits for 8 voices - int keys; - - int echo_ptr; - int noise_amp; - int noise; - int noise_count; - - int surround_threshold; - - static BOOST::int16_t const gauss []; - - enum state_t { - state_attack, - state_decay, - state_sustain, - state_release - }; - - struct voice_t { - short volume [2]; - short fraction;// 12-bit fractional position - short interp3; // most recent four decoded samples - short interp2; - short interp1; - short interp0; - short block_remain; // number of nybbles remaining in current block - unsigned short addr; - short block_header; // header byte from current block - short envcnt; - short envx; - short on_cnt; - short enabled; // 7 if enabled, 31 if disabled - short envstate; - short unused; // pad to power of 2 - }; - - voice_t voice_state [voice_count]; - - int clock_envelope( int ); -}; - -inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } - -inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } - -inline int Spc_Dsp::read( int i ) -{ - assert( (unsigned) i < register_count ); - return reg [i]; -} - -#endif +// Highly accurate SNES SPC-700 DSP emulator + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } + +class Sfm_Emu; + +class Spc_Dsp { + friend class Sfm_Emu; + +public: + typedef BOOST::uint8_t uint8_t; + +// 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 run() + // 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 (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + + // Saves/loads exact emulator state + enum { state_size = 640 }; // maximum space needed when saving + typedef dsp_copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Returns non-zero if new key-on events occurred since last call + bool check_kon(); + +// 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; } + void disable_surround( bool disable = true ); + void interpolation_level( int level = 0 ) { m.interpolation_level = level; } +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + 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 + uint8_t* regs; // pointer to voice's DSP registers + int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. + 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 + uint8_t t_envx_out; + }; + + // kill me now + const voice_t * get_voice( int ch ) const; + int get_max_level( int v, int ch ) const; +private: + enum { brr_block_size = 9 }; + + 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 counter; + 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) + bool kon_check; // set when a new KON occurs + + // Hidden registers also written to when main register is written to + int new_kon; + uint8_t endx_buf; + uint8_t envx_buf; + uint8_t outx_buf; + + // Temporary state between clocks + + // read once per sample + int t_pmon; + int t_non; + int t_eon; + int t_dir; + int t_koff; + + // read a few clocks ahead then used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_enabled; + + // internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + // left/right sums + int t_main_out [2]; + int t_echo_out [2]; + int t_echo_in [2]; + + voice_t voices [voice_count]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + int interpolation_level; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + + int max_level[voice_count][2]; + }; + state_t m; + + void init_counter(); + void run_counters(); + unsigned read_counter( int rate ); + + int interpolate( voice_t const* v ); + int interpolate_cubic( voice_t const* v ); + int interpolate_sinc( voice_t const* v ); + int interpolate_linear( voice_t const* v ); + int interpolate_nearest( voice_t const* v ); + void run_envelope( voice_t* const v ); + void decode_brr( voice_t* v ); + + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + void voice_output( voice_t const* v, int ch ); + void voice_V1( voice_t* const ); + void voice_V2( voice_t* const ); + void voice_V3( voice_t* const ); + void voice_V3a( voice_t* const ); + void voice_V3b( voice_t* const ); + void voice_V3c( voice_t* const ); + void voice_V4( voice_t* const ); + void voice_V5( voice_t* const ); + void voice_V6( voice_t* const ); + void voice_V7( voice_t* const ); + void voice_V8( voice_t* const ); + void voice_V9( voice_t* const ); + void voice_V7_V4_V1( voice_t* const ); + void voice_V8_V5_V2( voice_t* const ); + void voice_V9_V6_V3( voice_t* const ); + + void echo_read( int ch ); + int echo_output( int ch ); + void echo_write( int ch ); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + void soft_reset_common(); +}; + +#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::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + switch ( addr & 0x0F ) + { + case v_envx: + m.envx_buf = (uint8_t) data; + break; + + case v_outx: + m.outx_buf = (uint8_t) data; + break; + + case 0x0C: + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + { + m.endx_buf = 0; + m.regs [r_endx] = 0; + } + break; + } +} + +inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; } + +inline void Spc_Dsp::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +inline const Spc_Dsp::voice_t * Spc_Dsp::get_voice( int ch ) const +{ + assert( (unsigned) ch < voice_count ); + return &m.voices[ ch ]; +} + +inline int Spc_Dsp::get_max_level( int v, int ch ) const +{ + assert( (unsigned) v < voice_count ); + assert( (unsigned) ch < 2 ); + int ret = m.max_level[ v ][ ch ]; + (( Spc_Dsp * )this)->m.max_level[ v ][ ch ] = 0; + return ret; +} + +inline bool Spc_Dsp::check_kon() +{ + bool old = m.kon_check; + m.kon_check = 0; + return old; +} + +#if !SPC_NO_COPY_STATE_FUNCS + +class SPC_State_Copier { + Spc_Dsp::copy_func_t func; + unsigned char** buf; +public: + SPC_State_Copier( unsigned char** p, Spc_Dsp::copy_func_t f ) { func = f; buf = p; } + void copy( void* state, size_t size ); + int copy_int( int state, int size ); + void skip( int count ); + void extra(); +}; + +#define SPC_COPY( type, state )\ +{\ + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ + assert( (BOOST::type) state == state );\ +} + +#endif + +#endif diff --git a/Frameworks/GME/gme/Spc_Emu.cpp b/Frameworks/GME/gme/Spc_Emu.cpp old mode 100755 new mode 100644 index 22be9e2ad..330e9d758 --- a/Frameworks/GME/gme/Spc_Emu.cpp +++ b/Frameworks/GME/gme/Spc_Emu.cpp @@ -1,326 +1,423 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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" - -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 ) - { - dprintf( "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 ) - dprintf( "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; - dprintf( "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_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; } - -gme_type_t_ const gme_spc_type [1] = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; - -// Setup - -blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) -{ - apu.set_gain( gain() ); - 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::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_file_size ) - return gme_wrong_file_type; - return check_spc_header( in ); -} - -// Emulation - -void Spc_Emu::set_tempo_( double t ) { apu.set_tempo( t ); } - -blargg_err_t Spc_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - resampler.clear(); - RETURN_ERR( apu.load_spc( file_data, file_size ) ); - apu.clear_echo(); - 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 ) ); - - // 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 apu.play( 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( apu.play( n, resampler.buffer() ) ); - resampler.write( n ); - } - } - check( remain == 0 ); - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Spc_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2004-2009 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 ); + set_gain( 1.4 ); +} + +Spc_Emu::~Spc_Emu() { } + +// Track info + +int const trailer_offset = 0x10200; + +inline byte const* Spc_Emu::trailer_() const { return &file_begin() [min( file_size(), trailer_offset )]; } + +inline int Spc_Emu::trailer_size_() const { return max( 0, file_size() - trailer_offset ); } + +static void get_spc_xid6( byte const begin [], int size, track_info_t* out ) +{ + // header + byte const* end = begin + size; + if ( size < 8 || memcmp( begin, "xid6", 4 ) ) + { + check( false ); + return; + } + int info_size = get_le32( begin + 4 ); + byte const* in = begin + 8; + if ( end - in > info_size ) + { + dprintf( "SPC: Extra data after xid6\n" ); + end = in + info_size; + } + + int year = 0; + char copyright [256 + 5]; + int copyright_len = 0; + int const year_len = 5; + int disc = 0, track = 0; + + 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 ) + { + dprintf( "SPC: xid6 goes past end" ); + break; // block goes past end of data + } + + // handle specific block types + char* field = NULL; + 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 0x10: field = out->ost; break; + case 0x11: disc = data; break; + case 0x12: track = data; 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 ) + { + int loop = out->length - out->intro_length; + if ( loop >= 2000 ) + out->loop_length = loop; + } + } + break; + */ + + case 0x33: + check( len == 4 ); + if ( len >= 4 ) + { + out->fade_length = get_le32( in ) / 64; + } + 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 ) + dprintf( "SPC: Unknown 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; + //dprintf( "SPC: xid6 info tag wasn't properly padded to align\n" ); + break; + } + } + } + + char* p = ©right [year_len]; + if ( year ) + { + *--p = ' '; + // avoid using bloated printf + 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 ); + + if ( disc > 0 && disc <= 9 ) + { + out->disc [0] = disc + '0'; + out->disc [1] = 0; + } + + if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) ) + { + char* p = ©right [3]; + *p = 0; + if ( track & 255 ) *--p = char (track & 255); + track >>= 8; + for ( int n = 2; n-- && track; ) + { + *--p = char (track % 10 + '0'); + track /= 10; + } + memcpy( out->track, p, ©right [4] - p ); + } + + check( in == end ); +} + +static void get_spc_info( Spc_Emu::header_t const& h, byte const xid6 [], int xid6_size, + track_info_t* out ) +{ + // decode length (can be in text or binary format, sometimes ambiguous ugh) + int len_secs = 0; + int i; + for ( 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; + + long fade_msec = 0; + for ( i = 0; i < 4; i++ ) + { + unsigned n = h.fade_msec [i] - '0'; + if ( n > 9 ) + { + if ( i == 1 && (h.author [0] || !h.author [1]) ) + fade_msec = -1; + break; + } + fade_msec *= 10; + fade_msec += n; + } + if ( i == 4 && unsigned( h.author [0] - '0' ) <= 9 ) + fade_msec = fade_msec * 10 + h.author [0] - '0'; + if ( fade_msec < 0 || fade_msec > 0x7FFF ) + fade_msec = get_le32( h.fade_msec ); + if ( fade_msec < 0x7FFF ) + out->fade_length = fade_msec; + + 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 ); +} + +static void hash_spc_file( Spc_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.format, sizeof(h.format) ); + out.hash_( &h.version, sizeof(h.version) ); + out.hash_( &h.pc[0], sizeof(h.pc) ); + out.hash_( &h.a, sizeof(h.a) ); + out.hash_( &h.x, sizeof(h.x) ); + out.hash_( &h.y, sizeof(h.y) ); + out.hash_( &h.psw, sizeof(h.psw) ); + out.hash_( &h.sp, sizeof(h.sp) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + out.hash_( &h.emulator, sizeof(h.emulator) ); + out.hash_( &h.unused2[0], sizeof(h.unused2) ); + out.hash_( data, data_size ); +} + +blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const +{ + get_spc_info( header(), trailer_(), trailer_size_(), out ); + return blargg_ok; +} + +static blargg_err_t check_spc_header( void const* header ) +{ + if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) + return blargg_err_file_type; + return blargg_ok; +} + +struct Spc_File : Gme_Info_ +{ + Spc_Emu::header_t header; + blargg_vector data; + blargg_vector xid6; + + Spc_File() { set_type( gme_spc_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + int file_size = in.remain(); + if ( file_size < Snes_Spc::spc_min_file_size ) + return blargg_err_file_type; + RETURN_ERR( in.read( &header, header.size ) ); + RETURN_ERR( check_spc_header( header.tag ) ); + int const xid6_offset = 0x10200; + RETURN_ERR( data.resize( blargg_min( xid6_offset - header.size, file_size - header.size ) ) ); + RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); + int xid6_size = file_size - xid6_offset; + if ( xid6_size > 0 ) + { + RETURN_ERR( xid6.resize( xid6_size ) ); + RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); + } + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_spc_info( header, xid6.begin(), xid6.size(), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_spc_file( header, data.begin(), data.end() - data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } +static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } + +gme_type_t_ const gme_spc_type [1] = {{ "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }}; + +// Setup + +blargg_err_t Spc_Emu::set_sample_rate_( int sample_rate ) +{ + RETURN_ERR( apu.init() ); + if ( sample_rate != native_sample_rate ) + { + RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) ); + RETURN_ERR( resampler.set_rate( (double) native_sample_rate / sample_rate ) ); // 0.9965 rolloff + } + return blargg_ok; +} + +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 [], int size ) +{ + assert( offsetof (header_t,unused2 [46]) == header_t::size ); + set_voice_count( Spc_Dsp::voice_count ); + if ( size < Snes_Spc::spc_min_file_size ) + return blargg_err_file_type; + + static const char* const names [Spc_Dsp::voice_count] = { + "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" + }; + set_voice_names( names ); + + return check_spc_header( in ); +} + +// Emulation + +void Spc_Emu::set_tempo_( double t ) +{ + apu.set_tempo( (int) (t * Snes_Spc::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_begin(), file_size() ) ); + filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) ); + apu.clear_echo( true ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::play_and_filter( int count, sample_t out [] ) +{ + RETURN_ERR( apu.play( count, out ) ); + filter.run( out, count ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::skip_( int count ) +{ + if ( sample_rate() != native_sample_rate ) + { + count = (int) (count * resampler.rate()) & ~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_( int count, sample_t out [] ) +{ + if ( sample_rate() == native_sample_rate ) + return play_and_filter( count, out ); + + int remain = count; + while ( remain > 0 ) + { + remain -= resampler.read( &out [count - remain], remain ); + if ( remain > 0 ) + { + int n = resampler.buffer_free(); + RETURN_ERR( play_and_filter( n, resampler.buffer() ) ); + resampler.write( n ); + } + } + check( remain == 0 ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::hash_( Hash_Function& out ) const +{ + hash_spc_file( header(), file_begin() + header_t::size, blargg_min( (size_t) ( 0x10200 - header_t::size ), (size_t) ( file_end() - file_begin() - header_t::size ) ), out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Spc_Emu.h b/Frameworks/GME/gme/Spc_Emu.h old mode 100755 new mode 100644 index 44b54c309..02be3fc66 --- a/Frameworks/GME/gme/Spc_Emu.h +++ b/Frameworks/GME/gme/Spc_Emu.h @@ -1,12 +1,20 @@ // Super Nintendo SPC music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SPC_EMU_H #define SPC_EMU_H -#include "Fir_Resampler.h" #include "Music_Emu.h" #include "Snes_Spc.h" +#include "Spc_Filter.h" + +#if GME_SPC_FAST_RESAMPLER + #include "Upsampler.h" + typedef Upsampler Spc_Emu_Resampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler<24> Spc_Emu_Resampler; +#endif class Spc_Emu : public Music_Emu { public: @@ -14,64 +22,72 @@ public: // handled by resampling the 32kHz output; emulation accuracy is not affected. enum { native_sample_rate = 32000 }; + // Disables annoying pseudo-surround effect some music uses + void disable_surround( bool disable = true ) { apu.disable_surround( disable ); } + + // Enables gaussian, cubic or sinc interpolation + void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); } + + Snes_Spc const* get_apu() const; + Snes_Spc * get_apu(); + // SPC file header - enum { header_size = 0x100 }; struct header_t { - char tag [35]; + enum { size = 0x100 }; + + char tag [35]; byte format; byte version; - byte pc [2]; + 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 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]; + 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 - 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; + header_t const& header() const { return *(header_t const*) file_begin(); } + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_spc_type; } + +// Implementation 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 ); + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t set_sample_rate_( int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int, sample_t [] ); + virtual blargg_err_t skip_( int ); + virtual void mute_voices_( int ); + virtual void set_tempo_( double ); + private: - byte const* file_data; - long file_size; - Fir_Resampler<24> resampler; + Spc_Emu_Resampler resampler; + Spc_Filter filter; Snes_Spc apu; + + byte const* trailer_() const; + int trailer_size_() const; + blargg_err_t play_and_filter( int count, sample_t out [] ); }; -inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } +inline Snes_Spc const* Spc_Emu::get_apu() const { return &apu; } +inline Snes_Spc * Spc_Emu::get_apu() { return &apu; } #endif diff --git a/Frameworks/GME/gme/Vgm_Emu.cpp b/Frameworks/GME/gme/Vgm_Emu.cpp old mode 100755 new mode 100644 index 7d037d077..4b8523a18 --- a/Frameworks/GME/gme/Vgm_Emu.cpp +++ b/Frameworks/GME/gme/Vgm_Emu.cpp @@ -1,412 +1,538 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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 ); - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 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" + +// FM emulators are internally quieter to avoid 16-bit overflow +double const fm_gain = 3.0; +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + resampler.set_callback( play_frame_, this ); + disable_oversampling_ = false; + muted_voices = 0; + set_type( gme_vgm_type ); + set_max_initial_silence( 1 ); set_silence_lookahead( 1 ); // tracks should already be trimmed - - static equalizer_t const eq = { -14.0, 80 }; + + static equalizer_t const eq = { -14.0, 80 , 0,0,0,0,0,0,0,0 }; set_equalizer( eq ); } -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; } - -gme_type_t_ const gme_vgm_type [1] = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }; -gme_type_t_ const gme_vgz_type [1] = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }; - -// 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 ); - //dprintf( "blip_time_factor: %ld\n", blip_time_factor ); - //dprintf( "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 ); -} - -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; -} +Vgm_Emu::~Vgm_Emu() { } + +void Vgm_Emu::unload() +{ + core.unload(); + Classic_Emu::unload(); +} + +// 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 int check_gd3_header( byte const h [], int remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + int gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100 + if ( length > 0 ) + { + int 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; + check( out->loop_length <= length ); + // TODO: Also set out->length? We now have play_length for suggested play time. + } + else + { + out->length = length; + out->intro_length = length; + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + get_vgm_length( header(), out ); + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + byte const* gd3_data = gd3 + gd3_header_size; + parse_gd3( gd3_data, gd3_data + gd3_size, out ); + } + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) +{ + *data = 0; + *size = 0; + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + *data = gd3; + *size = gd3_size + gd3_header_size; + } + + return blargg_ok; +} + +static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.data_size[0], sizeof(h.data_size) ); + out.hash_( &h.version[0], sizeof(h.version) ); + out.hash_( &h.psg_rate[0], sizeof(h.psg_rate) ); + out.hash_( &h.ym2413_rate[0], sizeof(h.ym2413_rate) ); + out.hash_( &h.track_duration[0], sizeof(h.track_duration) ); + out.hash_( &h.loop_offset[0], sizeof(h.loop_offset) ); + out.hash_( &h.loop_duration[0], sizeof(h.loop_duration) ); + out.hash_( &h.frame_rate[0], sizeof(h.frame_rate) ); + out.hash_( &h.noise_feedback[0], sizeof(h.noise_feedback) ); + out.hash_( &h.noise_width, sizeof(h.noise_width) ); + out.hash_( &h.sn76489_flags, sizeof(h.sn76489_flags) ); + out.hash_( &h.ym2612_rate[0], sizeof(h.ym2612_rate) ); + out.hash_( &h.ym2151_rate[0], sizeof(h.ym2151_rate) ); + out.hash_( &h.data_offset[0], sizeof(h.data_offset) ); + out.hash_( &h.segapcm_rate[0], sizeof(h.segapcm_rate) ); + out.hash_( &h.segapcm_reg[0], sizeof(h.segapcm_reg) ); + out.hash_( &h.rf5c68_rate[0], sizeof(h.rf5c68_rate) ); + out.hash_( &h.ym2203_rate[0], sizeof(h.ym2203_rate) ); + out.hash_( &h.ym2608_rate[0], sizeof(h.ym2608_rate) ); + out.hash_( &h.ym2610_rate[0], sizeof(h.ym2610_rate) ); + out.hash_( &h.ym3812_rate[0], sizeof(h.ym3812_rate) ); + out.hash_( &h.ym3526_rate[0], sizeof(h.ym3526_rate) ); + out.hash_( &h.y8950_rate[0], sizeof(h.y8950_rate) ); + out.hash_( &h.ymf262_rate[0], sizeof(h.ymf262_rate) ); + out.hash_( &h.ymf278b_rate[0], sizeof(h.ymf278b_rate) ); + out.hash_( &h.ymf271_rate[0], sizeof(h.ymf271_rate) ); + out.hash_( &h.ymz280b_rate[0], sizeof(h.ymz280b_rate) ); + out.hash_( &h.rf5c164_rate[0], sizeof(h.rf5c164_rate) ); + out.hash_( &h.pwm_rate[0], sizeof(h.pwm_rate) ); + out.hash_( &h.ay8910_rate[0], sizeof(h.ay8910_rate) ); + out.hash_( &h.ay8910_type, sizeof(h.ay8910_type) ); + out.hash_( &h.ay8910_flags, sizeof(h.ay8910_flags) ); + out.hash_( &h.ym2203_ay8910_flags, sizeof(h.ym2203_ay8910_flags) ); + out.hash_( &h.ym2608_ay8910_flags, sizeof(h.ym2608_ay8910_flags) ); + out.hash_( &h.reserved, sizeof(h.reserved) ); + out.hash_( &h.gbdmg_rate[0], sizeof(h.gbdmg_rate) ); + out.hash_( &h.nesapu_rate[0], sizeof(h.nesapu_rate) ); + out.hash_( &h.multipcm_rate[0], sizeof(h.multipcm_rate) ); + out.hash_( &h.upd7759_rate[0], sizeof(h.upd7759_rate) ); + out.hash_( &h.okim6258_rate[0], sizeof(h.okim6258_rate) ); + out.hash_( &h.okim6258_flags, sizeof(h.okim6258_flags) ); + out.hash_( &h.k054539_flags, sizeof(h.k054539_flags) ); + out.hash_( &h.c140_type, sizeof(h.c140_type) ); + out.hash_( &h.reserved_flags, sizeof(h.reserved_flags) ); + out.hash_( &h.okim6295_rate[0], sizeof(h.okim6295_rate) ); + out.hash_( &h.k051649_rate[0], sizeof(h.k051649_rate) ); + out.hash_( &h.k054539_rate[0], sizeof(h.k054539_rate) ); + out.hash_( &h.huc6280_rate[0], sizeof(h.huc6280_rate) ); + out.hash_( &h.c140_rate[0], sizeof(h.c140_rate) ); + out.hash_( &h.k053260_rate[0], sizeof(h.k053260_rate) ); + out.hash_( &h.pokey_rate[0], sizeof(h.pokey_rate) ); + out.hash_( &h.qsound_rate[0], sizeof(h.qsound_rate) ); + out.hash_( &h.reserved2[0], sizeof(h.reserved2) ); + out.hash_( &h.extra_offset[0], sizeof(h.extra_offset) ); + out.hash_( data, data_size ); +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector data; + blargg_vector gd3; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + int file_size = in.remain(); + if ( file_size <= h.size_min ) + return blargg_err_file_type; + + RETURN_ERR( in.read( &h, h.size_min ) ); + if ( !h.valid_tag() ) + return blargg_err_file_type; + + if ( h.size() > h.size_min ) + RETURN_ERR( in.read( &h.rf5c68_rate, h.size() - h.size_min ) ); + + h.cleanup(); + + int data_offset = get_le32( h.data_offset ) + offsetof( Vgm_Core::header_t, data_offset ); + int data_size = file_size - offsetof( Vgm_Core::header_t, data_offset ) - data_offset; + int gd3_offset = get_le32( h.gd3_offset ); + if ( gd3_offset > 0 ) + gd3_offset += offsetof( Vgm_Core::header_t, gd3_offset ); + + int amount_to_skip = gd3_offset - h.size(); + + if ( gd3_offset > 0 && gd3_offset > data_offset ) + { + data_size = gd3_offset - data_offset; + amount_to_skip = 0; + + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - h.size() ) ); + RETURN_ERR( in.read( data.begin(), data_size ) ); + } + + int remain = file_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + RETURN_ERR( in.skip( amount_to_skip ) ); + RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); + int 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() ) ); + } + + if ( data_offset > gd3_offset ) + { + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - gd3_offset - sizeof gd3_h - gd3.size() ) ); + RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); + } + } + + return blargg_ok; + } + + 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 blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_vgm_file( h, data.begin(), data.end() - data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } +static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } + +gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }}; + +gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }}; + +// Setup + +void Vgm_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) +{ + RETURN_ERR( core.stereo_buf[0].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[1].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[2].set_sample_rate( sample_rate, 1000 / 30 ) ); + core.set_sample_rate( sample_rate ); + return Classic_Emu::set_sample_rate_( sample_rate ); +} + +void Vgm_Emu::update_eq( blip_eq_t const& eq ) +{ + core.psg[0].treble_eq( eq ); + core.psg[1].treble_eq( eq ); + core.ay[0].treble_eq( eq ); + core.ay[1].treble_eq( eq ); + core.huc6280[0].treble_eq( eq ); + core.huc6280[1].treble_eq( eq ); + core.pcm.treble_eq( eq ); +} + +void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < core.psg[0].osc_count ) + { + core.psg[0].set_output( i, c, l, r ); + core.psg[1].set_output( i, c, l, r ); + } +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + muted_voices = mask; + + Classic_Emu::mute_voices_( mask ); + + // TODO: what was this for? + //core.pcm.output( &core.blip_buf ); + + // TODO: silence PCM if FM isn't used? + if ( core.uses_fm() ) + { + core.psg[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.psg[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.ay[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + core.ay[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + for ( unsigned i = 0, j = 1; i < core.huc6280[0].osc_count; i++, j <<= 1) + { + Blip_Buffer * center = ( mask & j ) ? 0 : core.stereo_buf[2].center(); + Blip_Buffer * left = ( mask & j ) ? 0 : core.stereo_buf[2].left(); + Blip_Buffer * right = ( mask & j ) ? 0 : core.stereo_buf[2].right(); + core.huc6280[0].set_output( i, center, left, right ); + core.huc6280[1].set_output( i, center, left, right ); + } + if ( core.ym2612[0].enabled() ) + { + core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); + core.ym2612[0].mute_voices( mask ); + if ( core.ym2612[1].enabled() ) + core.ym2612[1].mute_voices( mask ); + } + + if ( core.ym2413[0].enabled() ) + { + int m = mask & 0x3F; + if ( mask & 0x20 ) + m |= 0x01E0; // channels 5-8 + if ( mask & 0x40 ) + m |= 0x3E00; + core.ym2413[0].mute_voices( m ); + if ( core.ym2413[1].enabled() ) + core.ym2413[1].mute_voices( m ); + } + + if ( core.ym2151[0].enabled() ) + { + core.ym2151[0].mute_voices( mask ); + if ( core.ym2151[1].enabled() ) + core.ym2151[1].mute_voices( mask ); + } + + if ( core.c140.enabled() ) + { + int m = 0; + int m_add = 7; + for ( unsigned i = 0; i < 8; i++, m_add <<= 3 ) + { + if ( mask & ( 1 << i ) ) m += m_add; + } + core.c140.mute_voices( m ); + } + + if ( core.rf5c68.enabled() ) + { + core.rf5c68.mute_voices( mask ); + } + + if ( core.rf5c164.enabled() ) + { + core.rf5c164.mute_voices( mask ); + } + } +} + +blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size ) +{ + RETURN_ERR( core.load_mem( data, size ) ); + + set_voice_count( core.psg[0].osc_count ); + + double fm_rate = 0.0; + if ( !disable_oversampling_ ) + fm_rate = sample_rate() * oversample_factor; + RETURN_ERR( core.init_chips( &fm_rate ) ); + + double psg_gain = ( ( core.header().psg_rate[3] & 0xC0 ) == 0x40 ) ? 0.5 : 1.0; + + if ( core.uses_fm() ) + { + set_voice_count( 8 ); + RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, gain() ) ); + RETURN_ERR( resampler.reset( core.stereo_buf[0].length() * sample_rate() / 1000 ) ); + core.psg[0].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.psg[1].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.ay[0].volume( 0.135 * fm_gain * gain() ); + core.ay[1].volume( 0.135 * fm_gain * gain() ); + core.huc6280[0].volume( 0.135 * fm_gain * gain() ); + core.huc6280[1].volume( 0.135 * fm_gain * gain() ); + } + else + { + core.psg[0].volume( psg_gain * gain() ); + core.psg[1].volume( psg_gain * gain() ); + } + + 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( core.uses_fm() ? fm_names : psg_names ); + + static int const types [8] = { + wave_type+1, wave_type+2, wave_type+3, noise_type+1, + 0, 0, 0, 0 + }; + set_voice_types( types ); + + return Classic_Emu::setup_buffer( core.stereo_buf[0].center()->clock_rate() ); +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + core.start_track(); + + mute_voices_(muted_voices); + + if ( core.uses_fm() ) + resampler.clear(); + + return blargg_ok; +} + +inline void Vgm_Emu::check_end() +{ + if ( core.track_ended() ) + set_track_ended(); +} + +inline void Vgm_Emu::check_warning() +{ + const char* w = core.warning(); + if ( w ) + set_warning( w ); +} + +blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) +{ + check_end(); + time_io = core.run_psg( msec ); + check_warning(); + return blargg_ok; +} + +inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) +{ + check_end(); + int result = core.play_frame( blip_time, sample_count, buf ); + check_warning(); + return result; +} + +int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) +{ + return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c ); +} + +blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) +{ + if ( !core.uses_fm() ) + return Classic_Emu::play_( count, out ); + + Stereo_Buffer * secondaries[] = { &core.stereo_buf[1], &core.stereo_buf[2] }; + resampler.dual_play( count, out, core.stereo_buf[0], secondaries, 2 ); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const +{ + byte const* p = file_begin() + header().size(); + byte const* e = file_end(); + int data_offset = get_le32( header().data_offset ); + if ( data_offset ) + p += data_offset + offsetof( header_t, data_offset ) - header().size(); + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset > 0 && gd3_offset + offsetof( header_t, gd3_offset ) > data_offset + offsetof( header_t, data_offset ) ) + e = file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + hash_vgm_file( header(), p, e - p, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Vgm_Emu.h b/Frameworks/GME/gme/Vgm_Emu.h old mode 100755 new mode 100644 index bcb784d56..3aaddf851 --- a/Frameworks/GME/gme/Vgm_Emu.h +++ b/Frameworks/GME/gme/Vgm_Emu.h @@ -1,84 +1,69 @@ -// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator +// Sega VGM music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef VGM_EMU_H #define VGM_EMU_H -#include "Vgm_Emu_Impl.h" +#include "Classic_Emu.h" +#include "Dual_Resampler.h" +#include "Vgm_Core.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 { +/* Emulates VGM music using SN76489/SN76496 PSG, and 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. A YM2413 is supported but not provided separately from the library. */ +class Vgm_Emu : public Classic_Emu { 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; } + bool is_classic_emu() const { return !core.uses_fm(); } - // Disable running FM chips at higher than normal rate. Will result in slightly + // Disables 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; } + 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]; - }; + // VGM file header (see Vgm_Core.h) + typedef Vgm_Core::header_t header_t; // 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 - 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() + header_t const& header() const { return core.header(); } + blargg_err_t hash_( Hash_Function& ) const; + + // Gd3 tag for currently loaded file + blargg_err_t gd3_data( const unsigned char ** data, int * size ); + + static gme_type_t static_type() { return gme_vgm_type; } + +// Implementation 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 load_mem_( byte const [], int ); + blargg_err_t set_sample_rate_( int sample_rate ); blargg_err_t start_track_( int ); - blargg_err_t play_( long count, sample_t* ); + blargg_err_t play_( int 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& ); + virtual void set_tempo_( double ); + virtual void mute_voices_( int mask ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + 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(); + unsigned muted_voices; + Dual_Resampler resampler; + Vgm_Core core; + + void check_end(); + void check_warning(); + int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ); + static int play_frame_( void*, blip_time_t, int, sample_t [] ); }; #endif diff --git a/Frameworks/GME/gme/Ym2413_Emu.cpp b/Frameworks/GME/gme/Ym2413_Emu.cpp old mode 100755 new mode 100644 index ede673045..bd4349e01 --- a/Frameworks/GME/gme/Ym2413_Emu.cpp +++ b/Frameworks/GME/gme/Ym2413_Emu.cpp @@ -1,21 +1,78 @@ - -// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip - -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#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* ) { } - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Ym2413_Emu.h" +#include "ym2413.h" + +Ym2413_Emu::Ym2413_Emu() { opll = 0; } + +Ym2413_Emu::~Ym2413_Emu() +{ + if ( opll ) ym2413_shutdown( opll ); +} + +int Ym2413_Emu::set_rate( int sample_rate, int clock_rate ) +{ + if ( opll ) + { + ym2413_shutdown( opll ); + opll = 0; + } + + opll = ym2413_init( clock_rate, sample_rate, 0 ); + if ( !opll ) + return 1; + + reset(); + return 0; +} + +void Ym2413_Emu::reset() +{ + ym2413_reset_chip( opll ); + ym2413_set_mask( opll, 0 ); +} + +static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; + +void Ym2413_Emu::write( int addr, int data ) +{ + ym2413_update_one( opll, DUMMYBUF, 0 ); + ym2413_write( opll, 0, addr ); + ym2413_write( opll, 1, data ); +} + +void Ym2413_Emu::mute_voices( int mask ) +{ + ym2413_set_mask( opll, mask ); +} + +void Ym2413_Emu::run( int pair_count, sample_t* out ) +{ + SAMP bufMO[ 1024 ]; + SAMP bufRO[ 1024 ]; + SAMP * buffers[2] = { bufMO, bufRO }; + + while (pair_count > 0) + { + int todo = pair_count; + if (todo > 1024) todo = 1024; + ym2413_update_one( opll, buffers, todo ); + + for (int i = 0; i < todo; i++) + { + int output_l, output_r; + int output = bufMO [i]; + output += bufRO [i]; + output *= 3; + output_l = output + out [0]; + output_r = output + out [1]; + if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); + if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); + out [0] = output_l; + out [1] = output_r; + out += 2; + } + + pair_count -= todo; + } +} diff --git a/Frameworks/GME/gme/Ym2413_Emu.h b/Frameworks/GME/gme/Ym2413_Emu.h old mode 100755 new mode 100644 index 98a2a48e1..59cb61bf8 --- a/Frameworks/GME/gme/Ym2413_Emu.h +++ b/Frameworks/GME/gme/Ym2413_Emu.h @@ -1,33 +1,37 @@ -// YM2413 FM sound chip emulator interface - -// Game_Music_Emu 0.5.2 -#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 +// YM2413 FM sound chip emulator interface + +// Game_Music_Emu $vers +#ifndef YM2413_EMU_H +#define YM2413_EMU_H + +struct OPLL; + +class Ym2413_Emu { + void* opll; +public: + Ym2413_Emu(); + ~Ym2413_Emu(); + + static bool supported() { return true; } + + // Sets output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + int set_rate( int sample_rate, int clock_rate ); + + // Resets to power-up state + void reset(); + + // Mutes voice n if bit n (1 << n) of mask is set + enum { channel_count = 14 }; + void mute_voices( int mask ); + + // Writes data to addr + void write( int addr, int data ); + + // Runs and writes pair_count*2 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/Frameworks/GME/gme/Ym2612_Emu.cpp b/Frameworks/GME/gme/Ym2612_Emu.cpp old mode 100755 new mode 100644 index 41ebb093d..dd850e740 --- a/Frameworks/GME/gme/Ym2612_Emu.cpp +++ b/Frameworks/GME/gme/Ym2612_Emu.cpp @@ -1,1319 +1,7 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -// Based on Gens 2.10 ym2612.c - #include "Ym2612_Emu.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_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_Impl -{ - enum { channel_count = Ym2612_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_Emu::sample_t* ); -}; - -void Ym2612_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_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_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); +#ifdef USE_GENS +#include "Ym2612_Emu_Gens.cpp" #else - sl.TLL = sl.TL << (ENV_HBITS - 7); +#include "Ym2612_Emu_MAME.cpp" #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_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_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_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_Emu::set_rate( double sample_rate, double clock_rate ) -{ - if ( !impl ) - { - impl = (Ym2612_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_Emu::~Ym2612_Emu() -{ - free( impl ); -} - -inline void Ym2612_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_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_Emu::reset() -{ - impl->reset(); -} - -void Ym2612_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_Emu::write0( int addr, int data ) -{ - impl->write0( addr, data ); -} - -void Ym2612_Emu::write1( int addr, int data ) -{ - impl->write1( addr, data ); -} - -void Ym2612_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_Emu::sample_t*, int ); -}; - -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); - -template -void ym2612_update_chan::func( tables_t& g, channel_t& ch, - Ym2612_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_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_Impl::run( int pair_count, Ym2612_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_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff --git a/Frameworks/GME/gme/Ym2612_Emu.h b/Frameworks/GME/gme/Ym2612_Emu.h old mode 100755 new mode 100644 index 383ac72d7..d99359187 --- a/Frameworks/GME/gme/Ym2612_Emu.h +++ b/Frameworks/GME/gme/Ym2612_Emu.h @@ -1,10 +1,16 @@ -// YM2612 FM sound chip emulator interface +// YM2612 FM sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef YM2612_EMU_H #define YM2612_EMU_H +// Gens is buggy and inaccurate, but faster +// #define USE_GENS +#ifdef USE_GENS struct Ym2612_Impl; +#else +typedef void Ym2612_Impl; +#endif class Ym2612_Emu { Ym2612_Impl* impl; @@ -12,24 +18,24 @@ public: Ym2612_Emu() { impl = 0; } ~Ym2612_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 ); + // Sets sample rate and chip clock rate, in Hz. Returns non-zero + // if error. If clock_rate=0, uses sample_rate*144 + const char* set_rate( double sample_rate, double clock_rate = 0 ); - // Reset to power-up state + // Resets to power-up state void reset(); - // Mute voice n if bit n (1 << n) of mask is set + // Mutes 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 + // Writes 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 + // Writes 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 + // Runs and adds pair_count*2 samples into current output buffer contents typedef short sample_t; enum { out_chan_count = 2 }; // stereo void run( int pair_count, sample_t* out ); diff --git a/Frameworks/GME/gme/blargg_common.h b/Frameworks/GME/gme/blargg_common.h old mode 100755 new mode 100644 index e48d6469b..05c74ffb2 --- a/Frameworks/GME/gme/blargg_common.h +++ b/Frameworks/GME/gme/blargg_common.h @@ -1,175 +1,224 @@ -// 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 - -// 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 - #if __cplusplus < 199711 - #define BLARGG_THROWS( spec ) - #else - #define BLARGG_THROWS( spec ) throw 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 - -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&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 -#include - -#if INT_MAX >= 0x7FFFFFFF - typedef int blargg_long; -#else - typedef long blargg_long; -#endif - -#if UINT_MAX >= 0xFFFFFFFF - typedef unsigned blargg_ulong; -#else - typedef unsigned long blargg_ulong; -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) - #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; -#endif - -#endif -#endif +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// $package +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include + +typedef const char* blargg_err_t; // 0 on success, otherwise error string + +// Success; no error +int const blargg_ok = 0; + +// BLARGG_RESTRICT: equivalent to C99's restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +#if __cplusplus >= 199711 + #define BLARGG_MUTABLE mutable +#else + #define BLARGG_MUTABLE +#endif + +/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). +I don't just use 'abcd' because that's implementation-dependent. */ +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +Can be used at file, function, or class scope. */ +#ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) +#else + // Others fail when declaring same function multiple times in class, + // so differentiate them by line + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) +#endif + +/* Pure virtual functions cause a vtable entry to a "called pure virtual" +error handler, requiring linkage to the C++ runtime library. This macro is +used in place of the "= 0", and simply expands to its argument. During +development, it expands to "= 0", allowing detection of missing overrides. */ +#define BLARGG_PURE( def ) def + +/* My code depends on ASCII anywhere a character or string constant is +compared with data read from a file, and anywhere file data is read and +treated as a string. */ +#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 + #error "ASCII character set required" +#endif + +/* My code depends on int being at least 32 bits. Almost everything these days +uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints +to test with. The issue can't be gotten around by using a suitable blargg_int +everywhere either, because int is often converted to implicitly when doing +arithmetic on smaller types. */ +#if UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" +#endif + +// In case compiler doesn't support these properly. Used rarely. +#define STATIC_CAST(T,expr) static_cast (expr) +#define CONST_CAST( T,expr) const_cast (expr) + +// User configuration can override the above macros if necessary +#include "blargg_config.h" + +#ifdef BLARGG_NAMESPACE + #define BLARGG_NAMESPACE_BEGIN namespace BLARGG_NAMESPACE { + #define BLARGG_NAMESPACE_END } + + BLARGG_NAMESPACE_BEGIN + BLARGG_NAMESPACE_END + using namespace BLARGG_NAMESPACE; +#else + #define BLARGG_NAMESPACE_BEGIN + #define BLARGG_NAMESPACE_END +#endif + +BLARGG_NAMESPACE_BEGIN + +/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a +future version. In GCC, we can let the compiler warn. In other compilers, +we strip it out unless BLARGG_LEGACY is true. */ +#if BLARGG_LEGACY + // Allow old client code to work without warnings + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) text +#elif __GNUC__ >= 4 + // In GCC, we can mark declarations and let the compiler warn + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text +#else + // By default, deprecated items are removed, to avoid use in new code + #define BLARGG_DEPRECATED_TEXT( text ) + #define BLARGG_DEPRECATED( text ) +#endif + +/* BOOST::int8_t, BOOST::int32_t, etc. +I used BOOST since I originally was going to allow use of the boost library +for prividing the definitions. If I'm defining them, they must be scoped or +else they could conflict with the standard ones at global scope. Even if +HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at +global scope already. */ +#if defined (HAVE_STDINT_H) || \ + UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF + #include + #define BOOST +#else + struct BOOST + { + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + }; +#endif + +/* My code is not written with exceptions in mind, so either uses new (nothrow) +OR overrides operator new in my classes. The former is best since clients +creating objects will get standard exceptions on failure, but that causes it +to require the standard C++ library. So, when the client is using the C +interface, I override operator new to use malloc. */ + +// BLARGG_DISABLE_NOTHROW is put inside classes +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if NULL can be returned + #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 + #define BLARGG_THROWS_NOTHING throw () + #else + #define BLARGG_THROWS_NOTHING + #endif + + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ + void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } + + #define BLARGG_NEW new +#else + // BLARGG_NEW is used in place of new in library code + #include + #define BLARGG_NEW new (std::nothrow) +#endif + + class blargg_vector_ { + protected: + void* begin_; + size_t size_; + void init(); + blargg_err_t resize_( size_t n, size_t elem_size ); + public: + size_t size() const { return size_; } + void clear(); + }; + +// Very lightweight vector for POD types (no constructor/destructor) +template +class blargg_vector : public blargg_vector_ { + union T_must_be_pod { T t; }; // fails if T is not POD +public: + blargg_vector() { init(); } + ~blargg_vector() { clear(); } + + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } + + T* begin() { return static_cast (begin_); } + const T* begin() const { return static_cast (begin_); } + + T* end() { return static_cast (begin_) + size_; } + const T* end() const { return static_cast (begin_) + size_; } + + T& operator [] ( size_t n ) + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } + + const T& operator [] ( size_t n ) const + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } +}; + +// Callback function with user data. +// blargg_callback set_callback; // for user, this acts like... +// void set_callback( T func, void* user_data = NULL ); // ...this +// To call function, do set_callback.f( .. set_callback.data ... ); +template +struct blargg_callback +{ + T f; + void* data; + blargg_callback() { f = NULL; } + void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } +}; + +#ifndef _WIN32 + // Not supported on any other platforms + #undef BLARGG_UTF8_PATHS +#endif + +BLARGG_DEPRECATED( typedef signed int blargg_long; ) +BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) +#if BLARGG_LEGACY + #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT +#endif + +BLARGG_NAMESPACE_END + +#endif diff --git a/Frameworks/GME/gme/blargg_config.h b/Frameworks/GME/gme/blargg_config.h old mode 100755 new mode 100644 index 9e9c751d6..c31f7d53b --- a/Frameworks/GME/gme/blargg_config.h +++ b/Frameworks/GME/gme/blargg_config.h @@ -1,30 +1,56 @@ -// 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 to support only the listed game music types. See gme_type_list.cpp -// for a list of all types. -//#define GME_TYPE_LIST gme_nsf_type, gme_gbs_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 +// Library configuration. Modify this file as necessary. + +// $package +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment a #define line below to have effect described. + +// Allow static linking with this library and one of my other libraries +// in the same program. +//#define BLARGG_NAMESPACE blargg_gme + +// Use zlib for transparent decompression of gzipped files. +//#define HAVE_ZLIB_H + +// Support only listed music types. Remove a line to disable that type. +/* #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_sfm_type,\ + gme_sgc_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type +*/ + +// Enable platform-specific optimizations. +//#define BLARGG_NONPORTABLE 1 + +// Use faster sample rate convertor for SPC music. +//#define GME_SPC_FAST_RESAMPLER 1 + +// Use faster sample rate convertor for VGM and GYM music. +//#define GME_VGM_FAST_RESAMPLER 1 + +// Use faster, significantly lower quality sound synthesis for classic emulators. +//#define BLIP_BUFFER_FAST 1 + +// Reduce memory usage of gme.h by disabling gme_set_effects_config(). +//#define GME_DISABLE_EFFECTS 1 + +// Force library to use assume big-endian processor. +//#define BLARGG_BIG_ENDIAN 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/Frameworks/GME/gme/blargg_endian.h b/Frameworks/GME/gme/blargg_endian.h old mode 100755 new mode 100644 index 671655654..290175f71 --- a/Frameworks/GME/gme/blargg_endian.h +++ b/Frameworks/GME/gme/blargg_endian.h @@ -1,158 +1,203 @@ -// CPU Byte Order Utilities - -// Game_Music_Emu 0.5.2 -#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 (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 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 (__mips__) || defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#else - // 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 char const*) p) [1] * 0x100u + - ((unsigned char const*) p) [0]; -} -inline unsigned get_be16( void const* p ) { - return ((unsigned char const*) p) [0] * 0x100u + - ((unsigned char const*) p) [1]; -} -inline blargg_ulong get_le32( void const* p ) { - return ((unsigned char const*) p) [3] * 0x01000000u + - ((unsigned char const*) p) [2] * 0x00010000u + - ((unsigned char const*) p) [1] * 0x00000100u + - ((unsigned char const*) p) [0]; -} -inline blargg_ulong get_be32( void const* p ) { - return ((unsigned char const*) p) [0] * 0x01000000u + - ((unsigned char const*) p) [1] * 0x00010000u + - ((unsigned char const*) p) [2] * 0x00000100u + - ((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) [3] = (unsigned char) (n >> 24); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be32( void* p, blargg_ulong n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [3] = (unsigned char) n; -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #endif - - #if BLARGG_CPU_POWERPC && defined (__MWERKS__) - // PowerPC has special byte-reversed instructions - // to do: assumes that PowerPC is running in big-endian mode - // to do: implement for other compilers which don't support these macros - #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) - #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) - #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) - #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif +// CPU Byte Order Utilities + +// $package +#ifndef BLARGG_ENDIAN_H +#define BLARGG_ENDIAN_H + +#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 + +BLARGG_NAMESPACE_BEGIN + +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 unsigned get_le24( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be24( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [2]; +} + +inline unsigned get_le32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [3] << 24 | + (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 24 | + (unsigned) ((unsigned char const*) p) [1] << 16 | + (unsigned) ((unsigned char const*) p) [2] << 8 | + (unsigned) ((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, unsigned 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, unsigned 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 ) (*(BOOST::uint16_t const*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::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( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } +inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } +inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } + +BLARGG_NAMESPACE_END + +#endif diff --git a/Frameworks/GME/gme/blargg_source.h b/Frameworks/GME/gme/blargg_source.h old mode 100755 new mode 100644 index 945bf3498..a81673619 --- a/Frameworks/GME/gme/blargg_source.h +++ b/Frameworks/GME/gme/blargg_source.h @@ -1,78 +1,174 @@ -// Included at the beginning of library source files, 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 ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -inline void blargg_dprintf_( const char*, ... ) { } -#undef dprintf -#define dprintf (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 - -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -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; - -// deprecated -#define BLARGG_CHECK_ALLOC CHECK_ALLOC -#define BLARGG_RETURN_ERR RETURN_ERR - -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif +/* Included at the beginning of library source files, AFTER all other #include +lines. Sets up helpful macros and services used in my source code. Since this +is only "active" in my source code, I don't have to worry about polluting the +global namespace with unprefixed names. */ + +// $package +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +#ifndef BLARGG_COMMON_H // optimization only + #include "blargg_common.h" +#endif +#include "blargg_errors.h" +#include "gme_custom_dprintf.h" + +#include /* memcpy(), memset(), memmove() */ +#include /* offsetof() */ + +/* The following four macros are for debugging only. Some or all might be +defined to do nothing, depending on the circumstances. Described is what +happens when a particular macro is defined to do something. When defined to +do nothing, the macros do NOT evaluate their argument(s). */ + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking internal state and consistency. A failed assertion indicates a bug +in MY code. + +void assert( bool expr ); */ +#include + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking caller-supplied parameters and operations that are outside the +control of the module. A failed requirement probably indicates a bug in YOUR +code. + +void require( bool expr ); */ +#undef require +#define require( expr ) assert( expr ) + +/* Like printf() except output goes to debugging console/file. + +void dprintf( const char format [], ... ); */ + +#ifdef CUSTOM_DPRINTF_FUNCTION + +static inline void dprintf( const char * fmt, ... ) +{ + if (gme_custom_dprintf) + { + va_list vl; + va_start(vl, fmt); + gme_custom_dprintf(fmt, vl); + va_end(vl); + } +} + +#else + +#ifdef NDEBUG +static inline void blargg_dprintf_( const char [], ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ +#else +#include +#include +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ +#ifndef _WIN32 +#include +static inline void blargg_dprintf_( const char * fmt, ... ) +{ + char error[512]; + va_list vl; + va_start(vl, fmt); + vsnprintf( error, 511, fmt, vl ); + va_end(vl); + fputs( error, stderr ); +} +#else +#include +static inline void blargg_dprintf_( const char * fmt, ... ) +{ + char error[512]; + va_list vl; + va_start(vl, fmt); + vsnprintf_s( error, 511, 511, fmt, vl ); + va_end(vl); + OutputDebugStringA( error ); +} +#endif +#endif + +#endif + +/* If expr is false, prints file and line number to debug console/log, then +continues execution normally. Meant for flagging potential problems or things +that should be looked into, but that aren't serious problems. + +void check( bool expr ); */ +#undef check +#define check( expr ) ((void) 0) + +/* If expr yields non-NULL error string, returns it from current function, +otherwise continues normally. */ +#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 NULL, returns out-of-memory error, otherwise continues normally. */ +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) \ + do {\ + if ( !(ptr) )\ + return blargg_err_memory;\ + } while ( 0 ) + +/* The usual min/max functions for built-in types. + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } */ +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && BLARGG_BUILD_DLL + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (__GNUC__) + // can always set visibility, even when not building DLL + #define BLARGG_EXPORT __attribute__ ((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + +#if BLARGG_LEGACY + #define BLARGG_CHECK_ALLOC CHECK_ALLOC + #define BLARGG_RETURN_ERR RETURN_ERR +#endif + +// Called after failed operation when overall operation may still complete OK. +// Only used by unit testing framework. +#undef ACK_FAILURE +#define ACK_FAILURE() ((void)0) + +/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc. +and check */ +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/Frameworks/GME/gme/gme.cpp b/Frameworks/GME/gme/gme.cpp old mode 100755 new mode 100644 index d6cebfa8a..59e699161 --- a/Frameworks/GME/gme/gme.cpp +++ b/Frameworks/GME/gme/gme.cpp @@ -1,256 +1,433 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +#if !GME_DISABLE_EFFECTS +#include "Effects_Buffer.h" +#endif +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2003-2009 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" + +#ifndef GME_TYPE_LIST + +// Default list of all supported game music types (copy this to blargg_config.h +// if you want to modify it) +#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_sfm_type,\ + gme_sgc_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type + +#endif + +static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; + +gme_type_t const* gme_type_list() +{ + return gme_type_list_; +} + +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','F','M','1'): return "SFM"; + case BLARGG_4CHAR('S','G','C',0x1A): return "SGC"; + case BLARGG_4CHAR('S','N','E','S'): return "SPC"; + case BLARGG_4CHAR('V','g','m',' '): return "VGM"; + } + 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 +} + +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 ); + + gme_type_t const* types = gme_type_list_; + for ( ; *types; types++ ) + if ( !strcmp( extension, (*types)->extension_ ) ) + break; + return *types; +} + +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 blargg_ok; +} + +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ) +{ + require( (data || !size) && out ); + *out = NULL; + + gme_type_t file_type = 0; + if ( size >= 4 ) + file_type = gme_identify_extension( gme_identify_header( data ) ); + if ( !file_type ) + return blargg_err_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; +} + +gme_err_t gme_open_file( const char path [], Music_Emu** out, int sample_rate ) +{ + require( path && out ); + *out = NULL; + + 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 blargg_err_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; +} + +Music_Emu* gme_new_emu( gme_type_t type, int rate ) +{ + if ( type ) + { + if ( rate == gme_info_only ) + return type->new_info(); + + Music_Emu* gme = type->new_emu(); + if ( gme ) + { + #if !GME_DISABLE_EFFECTS + if ( type->flags_ & 1 ) + { + gme->effects_buffer_ = BLARGG_NEW Simple_Effects_Buffer; + if ( gme->effects_buffer_ ) + gme->set_buffer( gme->effects_buffer_ ); + } + + if ( !(type->flags_ & 1) || gme->effects_buffer_ ) + #endif + { + if ( !gme->set_sample_rate( rate ) ) + { + check( gme->type() == type ); + return gme; + } + } + delete gme; + } + } + return NULL; +} + +gme_err_t gme_load_file( Music_Emu* gme, const char path [] ) { return gme->load_file( path ); } + +gme_err_t gme_load_data( Music_Emu* gme, void const* data, long size ) +{ + Mem_File_Reader in( data, size ); + return gme->load( in ); +} + +gme_err_t gme_load_custom( Music_Emu* gme, gme_reader_t func, long size, void* data ) +{ + Callback_Reader in( func, size, data ); + return gme->load( in ); +} + +void gme_delete( Music_Emu* gme ) { delete gme; } + +gme_type_t gme_type( Music_Emu const* gme ) { return gme->type(); } + +const char* gme_warning( Music_Emu* gme ) { return gme->warning(); } + +int gme_track_count( Music_Emu const* gme ) { return gme->track_count(); } + +struct gme_info_t_ : gme_info_t +{ + track_info_t info; + + BLARGG_DISABLE_NOTHROW +}; + +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 blargg_ok; +} + +void gme_free_info( gme_info_t* info ) +{ + delete STATIC_CAST(gme_info_t_*,info); +} + +void* gme_user_data ( Music_Emu const* gme ) { return gme->user_data(); } +void gme_set_user_data ( Music_Emu* gme, void* new_user_data ) { gme->set_user_data( new_user_data ); } +void gme_set_user_cleanup(Music_Emu* gme, gme_user_cleanup_t func ){ gme->set_user_cleanup( func ); } + +gme_err_t gme_start_track ( Music_Emu* gme, int index ) { return gme->start_track( index ); } +gme_err_t gme_play ( Music_Emu* gme, int n, short p [] ) { return gme->play( n, p ); } +void gme_set_fade ( Music_Emu* gme, int start_msec ) { gme->set_fade( start_msec ); } +gme_bool gme_track_ended ( Music_Emu const* gme ) { return gme->track_ended(); } +int gme_tell ( Music_Emu const* gme ) { return gme->tell(); } +gme_err_t gme_seek ( Music_Emu* gme, int msec ) { return gme->seek( msec ); } +int gme_voice_count ( Music_Emu const* gme ) { return gme->voice_count(); } +void gme_ignore_silence ( Music_Emu* gme, gme_bool disable ) { gme->ignore_silence( disable != 0 ); } +void gme_set_tempo ( Music_Emu* gme, double t ) { gme->set_tempo( t ); } +void gme_mute_voice ( Music_Emu* gme, int index, gme_bool mute ){ gme->mute_voice( index, mute != 0 ); } +void gme_mute_voices ( Music_Emu* gme, int mask ) { gme->mute_voices( mask ); } +void gme_set_equalizer ( Music_Emu* gme, gme_equalizer_t const* eq ) { gme->set_equalizer( *eq ); } +void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); } +const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); } -#include "Music_Emu.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" - -#ifndef GME_TYPE_LIST - -// Default list of all supported game music types (copy this to blargg_config.h -// if you want to modify it) -#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 - -#endif - -static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; - -gme_type_t const* gme_type_list() +void gme_effects( Music_Emu const* gme, gme_effects_t* out ) { - return gme_type_list_; -} + static gme_effects_t const zero = { 0, 0, 0,0,0,0,0,0, 0, 0, 0,0,0,0,0,0 }; + *out = zero; -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"; - } - 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 -} - -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; -} - -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; -} - -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long 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; -} - -gme_err_t gme_open_file( const char* path, Music_Emu** out, long 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; -} - -Music_Emu* gme_new_emu( gme_type_t type, long rate ) -{ - if ( type ) - { - if ( rate == gme_info_only ) - return type->new_info(); - - Music_Emu* me = type->new_emu(); - if ( me ) - { - #if !GME_DISABLE_STEREO_DEPTH - if ( type->flags_ & 1 ) - { - me->effects_buffer = BLARGG_NEW Effects_Buffer; - 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; -} - -gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } - -gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) -{ - Mem_File_Reader in( data, size ); - return me->load( in ); -} - -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 ); -} - -void gme_delete( Music_Emu* me ) { delete me; } - -gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } - -const char* gme_warning( Music_Emu* me ) { return me->warning(); } - -int gme_track_count( Music_Emu const* me ) { return me->track_count(); } - -const char* gme_track_info( Music_Emu const* me, track_info_t* out, int track ) -{ - return me->track_info( out, track ); -} - -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 -} - -void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } -void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } -void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } - -gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } -gme_err_t gme_play ( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } -void gme_set_fade ( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } -int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } -long gme_tell ( Music_Emu const* me ) { return me->tell(); } -gme_err_t gme_seek ( Music_Emu* me, long msec ) { return me->seek( msec ); } -int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } -void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } -void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } -void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } -void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } -void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } -gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } -const char** gme_voice_names ( Music_Emu const* me ) { return me->voice_names(); } + #if !GME_DISABLE_EFFECTS + { + Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_); + if ( b ) + { + out->enabled = b->config().enabled; + out->echo = b->config().echo; + out->stereo = b->config().stereo; + out->surround = b->config().surround; + } + } + #endif +} + +void gme_set_effects( Music_Emu* gme, gme_effects_t const* in ) +{ + #if !GME_DISABLE_EFFECTS + { + Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_); + if ( b ) + { + b->config().enabled = false; + if ( in ) + { + b->config().enabled = in->enabled; + b->config().echo = in->echo; + b->config().stereo = in->stereo; + b->config().surround = in->surround; + } + b->apply_config(); + } + } + #endif +} + +void gme_set_stereo_depth( Music_Emu* gme, double depth ) +{ + #if !GME_DISABLE_EFFECTS + { + if ( gme->effects_buffer_ ) + { + gme_effects_t cfg; + gme_effects( gme, &cfg ); + cfg.enabled = (depth > 0.0); + cfg.echo = depth; + cfg.stereo = depth; + cfg.surround = true; + gme_set_effects( gme, &cfg ); + } + } + #endif +} + +#define ENTRY( name ) { blargg_err_##name, gme_err_##name } +static blargg_err_to_code_t const gme_codes [] = +{ + ENTRY( generic ), + ENTRY( memory ), + ENTRY( caller ), + ENTRY( internal ), + ENTRY( limitation ), + + ENTRY( file_missing ), + ENTRY( file_read ), + ENTRY( file_io ), + ENTRY( file_eof ), + + ENTRY( file_type ), + ENTRY( file_feature ), + ENTRY( file_corrupt ), + + { 0, -1 } +}; +#undef ENTRY + +static int err_code( gme_err_t err ) +{ + return blargg_err_to_code( err, gme_codes ); +} + +int gme_err_code( gme_err_t err ) +{ + int code = err_code( err ); + return (code >= 0 ? code : gme_err_generic); +} + +gme_err_t gme_code_to_err( int code ) +{ + return blargg_code_to_err( code, gme_codes ); +} + +const char* gme_err_details( gme_err_t err ) +{ + // If we don't have error code assigned, return entire string + return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); +} + +const char* gme_err_str( gme_err_t err ) +{ + return blargg_err_str( err ); +} diff --git a/Frameworks/GME/gme/gme.h b/Frameworks/GME/gme/gme.h old mode 100755 new mode 100644 index 469c901cd..41c363a8f --- a/Frameworks/GME/gme/gme.h +++ b/Frameworks/GME/gme/gme.h @@ -1,222 +1,298 @@ -/* Game music emulator library C interface (also usable from C++) */ - -/* Game_Music_Emu 0.5.2 */ -#ifndef GME_H -#define GME_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* 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, long 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*, long 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*, long start_msec ); - -/* 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 */ -long gme_tell( 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*, long msec ); - - -/******** 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* ); - -/* Get information for a particular track (length, name, author, etc.) */ -typedef struct track_info_t track_info_t; -gme_err_t gme_track_info( Music_Emu const*, track_info_t* out, int track ); - -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 }; - - -/******** 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* ); - -/* Names of voices */ -const char** gme_voice_names( Music_Emu const* ); - -/* 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) */ -typedef struct gme_equalizer_t -{ - double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ - long bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ -} gme_equalizer_t; - -/* Get current frequency equalizater parameters */ -gme_equalizer_t gme_equalizer( Music_Emu const* ); - -/* Change frequency equalizer parameters */ -void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); - - - -/******** Game music types ********/ - -/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is -"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ -typedef struct gme_type_t_ const* gme_type_t; -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_; -}; - -/* Emulator type constants for each supported file type */ -extern struct gme_type_t_ const 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(); - - -/******** Advanced file loading ********/ - -/* Error returned if file type is not supported */ -extern const char gme_wrong_file_type []; - -/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long 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 ); - -/* 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, long 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, long 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 +/* Loads and plays video game music files into sample buffer */ + +/* Game_Music_Emu $vers */ +#ifndef GME_H +#define GME_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Pointer to error, or NULL if function was successful. See Errors below. */ +#ifndef gme_err_t /* (#ifndef allows better testing of library) */ + typedef const char* gme_err_t; +#endif + +/* First parameter of most functions is gme_t*, or const gme_t* if nothing is +changed. */ +typedef struct gme_t gme_t; + +/* Boolean; false = 0, true = 1 */ +typedef int gme_bool; + + +/******** Basic operations ********/ + +/* Opens game music file and points *out at it. If error, sets *out to NULL. */ +gme_err_t gme_open_file( const char path [], gme_t** out, int sample_rate ); + +/* Number of tracks */ +int gme_track_count( const gme_t* ); + +/* Starts a track, where 0 is the first track. Requires that 0 <= index < gme_track_count(). */ +gme_err_t gme_start_track( gme_t*, int index ); + +/* Generates 'count' 16-bit signed samples info 'out'. Output is in stereo, so count +must be even. */ +gme_err_t gme_play( gme_t*, int count, short out [] ); + +/* Closes file and frees memory. OK to pass NULL. */ +void gme_delete( gme_t* ); + + +/******** Track position/length ********/ + +/* Sets 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( gme_t*, int start_msec ); + +/* True if a track has reached its end */ +gme_bool gme_track_ended( const gme_t* ); + +/* Number of milliseconds played since beginning of track (1000 = one second) */ +int gme_tell( const gme_t* ); + +/* Seeks to new time in track. Seeking backwards or far forward can take a while. */ +gme_err_t gme_seek( gme_t*, int msec ); + + +/******** Informational ********/ + +/* Use in place of sample rate for open/load if you only need to get track +information from a music file */ +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( gme_t* ); + +/* Loads m3u playlist file (must be done after loading music) */ +gme_err_t gme_load_m3u( gme_t*, const char path [] ); + +/* Clears any loaded m3u playlist and any internal playlist that the music format +supports (NSFE for example). */ +void gme_clear_playlist( gme_t* ); + +/* Passes back pointer to 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( const gme_t*, 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 ********/ + +/* Disables automatic end-of-track detection and skipping of silence at beginning +if ignore is true */ +void gme_ignore_silence( gme_t*, gme_bool ignore ); + +/* Adjusts song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed, etc. +Track length as returned by track_info() ignores tempo (assumes it's 1.0). */ +void gme_set_tempo( gme_t*, double tempo ); + +/* Number of voices used by currently loaded file */ +int gme_voice_count( const gme_t* ); + +/* Name of voice i, from 0 to gme_voice_count() - 1 */ +const char* gme_voice_name( const gme_t*, int i ); + +/* Mutes/unmutes single voice i, where voice 0 is first voice */ +void gme_mute_voice( gme_t*, int index, gme_bool mute ); + +/* Sets 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( gme_t*, int muting_mask ); + +/* Frequency equalizer parameters (see gme.txt) */ +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; + +/* Gets current frequency equalizer parameters */ +void gme_equalizer( const gme_t*, gme_equalizer_t* out ); + +/* Changes frequency equalizer parameters */ +void gme_set_equalizer( gme_t*, gme_equalizer_t const* eq ); + + + +/******** Effects processor ********/ + +/* Adds stereo surround and echo to music that's usually mono or has little +stereo. Has no effect on GYM, SPC, and Sega Genesis VGM music. */ + +/* Simplified control using a single value, where 0.0 = off and 1.0 = maximum */ +void gme_set_stereo_depth( gme_t*, double depth ); + +struct gme_effects_t +{ + double echo; /* Amount of echo, where 0.0 = none, 1.0 = lots */ + double stereo; /* Separation, where 0.0 = mono, 1.0 = hard left and right */ + + double d2,d3,d4,d5,d6,d7; /* reserved */ + + gme_bool enabled; /* If 0, no effects are added */ + gme_bool surround;/* If 1, some channels are put in "back", using phase inversion */ + + int i1,i3,i4,i5,i6,i7; /* reserved */ +}; +typedef struct gme_effects_t gme_effects_t; + +/* Sets effects configuration, or disables effects if NULL */ +void gme_set_effects( gme_t*, gme_effects_t const* ); + +/* Passes back current effects configuration */ +void gme_effects( const gme_t*, gme_effects_t* out ); + + +/******** Game music types ********/ + +/* Music file type identifier. Can also hold NULL. */ +typedef const struct gme_type_t_* gme_type_t; + +/* Type of this emulator */ +gme_type_t gme_type( const gme_t* ); + +/* 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 */ +gme_bool gme_type_multitrack( gme_type_t ); + + +/******** Advanced file loading ********/ + +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ +gme_err_t gme_open_data( void const* data, long size, gme_t** emu_out, int sample_rate ); + +/* Determines likely game music type based on first four bytes of file. Returns +string containing proper file suffix ("NSF", "SPC", etc.) or "" if file header +is not recognized. */ +const char* gme_identify_header( void const* header ); + +/* Gets corresponding music type for file path or extension passed in. */ +gme_type_t gme_identify_extension( const char path_or_extension [] ); + +/* Determines 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 ); + +/* Creates new emulator and sets sample rate. Returns NULL if out of memory. If you only need +track information, pass gme_info_only for sample_rate. */ +gme_t* gme_new_emu( gme_type_t, int sample_rate ); + +/* Loads music file into emulator */ +gme_err_t gme_load_file( gme_t*, const char path [] ); + +/* Loads music file from memory into emulator. Makes a copy of data passed. */ +gme_err_t gme_load_data( gme_t*, void const* data, long size ); + +/* Loads 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, long count ); +gme_err_t gme_load_custom( gme_t*, gme_reader_t, long file_size, void* your_data ); + +/* Loads m3u playlist file from memory (must be done after loading music) */ +gme_err_t gme_load_m3u_data( gme_t*, void const* data, long size ); + + +/******** User data ********/ + +/* Sets/gets pointer to data you want to associate with this emulator. +You can use this for whatever you want. */ +void gme_set_user_data( gme_t*, void* new_user_data ); +void* gme_user_data( const gme_t* ); + +/* Registers cleanup function to be called when deleting emulator, or NULL to +clear it. Passes user_data when calling cleanup function. */ +typedef void (*gme_user_cleanup_t)( void* user_data ); +void gme_set_user_cleanup( gme_t*, gme_user_cleanup_t func ); + + +/******** Errors ********/ + +/* Internally, a gme_err_t is a const char* that points to a normal C string. +This means that other strings can be passed to the following functions. In the +descriptions below, these other strings are referred to as being not gme_err_t +strings. */ + +/* Error string associated with err. Returns "" if err is NULL. Returns err +unchanged if it isn't a gme_err_t string. */ +const char* gme_err_str( gme_err_t err ); + +/* Details of error beyond main cause, or "" if none or err is NULL. Returns +err unchanged if it isn't a gme_err_t string. */ +const char* gme_err_details( gme_err_t err ); + +/* Numeric code corresponding to err. Returns gme_ok if err is NULL. Returns +gme_err_generic if err isn't a gme_err_t string. */ +int gme_err_code( gme_err_t err ); + +enum { + gme_ok = 0,/* Successful call. Guaranteed to be zero. */ + gme_err_generic = 0x01,/* Error of unspecified type */ + gme_err_memory = 0x02,/* Out of memory */ + gme_err_caller = 0x03,/* Caller violated requirements of function */ + gme_err_internal = 0x04,/* Internal problem, corruption, etc. */ + gme_err_limitation = 0x05,/* Exceeded program limit */ + + gme_err_file_missing = 0x20,/* File not found at specified path */ + gme_err_file_read = 0x21,/* Couldn't open file for reading */ + gme_err_file_io = 0x23,/* Read/write error */ + gme_err_file_eof = 0x25,/* Tried to read past end of file */ + + gme_err_file_type = 0x30,/* File is of wrong type */ + gme_err_file_feature = 0x32,/* File requires unsupported feature */ + gme_err_file_corrupt = 0x33 /* File is corrupt */ +}; + +/* gme_err_t corresponding to numeric code. Note that this might not recover +the original gme_err_t before it was converted to a numeric code; in +particular, gme_err_details(gme_code_to_err(code)) will be "" in most cases. */ +gme_err_t gme_code_to_err( int code ); + + + +/* Deprecated */ +typedef gme_t Music_Emu; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Frameworks/MAC/MAC.xcodeproj/project.pbxproj b/Frameworks/MAC/MAC.xcodeproj/project.pbxproj index 435dbac54..1e1fa6332 100644 --- a/Frameworks/MAC/MAC.xcodeproj/project.pbxproj +++ b/Frameworks/MAC/MAC.xcodeproj/project.pbxproj @@ -414,9 +414,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "MAC" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MAC */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -511,6 +517,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PER_ARCH_CFLAGS_ppc = "-maltivec"; PRODUCT_NAME = MAC; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "mac-src"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -537,6 +544,7 @@ OTHER_LDFLAGS = "-Wl,-read_only_relocs,warning"; PER_ARCH_CFLAGS_ppc = "-maltivec"; PRODUCT_NAME = MAC; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "mac-src"; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp index 74bd51b39..40caa5014 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp @@ -23,7 +23,7 @@ CAPEDecompress::CAPEDecompress(int * pErrorCode, CAPEInfo * pAPEInfo, int nStart } // get format information - GetInfo(APE_INFO_WAVEFORMATEX, (int) &m_wfeInput); + GetInfo(APE_INFO_WAVEFORMATEX, (long) &m_wfeInput); m_nBlockAlign = GetInfo(APE_INFO_BLOCK_ALIGN); // initialize other stuff @@ -367,9 +367,9 @@ int CAPEDecompress::SeekToFrame(int nFrameIndex) /***************************************************************************************** Get information from the decompressor *****************************************************************************************/ -int CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { - int nRetVal = 0; + long nRetVal = 0; BOOL bHandled = TRUE; switch (Field) @@ -452,7 +452,7 @@ int CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, (m_nFinishBlock - m_nStartBlock) * GetInfo(APE_INFO_BLOCK_ALIGN), &wfeFormat, 0); diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h index ec49c071f..18ffb881c 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h +++ b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h @@ -22,7 +22,7 @@ public: int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved); int Seek(int nBlockOffset); - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); protected: diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp index d904c9d13..30baa0e3d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp @@ -129,9 +129,9 @@ int CAPEInfo::GetFileInformation(BOOL bGetTagInformation) /***************************************************************************************** Primary query function *****************************************************************************************/ -int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { - int nRetVal = -1; + long nRetVal = -1; switch (Field) { @@ -254,7 +254,7 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, m_APEFileInfo.nWAVDataBytes, &wfeFormat, m_APEFileInfo.nWAVTerminatingBytes); memcpy(pBuffer, &WAVHeader, sizeof(WAVE_HEADER)); @@ -311,7 +311,7 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) break; } case APE_INFO_IO_SOURCE: - nRetVal = (int) m_spIO.GetPtr(); + nRetVal = (long) m_spIO.GetPtr(); break; case APE_INFO_FRAME_BYTES: { @@ -350,10 +350,10 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) break; } case APE_INFO_TAG: - nRetVal = (int) m_spAPETag.GetPtr(); + nRetVal = (long) m_spAPETag.GetPtr(); break; case APE_INTERNAL_INFO: - nRetVal = (int) &m_APEFileInfo; + nRetVal = (long) &m_APEFileInfo; break; default: break; diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h index 49e5c2fb0..5e113f1ba 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h +++ b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h @@ -82,7 +82,7 @@ public: virtual ~CAPEInfo(); // query for information - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); private: diff --git a/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp b/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp index a757218d8..b4587bf4a 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp @@ -291,14 +291,14 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal); // get the input format - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeInput)) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeInput)) // allocate space for the header spTempBuffer.Assign(new unsigned char [spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); // get the header - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES))); + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_DATA, (long) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES))); // initialize the output if (nOutputMode == UNMAC_DECODER_OUTPUT_WAV) @@ -370,7 +370,7 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi { spTempBuffer.Assign(new unsigned char[spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES))) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (long) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES))) unsigned int nBytesToWrite = spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES); unsigned int nBytesWritten = 0; @@ -392,7 +392,7 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi spTempBuffer.Assign(new unsigned char[nTerminatingBytes], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), nTerminatingBytes)) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (long) spTempBuffer.GetPtr(), nTerminatingBytes)) if (bHasTag) { diff --git a/Frameworks/MAC/mac-src/src/MACLib/MACLib.h b/Frameworks/MAC/mac-src/src/MACLib/MACLib.h index c2f25b13e..b548d624b 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/MACLib.h +++ b/Frameworks/MAC/mac-src/src/MACLib/MACLib.h @@ -259,7 +259,7 @@ public: // int nParam2 // generic parameter... usage is listed in APE_DECOMPRESS_FIELDS ////////////////////////////////////////////////////////////////////////////////////////////// - virtual int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0) = 0; + virtual long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0) = 0; }; /************************************************************************************************* diff --git a/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp b/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp index 3efa090c6..641c7e387 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp @@ -100,7 +100,7 @@ __MD5Transform ( uint32_t state [4], CopyToLittleEndian (tempBuffer, in, 16); x = tempBuffer; #else - if ( (unsigned int)in & 3 ) { + if ( (unsigned long)in & 3 ) { memcpy ( tempBuffer, in, 64 ); x = tempBuffer; } diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp index 1d69defd8..9c1ebea6d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp @@ -165,7 +165,7 @@ int CAPEDecompressOld::Seek(int nBlockOffset) return ERROR_SUCCESS; } -int CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { int nRetVal = 0; BOOL bHandled = TRUE; @@ -250,7 +250,7 @@ int CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nPa } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, (m_nFinishBlock - m_nStartBlock) * GetInfo(APE_INFO_BLOCK_ALIGN), &wfeFormat, 0); diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h index c3b3b61a6..08126d5f5 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h @@ -13,7 +13,7 @@ public: int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved); int Seek(int nBlockOffset); - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); protected: diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp b/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp index 43d373262..bc2b3a96d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp @@ -78,7 +78,7 @@ int CUnMAC::Initialize(IAPEDecompress *pAPEDecompress) // set the initialized flag to TRUE m_bInitialized = TRUE; - m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &m_wfeInput); + m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &m_wfeInput); // return a successful value return ERROR_SUCCESS; @@ -197,7 +197,7 @@ int CUnMAC::DecompressFrameOld(unsigned char *pOutputData, int32 FrameIndex, int { m_pAPEDecompressCore->GenerateDecodedArrays(nBlocks, nSpecialCodes, FrameIndex, CPULoadBalancingFactor); - WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &WaveFormatEx); + WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &WaveFormatEx); m_pPrepare->UnprepareOld(m_pAPEDecompressCore->GetDataX(), m_pAPEDecompressCore->GetDataY(), nBlocks, &WaveFormatEx, pOutputData, (unsigned int *) &CRC, (int *) &nSpecialCodes, m_pAPEDecompress->GetInfo(APE_INFO_FILE_VERSION)); } @@ -205,7 +205,7 @@ int CUnMAC::DecompressFrameOld(unsigned char *pOutputData, int32 FrameIndex, int { m_pAPEDecompressCore->GenerateDecodedArrays(nBlocks, nSpecialCodes, FrameIndex, CPULoadBalancingFactor); - WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &WaveFormatEx); + WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &WaveFormatEx); m_pPrepare->UnprepareOld(m_pAPEDecompressCore->GetDataX(), NULL, nBlocks, &WaveFormatEx, pOutputData, (unsigned int *) &CRC, (int *) &nSpecialCodes, m_pAPEDecompress->GetInfo(APE_INFO_FILE_VERSION)); } diff --git a/Frameworks/MAD/MAD.xcodeproj/project.pbxproj b/Frameworks/MAD/MAD.xcodeproj/project.pbxproj index bab827734..9e2e4d94f 100644 --- a/Frameworks/MAD/MAD.xcodeproj/project.pbxproj +++ b/Frameworks/MAD/MAD.xcodeproj/project.pbxproj @@ -196,9 +196,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "MAD" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MAD */; productRefGroup = 034768DDFF38A45A11DB9C8B /* Products */; projectDirPath = ""; @@ -276,6 +282,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = FPM_DEFAULT; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_STYLE = DYNAMIC; @@ -285,6 +292,7 @@ PER_ARCH_CFLAGS_ppc = "-DFPM_PPC"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -304,6 +312,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = FPM_INTEL; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_STYLE = DYNAMIC; @@ -313,6 +322,7 @@ PER_ARCH_CFLAGS_ppc = "-DFPM_PPC"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj index 54c39eb68..6bbb7c535 100644 --- a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj +++ b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj @@ -213,9 +213,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "MPCDec" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MPCDec */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -284,6 +290,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = mpcdec; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -306,6 +313,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = mpcdec; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = Files/include/; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj b/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj index d2766a7f6..5f6f03a2e 100644 --- a/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj +++ b/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj @@ -154,9 +154,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "ogg" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* ogg */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -219,6 +225,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = ogg; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "libogg-src/include"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -243,6 +250,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = ogg; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "libogg-src/include"; WRAPPER_EXTENSION = framework; }; @@ -254,7 +262,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -265,7 +273,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Frameworks/Shorten/Files/shorten/include/shn_reader.h b/Frameworks/Shorten/Files/shorten/include/shn_reader.h index ce52b3416..2f122e9a4 100644 --- a/Frameworks/Shorten/Files/shorten/include/shn_reader.h +++ b/Frameworks/Shorten/Files/shorten/include/shn_reader.h @@ -176,7 +176,7 @@ class shn_reader void *pmalloc(ulong size); slong **long2d(ulong n0, ulong n1); - friend void *thread_runner(shn_reader *reader) + static void *thread_runner(shn_reader *reader) { reader->Run(); return NULL; diff --git a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj index 6b10654d7..4183aa76f 100644 --- a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj @@ -171,9 +171,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Shorten" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Shorten */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -237,10 +243,11 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; - GCC_VERSION = 4.0; + GCC_VERSION = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -261,11 +268,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; - GCC_VERSION = 4.0; + GCC_VERSION = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; OTHER_CFLAGS = "-DHAVE_CONFIG_H"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = "$(CACHE_ROOT)/SharedPrecompiledHeaders"; USER_HEADER_SEARCH_PATHS = Files/shorten/include; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj index ce6794de3..9ed069ab0 100644 --- a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj @@ -924,9 +924,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "TagLib" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* TagLib */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -1085,6 +1091,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -1111,6 +1118,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj b/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj index fdec2a98d..53c47eb36 100644 --- a/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj +++ b/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj @@ -351,9 +351,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Vorbis" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Vorbis */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -489,6 +495,7 @@ Ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "Files/lib/ Files/include/"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -525,6 +532,7 @@ Ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/WMA/WMA.xcodeproj/project.pbxproj b/Frameworks/WMA/WMA.xcodeproj/project.pbxproj index ec578fd96..7f69ff31d 100644 --- a/Frameworks/WMA/WMA.xcodeproj/project.pbxproj +++ b/Frameworks/WMA/WMA.xcodeproj/project.pbxproj @@ -234,9 +234,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WMA" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* WMA */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -301,7 +307,7 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(NATIVE_ARCH_32_BIT)"; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -315,6 +321,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -324,6 +331,10 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -334,6 +345,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj index 323d8a400..0667da571 100644 --- a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj @@ -190,9 +190,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WavPack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* WavPack */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -279,6 +285,7 @@ ); OTHER_CFLAGS_QUOTED_1 = "-DPACKAGE_STRING=\\\"wavpack\\ 4.2\\\""; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -316,6 +323,7 @@ ); OTHER_CFLAGS_QUOTED_1 = "-DPACKAGE_STRING=\\\"wavpack\\ 4.2\\\""; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Plugins/APL/APL.xcodeproj/project.pbxproj b/Plugins/APL/APL.xcodeproj/project.pbxproj index be0811ae4..cfe963f94 100644 --- a/Plugins/APL/APL.xcodeproj/project.pbxproj +++ b/Plugins/APL/APL.xcodeproj/project.pbxproj @@ -149,9 +149,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "APL" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CueSheet */; projectDirPath = ""; projectRoot = ""; @@ -229,6 +235,7 @@ ); PREBINDING = NO; PRODUCT_NAME = APL; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -237,6 +244,10 @@ 99B989F90CC7E10500C256E9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); COPY_PHASE_STRIP = YES; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; @@ -253,6 +264,7 @@ ); PREBINDING = NO; PRODUCT_NAME = APL; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = NO; }; diff --git a/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj b/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj index a2273e9dd..f478e8849 100644 --- a/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj +++ b/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj @@ -188,9 +188,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "AudioOverload" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* AudioOverload */; projectDirPath = ""; projectReferences = ( @@ -263,6 +269,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; @@ -273,6 +280,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Debug; @@ -281,6 +289,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -288,6 +300,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; @@ -302,7 +315,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -315,7 +328,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj index e8430ed0d..b29ed45ff 100644 --- a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj +++ b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj @@ -143,9 +143,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "CoreAudio" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CoreAudio */; projectDirPath = ""; projectRoot = ""; @@ -190,6 +196,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CoreAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -209,6 +216,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CoreAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj index 911740f77..35f2e6c87 100644 --- a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj +++ b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj @@ -158,9 +158,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "CueSheet" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CueSheet */; projectDirPath = ""; projectRoot = ""; @@ -221,6 +227,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CueSheet; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -231,8 +238,8 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = ( - ppc, i386, + ppc, ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; @@ -241,6 +248,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CueSheet; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj b/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj index 756273985..b5069e8bd 100644 --- a/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj +++ b/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj @@ -192,9 +192,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Dumb" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Dumb */; projectDirPath = ""; projectReferences = ( @@ -273,10 +279,15 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Dumb_Prefix.pch; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -294,10 +305,15 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Dumb_Prefix.pch; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Dumb/DumbDecoder.h b/Plugins/Dumb/DumbDecoder.h index 8772120d4..8e4fbc4bb 100755 --- a/Plugins/Dumb/DumbDecoder.h +++ b/Plugins/Dumb/DumbDecoder.h @@ -10,6 +10,10 @@ #import +#define __FRAMEWORK__ +#import +#undef __FRAMEWORK__ + #import "Plugin.h" @interface DumbDecoder : NSObject { diff --git a/Plugins/Dumb/DumbDecoder.m b/Plugins/Dumb/DumbDecoder.m index 832942e24..d0f0c4392 100755 --- a/Plugins/Dumb/DumbDecoder.m +++ b/Plugins/Dumb/DumbDecoder.m @@ -49,6 +49,33 @@ void closeCallback(void *f) NSLog(@"CLOSE"); //I DO NOTHING } +int seekCallback(void *f, long n) +{ + DumbDecoder *decoder = (DumbDecoder *)f; + + if (![[decoder source] seekable]) return -1; + + if ([[decoder source] seek:n whence:SEEK_SET]) return 0; + else return -1; +} + +long getsizeCallback(void *f) +{ + DumbDecoder *decoder = (DumbDecoder *)f; + + if (![[decoder source] seekable]) return 0; + + long current_offset = [[decoder source] tell]; + + [[decoder source] seek:0 whence:SEEK_END]; + + long size = [[decoder source] tell]; + + [[decoder source] seek:current_offset whence:SEEK_SET]; + + return size; +} + - (BOOL)open:(id)s { [self setSource:s]; @@ -58,6 +85,8 @@ void closeCallback(void *f) dfs.getc = getCharCallback; dfs.getnc = readCallback; dfs.close = closeCallback; + dfs.seek = seekCallback; + dfs.get_size = getsizeCallback; // dumb_register_stdfiles(); @@ -69,18 +98,7 @@ void closeCallback(void *f) } NSString *ext = [[[[s url] path] pathExtension] lowercaseString]; - if ([ext isEqualToString:@"it"]) - duh = dumb_read_it(df); - else if ([ext isEqualToString:@"xm"]) - duh = dumb_read_xm(df); - else if ([ext isEqualToString:@"s3m"]) - duh = dumb_read_s3m(df); - else if ([ext isEqualToString:@"mod"]) - duh = dumb_read_mod(df); - else { - NSLog(@"DUH IS NUL!!!"); - duh = NULL; - } + duh = dumb_read_any(df, [ext isEqualToString:@"mod"] ? 0 : 1, 0); if (!duh) { NSLog(@"Failed to create duh"); @@ -97,7 +115,10 @@ void closeCallback(void *f) return NO; } - [self willChangeValueForKey:@"properties"]; + DUMB_IT_SIGRENDERER * itsr = duh_get_it_sigrenderer( dsr ); + dumb_it_set_ramp_style( itsr, 2 ); + + [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; return YES; @@ -187,7 +208,7 @@ void closeCallback(void *f) + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"it", @"xm", @"s3m", @"mod", nil]; + return [NSArray arrayWithObjects:@"it", @"xm", @"s3m", @"mod", @"stm", @"ptm", @"mtm", @"669", @"psm", @"am", @"dsm", @"amf", @"okt", @"okta", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/Dumb/DumbMetadataReader.m b/Plugins/Dumb/DumbMetadataReader.m index c03802d76..73121f089 100644 --- a/Plugins/Dumb/DumbMetadataReader.m +++ b/Plugins/Dumb/DumbMetadataReader.m @@ -32,17 +32,7 @@ DUH *duh; NSString *ext = [[[url path] pathExtension] lowercaseString]; - if ([ext isEqualToString:@"it"]) - duh = dumb_load_it_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"xm"]) - duh = dumb_load_xm_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"s3m"]) - duh = dumb_load_s3m_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"mod"]) - duh = dumb_load_mod_quick([[url path] UTF8String]); - else { - duh = NULL; - } + duh = dumb_load_any_quick([[url path] UTF8String], [ext isEqualToString:@"mod"] ? 0 : 1, 0); if (!duh) { diff --git a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj index 8d9975fdd..fa136cd4e 100644 --- a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj +++ b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj @@ -135,9 +135,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "FileSource" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* FileSource */; projectDirPath = ""; projectRoot = ""; @@ -182,6 +188,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = FileSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -201,6 +208,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = FileSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Flac/Flac.xcodeproj/project.pbxproj b/Plugins/Flac/Flac.xcodeproj/project.pbxproj index 6ab077163..9de2f93a3 100644 --- a/Plugins/Flac/Flac.xcodeproj/project.pbxproj +++ b/Plugins/Flac/Flac.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Flac" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Flac */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Flac; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -282,6 +289,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Flac; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/GME/GME.xcodeproj/project.pbxproj b/Plugins/GME/GME.xcodeproj/project.pbxproj index 300738900..a7618c0d3 100644 --- a/Plugins/GME/GME.xcodeproj/project.pbxproj +++ b/Plugins/GME/GME.xcodeproj/project.pbxproj @@ -201,9 +201,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "GME" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* GME */; projectDirPath = ""; projectReferences = ( @@ -286,6 +292,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -306,6 +313,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/GME/GameContainer.m b/Plugins/GME/GameContainer.m index 29b4a1e74..79d2d659e 100755 --- a/Plugins/GME/GameContainer.m +++ b/Plugins/GME/GameContainer.m @@ -16,7 +16,7 @@ + (NSArray *)fileTypes { //There doesn't seem to be a way to get this list. These are the only multitrack types. - return [NSArray arrayWithObjects:@"ay", @"gbs", @"nsf", @"nsfe", @"sap", nil]; + return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sgc", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/GME/GameDecoder.m b/Plugins/GME/GameDecoder.m index f5490766d..ca34414a3 100755 --- a/Plugins/GME/GameDecoder.m +++ b/Plugins/GME/GameDecoder.m @@ -67,7 +67,7 @@ gme_err_t readCallback( void* data, void* out, long count ) int track_num = [[[source url] fragment] intValue]; //What if theres no fragment? Assuming we get 0. - track_info_t info; + gme_info_t * info; error = gme_track_info( emu, &info, track_num ); if (error) { @@ -75,18 +75,20 @@ gme_err_t readCallback( void* data, void* out, long count ) } //As recommended - if (info.length > 0) { - NSLog(@"Using length: %li", info.length); - length = info.length; + if (info->length > 0) { + NSLog(@"Using length: %i", info->length); + length = info->length; } - else if (info.loop_length > 0) { - NSLog(@"Using loop length: %li", info.loop_length); - length = info.intro_length + 2*info.loop_length; + else if (info->loop_length > 0) { + NSLog(@"Using loop length: %i", info->loop_length); + length = info->intro_length + 2*info->loop_length; } else { length = 150000; NSLog(@"Setting default: %li", length); } + + gme_free_info( info ); NSLog(@"Length: %li", length); @@ -157,17 +159,7 @@ gme_err_t readCallback( void* data, void* out, long count ) + (NSArray *)fileTypes { - NSMutableArray *types = [NSMutableArray array]; - gme_type_t const* type = gme_type_list(); - while(*type) - { - //We're digging a little deep here, but there seems to be no other choice. - [types addObject:[NSString stringWithCString:(*type)->extension_ encoding: NSASCIIStringEncoding]]; - - type++; - } - - return [[types copy] autorelease]; + return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sgc", @"spc", @"vgm", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/GME/GameMetadataReader.m b/Plugins/GME/GameMetadataReader.m index 9bc82b79e..d86b6afb4 100644 --- a/Plugins/GME/GameMetadataReader.m +++ b/Plugins/GME/GameMetadataReader.m @@ -60,7 +60,7 @@ else track_num = [[url fragment] intValue]; - track_info_t info; + gme_info_t * info; error = gme_track_info( emu, &info, track_num ); if (error) { @@ -69,13 +69,17 @@ gme_delete(emu); - return [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: info.system], @"genre", - [NSString stringWithUTF8String: info.game], @"album", - [NSString stringWithUTF8String: info.song], @"title", - [NSString stringWithUTF8String: info.author], @"artist", + NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String: info->system], @"genre", + [NSString stringWithUTF8String: info->game], @"album", + [NSString stringWithUTF8String: info->song], @"title", + [NSString stringWithUTF8String: info->author], @"artist", [NSNumber numberWithInt:track_num+1], @"track", nil]; + + gme_free_info( info ); + + return dict; } @end diff --git a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj index 501a89331..165eabbad 100644 --- a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj +++ b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj @@ -166,9 +166,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "HTTPSource" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* HTTPSource */; projectDirPath = ""; projectRoot = ""; @@ -217,6 +223,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = HTTPSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -238,6 +245,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = HTTPSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/M3u/M3u.xcodeproj/project.pbxproj b/Plugins/M3u/M3u.xcodeproj/project.pbxproj index e701eb8c0..e3fd02ff6 100644 --- a/Plugins/M3u/M3u.xcodeproj/project.pbxproj +++ b/Plugins/M3u/M3u.xcodeproj/project.pbxproj @@ -138,9 +138,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "M3u" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* M3u */; projectDirPath = ""; projectRoot = ""; @@ -198,6 +204,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = M3u; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -219,6 +226,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = M3u; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = NO; diff --git a/Plugins/MAD/MAD.xcodeproj/project.pbxproj b/Plugins/MAD/MAD.xcodeproj/project.pbxproj index 8e1a59c55..11c4de22a 100644 --- a/Plugins/MAD/MAD.xcodeproj/project.pbxproj +++ b/Plugins/MAD/MAD.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "MAD" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* MAD */; projectDirPath = ""; projectReferences = ( @@ -259,10 +265,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = MAD_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = FPM_DEFAULT; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -292,6 +300,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj b/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj index 4fbc0518a..30da74d4c 100644 --- a/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj +++ b/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj @@ -189,9 +189,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "MonkeysAudio" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* MonkeysAudio */; projectDirPath = ""; projectReferences = ( @@ -266,6 +272,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = MonkeysAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -290,6 +297,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = MonkeysAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj index 0afefbfae..d7360a0e9 100644 --- a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj +++ b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Musepack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Musepack */; projectDirPath = ""; projectReferences = ( @@ -259,6 +265,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Musepack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -284,6 +291,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Musepack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Pls/Pls.xcodeproj/project.pbxproj b/Plugins/Pls/Pls.xcodeproj/project.pbxproj index 8490e3b84..cfc60b876 100644 --- a/Plugins/Pls/Pls.xcodeproj/project.pbxproj +++ b/Plugins/Pls/Pls.xcodeproj/project.pbxproj @@ -138,9 +138,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Pls" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Pls */; projectDirPath = ""; projectRoot = ""; @@ -197,6 +203,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Pls; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -217,6 +224,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Pls; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj index f4e7f32c2..48ab7443a 100644 --- a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Shorten" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Shorten */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -282,6 +289,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj index bf5db7f9b..b82050f7b 100644 --- a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj @@ -198,9 +198,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "TagLib" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* TagLib */; projectDirPath = ""; projectReferences = ( @@ -271,6 +277,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -291,6 +298,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj b/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj index c1f206248..1d6e98564 100644 --- a/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj +++ b/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Vorbis" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Vorbis */; projectDirPath = ""; projectReferences = ( @@ -265,6 +271,7 @@ ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -296,6 +303,7 @@ ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/WMA/WMA.xcodeproj/project.pbxproj b/Plugins/WMA/WMA.xcodeproj/project.pbxproj index 6b9abaa52..12032e21d 100644 --- a/Plugins/WMA/WMA.xcodeproj/project.pbxproj +++ b/Plugins/WMA/WMA.xcodeproj/project.pbxproj @@ -184,9 +184,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "WMA" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* WMA */; projectDirPath = ""; projectReferences = ( @@ -245,6 +251,7 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -265,6 +272,7 @@ "\"$(SRCROOT)/../../Frameworks/WMA/build/Debug/WMA.framework\"", ); PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -274,6 +282,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -286,6 +298,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; LIBRARY_SEARCH_PATHS = ""; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj index 40c4d9d1f..0f4c1a1f9 100644 --- a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj @@ -180,9 +180,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "WavPack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* WavPack */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -284,6 +291,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj index 6000f1326..d480153db 100644 --- a/Preferences/General/General.xcodeproj/project.pbxproj +++ b/Preferences/General/General.xcodeproj/project.pbxproj @@ -240,8 +240,11 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "General" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, @@ -371,8 +374,12 @@ buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -382,8 +389,12 @@ buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl b/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl index 9ac3653fa07afce022d909836279f7797b587e05..e35673015940aa06d2bad06fe5b6763dcfd23726 100755 GIT binary patch literal 1093888 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�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�KZUMmF)Hbe-@2&=7-h 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

r3Y=YAK;+T3JaCy1@lq=Ppc zT+H8{%x`pRlpl`?+$S>rBmsa~P2ux6=%%2_jj!N-ilM;@h2(jhxR<;y=?sxv8{$3UCAX4_3(Mzf|rkM2IZS-~jgrP)G28+FbvK$8##B33*mGiCEi^?)GuLs^_6w zwWaZ8Ex3lu6~n*iTdjN*dRLTBCq4#)rR~=1dlZhhxi8XR*~ht4Wz!*lI*fbm+sC;u zSYc!MyH8qY{rNir1Y)=BU{c%j_W*JW^0#P&$KNpqpIGH};@hDez94`5c7(6g z$M=sXT>jt(7QiQ=7IrdMvT`S`#Aa|wJfDiol(5fbSOjuzivbsMBGHi zJfdHAVv&&s-psJejyaf8mNyRRYrEI?&k%PA_kf#A{LK{^cypW?L(_lm-QBtDIEYA2 z8Hp2;ew@O-sjh-^p zhxgCNDM3-NRiX1{pL0~St$p@lcGek1YJAsY;bZP-+KP5{BeD=#YpmvCVaGie_9#k9 z6q{4%Y8)KmAao3i;5|9P!SM{fmem&}ld@@-hr;nja9WRpBR+&1a@yh#OKI1msW^y) zEp)QSk}&JpzO0PR?X&pX3}{uvX3h_6_ezBDF=treTNA5q2a%LFWJg9&UIt(UYvZfa{0Yhe@QYLrqyLkXK>vSez572(cG&*~ zhu8m6veW)ArM>?;j#e6LlebxKg@9q6b#kW{cxsyzzpjQSE|*j)A+j;Qz3Kabm?-u2f@r zDbw?fWmQ}JBu%gK{iWAf!F`f=GB5r>cimr$wb0DF5d&GoX^U?oS-PSFr)!d6y;`u! z7SK%)AA-fLQj5Fv(LATC`v7sez7;=Fj@FCcWz4Lcoagmbdcq!dcchqK2^|eh%gbqP z@%jD997X{!2-mAcY%df7wzskg%CMjm`Reb`{YuZER|A!BpI);iW<{-l>!n^1U9VL~ zp_zCul}=)|xSv?W?yB)`w+Yods6Q31ml8Un9vbhjaC@8Q zdNqi6IneY$8E1rBz?M4ddX+Hemn5r3R&$`0R~_^QMTft=m)8pDm)hT3Wqc0X-#Uu1 zfc+gUD6D69VRqJOJran0`+Mwt>uZ1ifRY{A-{X`G<9|JMFPY-G<--2gu2R~yzd!Cx z!WKG531QYgq?7jdApZWJ*x!TD3Z2;BUTma|vA>^#!m#~)PiYR>-{IOQoR7;Kpza

`XAD2@nNhVxODRm%u<^%`9d`p%Pb^i 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

MM>())@w%xu1wR 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 z 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

7MU>O9M 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 V68KjF|4QIr3H&R8|Ia1xe*n6ea4rA< literal 134672 zcmeFa4}6r>l{bC^VGSBIwo!wC3<@=BfS}P15M&Yp3^ibYph2<8Fd0a6l8nhj6Ak1E z88s@{P_d0I+MvMFUF?fp+!rfgQ0ii(yST;uVWr!!)pyy?;%{-6em30?n&0<(?|EjP zOePuo=iSfm{k+VF%Q^Sox#ymH?z!jQ=gD(__Qg*QDy2psd<6l&41{w<_%9vdB7~7D zg=t^#Kc-#yU68;930#oC1qob`zy%3hkiZ2AT#̾#oC1qob`zy%5XA0~k>KK-x1 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

`}jusPsR`b#!p$6 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 #ifdef __OBJC__ -# include "GrowlApplicationBridge.h" +# include #endif -#include "GrowlApplicationBridge-Carbon.h" diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h index 4341f3fbd..363975762 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h @@ -18,14 +18,11 @@ #import #import -#import "GrowlDefines.h" +#import //Forward declarations @protocol GrowlApplicationBridgeDelegate; -//Internal notification when the user chooses not to install (to avoid continuing to cache notifications awaiting installation) -#define GROWL_USER_CHOSE_NOT_TO_INSTALL_NOTIFICATION @"User chose not to install" - //------------------------------------------------------------------------------ #pragma mark - @@ -45,9 +42,9 @@ * @method isGrowlInstalled * @abstract Detects whether Growl is installed. * @discussion Determines if the Growl prefpane and its helper app are installed. - * @result Returns YES if Growl is installed, NO otherwise. + * @result this method will forever return YES. */ -+ (BOOL) isGrowlInstalled; ++ (BOOL) isGrowlInstalled __attribute__((deprecated)); /*! * @method isGrowlRunning @@ -57,6 +54,34 @@ */ + (BOOL) isGrowlRunning; + +/*! + * @method isMistEnabled + * @abstract Gives the caller a fairly good indication of whether or not built-in notifications(Mist) will be used. + * @discussion since this call makes use of isGrowlRunning it is entirely possible for this value to change between call and + * executing a notification dispatch + * @result Returns YES if Growl isn't reachable and the developer has not opted-out of + * Mist and the user hasn't set the global mist enable key to false. + */ ++ (BOOL)isMistEnabled; + +/*! + * @method setShouldUseBuiltInNotifications + * @abstract opt-out mechanism for the mist notification style in the event growl can't be reached. + * @discussion if growl is unavailable due to not being installed or as a result of being turned off then + * this option can enable/disable a built-in fire and forget display style + * @param should Specifies whether or not the developer wants to opt-in (default) or opt out + * of the built-in Mist style in the event Growl is unreachable. + */ ++ (void)setShouldUseBuiltInNotifications:(BOOL)should; + +/*! + * @method shouldUseBuiltInNotifications + * @abstract returns the current opt-in state of the framework's use of the Mist display style. + * @result Returns NO if the developer opt-ed out of Mist, the default value is YES. + */ ++ (BOOL)shouldUseBuiltInNotifications; + #pragma mark - /*! @@ -87,7 +112,7 @@ * * @param inDelegate The delegate for the GrowlApplicationBridge. It must conform to the GrowlApplicationBridgeDelegate protocol. */ -+ (void) setGrowlDelegate:(NSObject *)inDelegate; ++ (void) setGrowlDelegate:(id)inDelegate; /*! * @method growlDelegate @@ -95,7 +120,7 @@ * @discussion See setGrowlDelegate: for details. * @result The Growl delegate. */ -+ (NSObject *) growlDelegate; ++ (id) growlDelegate; #pragma mark - @@ -165,40 +190,6 @@ clickContext:(id)clickContext identifier:(NSString *)identifier; -/*! - * @method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:identifier: - * @abstract Send a Growl notification. - * @discussion This is the preferred means for sending a Growl notification. - * The notification name and at least one of the title and description are - * required (all three are preferred). All other parameters may be - * nil (or 0 or NO as appropriate) to accept default values. - * - * If using the Growl-WithInstaller framework, if Growl is not installed the - * user will be prompted to install Growl. If the user cancels, this method - * will have no effect until the next application session, at which time when - * it is called the user will be prompted again. The user is also given the - * option to not be prompted again. If the user does choose to install Growl, - * the requested notification will be displayed once Growl is installed and - * running. - * - * @param title The title of the notification displayed to the user. - * @param description The full description of the notification displayed to the user. - * @param notifName The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane. - * @param iconData NSData object to show with the notification as its icon. If nil, the application's icon will be used instead. - * @param priority The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority. - * @param isSticky If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications. - * @param clickContext A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of NSString, NSArray, NSNumber, NSDictionary, and NSData types). - * @param identifier An identifier for this notification. Notifications with equal identifiers are coalesced. - */ -+ (void) notifyWithTitle:(NSString *)title - description:(NSString *)description - notificationName:(NSString *)notifName - iconData:(NSData *)iconData - priority:(signed int)priority - isSticky:(BOOL)isSticky - clickContext:(id)clickContext - identifier:(NSString *)identifier; - /*! @method notifyWithDictionary: * @abstract Notifies using a userInfo dictionary suitable for passing to * NSDistributedNotificationCenter. @@ -269,6 +260,7 @@ * Growl when next it is ready; NO if not. */ + (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag; + /*! @method willRegisterWhenGrowlIsReady * @abstract Reports whether GrowlApplicationBridge will register with Growl * when Growl next launches. @@ -357,7 +349,7 @@ * Key Value * --- ----- * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON The icon of the application. + * GROWL_APP_ICON_DATA The data of the icon of the application. * GROWL_APP_LOCATION The location of the application. * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL * @@ -370,6 +362,7 @@ * copy of regDict. */ + (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict; + /*! @method registrationDictionaryByFillingInDictionary:restrictToKeys: * @abstract Tries to fill in missing keys in a registration dictionary. * @discussion This method examines the passed-in dictionary for missing keys, @@ -378,7 +371,7 @@ * Key Value * --- ----- * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON The icon of the application. + * GROWL_APP_ICON_DATA The data of the icon of the application. * GROWL_APP_LOCATION The location of the application. * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL * @@ -402,13 +395,39 @@ * the keys that it will look for are: * * \li GROWL_APP_NAME - * \li GROWL_APP_ICON + * \li GROWL_APP_ICON_DATA * * @since Growl.framework 1.1 */ + (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict; + (NSDictionary *) frameworkInfoDictionary; + +#pragma mark - + +/*! + *@method growlURLSchemeAvailable + *@abstract Lets the app know whether growl:// is registered on the system, used for certain methods below this + *@return Returns whether growl:// is registered on the system + *@discussion Methods such as openGrowlPreferences rely on the growl:// URL scheme to function + * Further, this method can provide a check on whether Growl is installed, + * however, the framework will not be relying on this method for choosing when/how to notify, + * and it is not recommended that the app rely on it for other than whether to use growl:// methods + *@since Growl.framework 1.4 + */ ++ (BOOL) isGrowlURLSchemeAvailable; + +/*! + * @method openGrowlPreferences: + * @abstract Open Growl preferences, optionally to this app's settings, growl:// method + * @param showApp Whether to show the application's settings, otherwise just opens to the last position + * @return Return's whether opening the URL was succesfull or not. + * @discussion Will launch if Growl is installed, but not running, and open the preferences window + * Uses growl:// URL scheme + * @since Growl.framework 1.4 + */ ++ (BOOL) openGrowlPreferences:(BOOL)showApp; + @end //------------------------------------------------------------------------------ @@ -417,27 +436,15 @@ /*! * @protocol GrowlApplicationBridgeDelegate * @abstract Required protocol for the Growl delegate. - * @discussion The methods in this protocol are required and are called + * @discussion The methods in this protocol are optional and are called * automatically as needed by GrowlApplicationBridge. See * +[GrowlApplicationBridge setGrowlDelegate:]. * See also GrowlApplicationBridgeDelegate_InformalProtocol. */ -@protocol GrowlApplicationBridgeDelegate +@protocol GrowlApplicationBridgeDelegate -// -registrationDictionaryForGrowl has moved to the informal protocol as of 0.7. - -@end - -//------------------------------------------------------------------------------ -#pragma mark - - -/*! - * @category NSObject(GrowlApplicationBridgeDelegate_InformalProtocol) - * @abstract Methods which may be optionally implemented by the GrowlDelegate. - * @discussion The methods in this informal protocol will only be called if implemented by the delegate. - */ -@interface NSObject (GrowlApplicationBridgeDelegate_InformalProtocol) +@optional /*! * @method registrationDictionaryForGrowl @@ -544,66 +551,17 @@ */ - (void) growlNotificationTimedOut:(id)clickContext; + +/*! + * @method hasNetworkClientEntitlement + * @abstract Used only in sandboxed situations since we don't know whether the app has com.apple.security.network.client entitlement + * @discussion GrowlDelegate calls to find out if we have the com.apple.security.network.client entitlement, + * since we can't find this out without hitting the sandbox. We only call it if we detect that the application is sandboxed. + */ +- (BOOL) hasNetworkClientEntitlement; + @end #pragma mark - -/*! - * @category NSObject(GrowlApplicationBridgeDelegate_Installation_InformalProtocol) - * @abstract Methods which may be optionally implemented by the Growl delegate when used with Growl-WithInstaller.framework. - * @discussion The methods in this informal protocol will only be called if - * implemented by the delegate. They allow greater control of the information - * presented to the user when installing or upgrading Growl from within your - * application when using Growl-WithInstaller.framework. - */ -@interface NSObject (GrowlApplicationBridgeDelegate_Installation_InformalProtocol) - -/*! - * @method growlInstallationWindowTitle - * @abstract Return the title of the installation window. - * @discussion If not implemented, Growl will use a default, localized title. - * @result An NSString object to use as the title. - */ -- (NSString *)growlInstallationWindowTitle; - -/*! - * @method growlUpdateWindowTitle - * @abstract Return the title of the upgrade window. - * @discussion If not implemented, Growl will use a default, localized title. - * @result An NSString object to use as the title. - */ -- (NSString *)growlUpdateWindowTitle; - -/*! - * @method growlInstallationInformation - * @abstract Return the information to display when installing. - * @discussion This information may be as long or short as desired (the window - * will be sized to fit it). It will be displayed to the user as an - * explanation of what Growl is and what it can do in your application. It - * should probably note that no download is required to install. - * - * If this is not implemented, Growl will use a default, localized explanation. - * @result An NSAttributedString object to display. - */ -- (NSAttributedString *)growlInstallationInformation; - -/*! - * @method growlUpdateInformation - * @abstract Return the information to display when upgrading. - * @discussion This information may be as long or short as desired (the window - * will be sized to fit it). It will be displayed to the user as an - * explanation that an updated version of Growl is included in your - * application and no download is required. - * - * If this is not implemented, Growl will use a default, localized explanation. - * @result An NSAttributedString object to display. - */ -- (NSAttributedString *)growlUpdateInformation; - -@end - -//private -@interface GrowlApplicationBridge (GrowlInstallationPrompt_private) -+ (void) _userChoseNotToInstallGrowl; -@end #endif /* __GrowlApplicationBridge_h__ */ diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h index 2b971cfe5..0a196f1e3 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h @@ -7,10 +7,8 @@ #ifdef __OBJC__ #define XSTR(x) (@x) -#define STRING_TYPE NSString * #else #define XSTR CFSTR -#define STRING_TYPE CFStringRef #endif /*! @header GrowlDefines.h @@ -56,7 +54,7 @@ * This key is optional. */ #define GROWL_APP_ID XSTR("ApplicationId") -/*! @defined GROWL_APP_ICON +/*! @defined GROWL_APP_ICON_DATA * @abstract The image data for your application's icon. * @discussion Image data representing your application's icon. This may be * superimposed on a notification icon as a badge, used as the notification @@ -66,7 +64,7 @@ * * Optional. Not supported by all display plugins. */ -#define GROWL_APP_ICON XSTR("ApplicationIcon") +#define GROWL_APP_ICON_DATA XSTR("ApplicationIcon") /*! @defined GROWL_NOTIFICATIONS_DEFAULT * @abstract The array of notifications to turn on by default. * @discussion These are the names of the notifications that should be enabled @@ -101,6 +99,14 @@ * This key is optional. */ #define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions") +/*! @defined GROWL_NOTIFICATIONS_ICONS + * @abstract A dictionary of icons for each notification + * @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are + * icons for each notification, for GNTP spec + * + * This key is optional. + */ +#define GROWL_NOTIFICATIONS_ICONS XSTR("NotificationIcons") /*! @defined GROWL_TICKET_VERSION * @abstract The version of your registration ticket. @@ -144,20 +150,20 @@ */ #define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription") /*! @defined GROWL_NOTIFICATION_ICON - * @discussion Image data for the notification icon. Must be in a format + * @discussion Image data for the notification icon. Image data must be in a format * supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. * * Optional. Not supported by all display plugins. */ -#define GROWL_NOTIFICATION_ICON XSTR("NotificationIcon") +#define GROWL_NOTIFICATION_ICON_DATA XSTR("NotificationIcon") /*! @defined GROWL_NOTIFICATION_APP_ICON * @discussion Image data for the application icon, in case GROWL_APP_ICON does - * not apply for some reason. Must be in a format supported by NSImage, such + * not apply for some reason. Image data be in a format supported by NSImage, such * as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. * * Optional. Not supported by all display plugins. */ -#define GROWL_NOTIFICATION_APP_ICON XSTR("NotificationAppIcon") +#define GROWL_NOTIFICATION_APP_ICON_DATA XSTR("NotificationAppIcon") /*! @defined GROWL_NOTIFICATION_PRIORITY * @discussion The priority of the notification as an integer number from * -2 to +2 (+2 being highest). @@ -185,16 +191,6 @@ */ #define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext") -/*! @defined GROWL_DISPLAY_PLUGIN - * @discussion The name of a display plugin which should be used for this notification. - * Optional. If this key is not set or the specified display plugin does not - * exist, the display plugin stored in the application ticket is used. This key - * allows applications to use different default display plugins for their - * notifications. The user can still override those settings in the preference - * pane. - */ -#define GROWL_DISPLAY_PLUGIN XSTR("NotificationDisplayPlugin") - /*! @defined GROWL_NOTIFICATION_IDENTIFIER * @abstract An identifier for the notification for coalescing purposes. * Notifications with the same identifier fall into the same class; only @@ -224,6 +220,19 @@ */ #define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress") +/*! @defined GROWL_NOTIFICATION_ALREADY_SHOWN + * @abstract If this key is set, it should contain a bool value wrapped + * in a NSNumber which describes whether the notification has + * already been displayed, for instance by built in Notification + * Center support. This value can be used to allow display + * plugins to skip a notification, while still allowing Growl + * actions to run on them. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_ALREADY_SHOWN XSTR("AlreadyShown") + + // Notifications #pragma mark Notifications @@ -245,7 +254,7 @@ * The userInfo dictionary for this notification can contain these keys: *

@@ -288,12 +297,6 @@ * Growl_PostNotification. */ #define GROWL_NOTIFICATION XSTR("GrowlNotification") -/*! @defined GROWL_SHUTDOWN -* @abstract The distributed notification name that tells Growl to shutdown. -* @discussion The Growl preference pane posts this notification when the -* "Stop Growl" button is clicked. -*/ -#define GROWL_SHUTDOWN XSTR("GrowlShutdown") /*! @defined GROWL_PING * @abstract A distributed notification to check whether Growl is running. * @discussion This is used by the Growl preference pane. If it receives a @@ -313,15 +316,48 @@ * registration dictionary supplied by its delegate. */ #define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!") -/*! @defined GROWL_NOTIFICATION_CLICKED - * @abstract The distributed notification sent when a supported notification is clicked. + + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX + * @abstract Part of the name of the distributed notification sent when a supported notification is clicked. * @discussion When a Growl notification with a click context is clicked on by - * the user, Growl posts this distributed notification. - * The GrowlApplicationBridge responds to this notification by calling a - * callback in its delegate. + * the user, Growl posts a distributed notification whose name is in the format: + * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX] + * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. */ -#define GROWL_NOTIFICATION_CLICKED XSTR("GrowlClicked!") -#define GROWL_NOTIFICATION_TIMED_OUT XSTR("GrowlTimedOut!") +#define GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX XSTR("GrowlClicked!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX + * @abstract Part of the name of the distributed notification sent when a supported notification times out without being clicked. + * @discussion When a Growl notification with a click context times out, Growl posts a distributed notification + * whose name is in the format: + * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX] + * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. + * NOTE: The user may have actually clicked the 'close' button; this triggers an *immediate* time-out of the notification. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX XSTR("GrowlTimedOut!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON + * @abstract The distributed notification sent when the Notification Center support is toggled on in Growl 2.0 + * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent + * to inform all running apps that they should now speak to Notification Center directly. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON XSTR("GrowlNotificationCenterOn!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF + * @abstract The distributed notification sent when the Notification Center support is toggled off in Growl 2.0 + * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent + * to inform all running apps that they should no longer speak to Notification Center directly. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF XSTR("GrowlNotificationCenterOff!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY + * @abstract The distributed notification sent by an application to query Growl 2.0's notification center support. + * @discussion When an app starts up, it will send this query to get Growl 2.0 to spit out whether notification + * center support is on or off. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY XSTR("GrowlNotificationCenterYN?") + /*! @group Other symbols */ /* Symbols which don't fit into any of the other categories. */ @@ -345,4 +381,6 @@ #define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition" +#define GROWL_PLUGIN_CONFIG_ID XSTR("GrowlPluginConfigurationID") + #endif //ndef _GROWLDEFINES_H diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist b/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist index ab7194d2d..6a90f41b9 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist @@ -2,6 +2,8 @@ + BuildMachineOSBuild + 12C60 CFBundleDevelopmentRegion English CFBundleExecutable @@ -13,11 +15,25 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.2 + 2.0.1 CFBundleSignature GRRR CFBundleVersion - 1.1.2 + 2.0.1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 4G2008a + DTPlatformVersion + GM + DTSDKBuild + 12C37 + DTSDKName + macosx10.8 + DTXcode + 0452 + DTXcodeBuild + 4G2008a NSPrincipalClass GrowlApplicationBridge