Repository: vgmrips/vgmplay Branch: master Commit: 8262c68570d6 Files: 229 Total size: 3.1 MB Directory structure: gitextract_fr0538in/ ├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── VGMPlay/ │ ├── .gitignore │ ├── ChipMapper.c │ ├── ChipMapper.h │ ├── Makefile │ ├── PortTalk_IOCTL.h │ ├── Stream.c │ ├── Stream.h │ ├── VGMFile.h │ ├── VGMPlay.c │ ├── VGMPlay.dsp │ ├── VGMPlay.dsw │ ├── VGMPlay.h │ ├── VGMPlay.ini │ ├── VGMPlay.sln │ ├── VGMPlay.txt │ ├── VGMPlay.vcxproj │ ├── VGMPlay.vcxproj.filters │ ├── VGMPlayUI.c │ ├── VGMPlay_AddFmts.c │ ├── VGMPlay_Intf.h │ ├── VGMPlay_Updates.txt │ ├── XMasFiles/ │ │ ├── SWJ-SQRC01_1C.h │ │ ├── SWJ-SQRC01_1C_trimmed_optimized.vgz │ │ ├── WEWISH.CMF │ │ ├── XMasBonus.h │ │ ├── clyde1_1.dro │ │ ├── lem_xmas_001_jb.dro │ │ ├── lemmings_012_tim7.vgm │ │ └── rudolph.dro │ ├── chips/ │ │ ├── 2151intf.c │ │ ├── 2151intf.h │ │ ├── 2203intf.c │ │ ├── 2203intf.h │ │ ├── 2413intf.c │ │ ├── 2413intf.h │ │ ├── 2608intf.c │ │ ├── 2608intf.h │ │ ├── 2610intf.c │ │ ├── 2610intf.h │ │ ├── 2612intf.c │ │ ├── 2612intf.h │ │ ├── 262intf.c │ │ ├── 262intf.h │ │ ├── 3526intf.c │ │ ├── 3526intf.h │ │ ├── 3812intf.c │ │ ├── 3812intf.h │ │ ├── 8950intf.c │ │ ├── 8950intf.h │ │ ├── ChipIncl.h │ │ ├── Ootake_PSG.c │ │ ├── Ootake_PSG.h │ │ ├── adlibemu.h │ │ ├── adlibemu_opl2.c │ │ ├── adlibemu_opl3.c │ │ ├── ay8910.c │ │ ├── ay8910.h │ │ ├── ay8910_opl.c │ │ ├── ay_intf.c │ │ ├── ay_intf.h │ │ ├── c140.c │ │ ├── c140.h │ │ ├── c352.c │ │ ├── c352.h │ │ ├── c6280.c │ │ ├── c6280.h │ │ ├── c6280intf.c │ │ ├── c6280intf.h │ │ ├── dac_control.c │ │ ├── dac_control.h │ │ ├── emu2149.c │ │ ├── emu2149.h │ │ ├── emu2413.c │ │ ├── emu2413.h │ │ ├── emu2413_NESpatches.txt │ │ ├── emutypes.h │ │ ├── es5503.c │ │ ├── es5503.h │ │ ├── es5506.c │ │ ├── es5506.h │ │ ├── fm.c │ │ ├── fm.h │ │ ├── fm2612.c │ │ ├── fmopl.c │ │ ├── fmopl.h │ │ ├── gb.c │ │ ├── gb.h │ │ ├── iremga20.c │ │ ├── iremga20.h │ │ ├── k051649.c │ │ ├── k051649.h │ │ ├── k053260.c │ │ ├── k053260.h │ │ ├── k054539.c │ │ ├── k054539.h │ │ ├── mamedef.h │ │ ├── multipcm.c │ │ ├── multipcm.h │ │ ├── nes_apu.c │ │ ├── nes_apu.h │ │ ├── nes_defs.h │ │ ├── nes_intf.c │ │ ├── nes_intf.h │ │ ├── np_nes_apu.c │ │ ├── np_nes_apu.h │ │ ├── np_nes_dmc.c │ │ ├── np_nes_dmc.h │ │ ├── np_nes_fds.c │ │ ├── np_nes_fds.h │ │ ├── okim6258.c │ │ ├── okim6258.h │ │ ├── okim6295.c │ │ ├── okim6295.h │ │ ├── opl.c │ │ ├── opl.h │ │ ├── opll.c │ │ ├── opll.h │ │ ├── opm.c │ │ ├── opm.h │ │ ├── panning.c │ │ ├── panning.h │ │ ├── pokey.c │ │ ├── pokey.h │ │ ├── pwm.c │ │ ├── pwm.h │ │ ├── qsound_ctr.c │ │ ├── qsound_ctr.h │ │ ├── qsound_intf.c │ │ ├── qsound_intf.h │ │ ├── qsound_mame.c │ │ ├── qsound_mame.h │ │ ├── rf5c68.c │ │ ├── rf5c68.h │ │ ├── saa1099.c │ │ ├── saa1099.h │ │ ├── scd_pcm.c │ │ ├── scd_pcm.h │ │ ├── scsp.c │ │ ├── scsp.h │ │ ├── scspdsp.c │ │ ├── scspdsp.h │ │ ├── scsplfo.c │ │ ├── segapcm.c │ │ ├── segapcm.h │ │ ├── sn76489.c │ │ ├── sn76489.h │ │ ├── sn76496.c │ │ ├── sn76496.h │ │ ├── sn76496_opl.c │ │ ├── sn764intf.c │ │ ├── sn764intf.h │ │ ├── upd7759.c │ │ ├── upd7759.h │ │ ├── vrc7tone.h │ │ ├── vsu.c │ │ ├── vsu.h │ │ ├── ws_audio.c │ │ ├── ws_audio.h │ │ ├── ws_initialIo.h │ │ ├── x1_010.c │ │ ├── x1_010.h │ │ ├── ym2151.c │ │ ├── ym2151.h │ │ ├── ym2413.c │ │ ├── ym2413.h │ │ ├── ym2413_opl.c │ │ ├── ym2413hd.c │ │ ├── ym2413hd.h │ │ ├── ym2612.c │ │ ├── ym2612.h │ │ ├── ym3438.c │ │ ├── ym3438.h │ │ ├── ymdeltat.c │ │ ├── ymdeltat.h │ │ ├── ymf262.c │ │ ├── ymf262.h │ │ ├── ymf271.c │ │ ├── ymf271.h │ │ ├── ymf278b.c │ │ ├── ymf278b.h │ │ ├── ymz280b.c │ │ └── ymz280b.h │ ├── dbus.c │ ├── dbus.h │ ├── dbus_stub.c │ ├── licenses/ │ │ ├── GPL.txt │ │ ├── List.txt │ │ └── mame_license.txt │ ├── mmkeys.h │ ├── mmkeys_Win.c │ ├── mmkeys_stub.c │ ├── pt_ioctl.c │ ├── stdbool.h │ ├── vgm-player │ ├── vgm2pcm.c │ ├── vgm2wav.c │ ├── vgm2wav.dsp │ ├── vgmplay.1 │ ├── vgmspec171.txt │ ├── xdg/ │ │ ├── vgmplay-mime.xml │ │ └── vgmplay.desktop.in │ └── zlib/ │ ├── zconf.h │ ├── zdll.lib │ ├── zlib.def │ ├── zlib.h │ ├── zlib.lib │ └── zlibd.lib └── in_vgm/ ├── README.md ├── Winamp/ │ ├── DSP.H │ ├── GEN.H │ ├── IN2.H │ ├── OUT.H │ ├── ipc_pe.h │ ├── wa_dlg.h │ └── wa_ipc.h ├── dlg_cfg.c ├── dlg_fileinfo.c ├── in_vgm.c ├── in_vgm.dsp ├── in_vgm.dsw ├── in_vgm.h ├── in_vgm.rc ├── ini_func.c ├── ini_func.h └── resource.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.dsp text eol=crlf *.dsw text eol=crlf *.sln text eol=crlf *.vcxproj* text eol=crlf ================================================ FILE: .gitignore ================================================ ################## ## Visual Studio 6 ################## # Build results [Dd]ebug/ [Rr]elease/ *.exe *.dll # Object files *.ilk *.obj *.pch *.pdb *.sbr *.tmp # Project cache files *.aps *.ncb *.opt *.sdf *.suo *.user Thumbs.db ehthumbs.db Desktop.ini ================================================ FILE: .travis.yml ================================================ language: c cache: ccache before_install: - cd VGMPlay - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libao; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ccache; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sed -i .bak 's/MACOSX = 0/MACOSX = 1/g' Makefile; fi os: - linux - osx compiler: - gcc addons: apt: packages: - make - gcc - zlib1g-dev - libao-dev - libdbus-1-dev script: - make ================================================ FILE: README.md ================================================ # VGMPlay [![Build Status](https://travis-ci.org/vgmrips/vgmplay.svg?branch=master)](https://travis-ci.org/vgmrips/vgmplay) The official and always up-to-date player for all [VGM](https://en.wikipedia.org/wiki/VGM_(file_format)) files. In the future, the existing VGMPlay will be replaced by [libvgm](https://github.com/ValleyBell/libvgm), which is currently in development. ## Contact * [VGMRips Forums](http://vgmrips.net/forum/index.php) * IRC: irc.digibase.ca #vgmrips ## Compile VGMPlay under Windows ### Using MS Visual Studio 6.0: 1. Open `VGMPlay.dsw`. 2. Build the project. 3. Done. ### Using later versions of MS Visual Studio: 1. Open `VGMPlay.vcxproj`. 2. Build the project. 3. Done. ### Using MinGW/MSYS: 1. open MSYS and run `make WINDOWS=1` in VGMPlay's folder. 2. Done. Note: You can compile it without MSYS, but you need to manually create the OBJDIRS paths (or make them use the backslash '\'), because mkdir fails at paths with a forward slash. ## Compile VGMPlay under Linux 1. [optional step] If you have libao installed, you can edit the Makefile to make VGMPlay use `libao` instead of `OSS`. 2. run `make` in VGMPlay's folder 3. Done. Optionally `sudo make install` and `sudo make play_install`. ### Building on Ubuntu (16.04) #### Requirements The following packages are needed in order to compile the binaries ```sh sudo apt-get install make gcc zlib1g-dev libao-dev libdbus-1-dev ``` #### Building ```sh make ``` ## Compile VGMPlay under macOS 1. install libao by executing the line `brew install libao` 2. run `make install MACOSX=1 DISABLE_HWOPL_SUPPORT=1` in VGMPlay's folder (Alternatively edit the Makefile to set those constants and just run `make`.) 3. Done. Thanks to grauw for macOS compilation instructions. ## Compile VGMPlay under Android 1. Install [Termux](https://github.com/termux/termux-app) on [F-Droid](https://f-droid.org/en/packages/com.termux/) or [GitHub](https://github.com/termux/termux-app/releases). Do not download Termux from Play Store for security and depreciation reasons 2. Open Termux and do `pkg update` 3. When you do pkg update, do `pkg install clang dbus git libao make pkg-config -y` 4. After the installation is done, do `git clone https://github.com/vgmrips/vgmplay` 5. After Done Cloning, do `cd vgmplay/VGMPlay` 6. And then do `make` ================================================ FILE: VGMPlay/.gitignore ================================================ /obj /vgm2pcm /vgmplay /vgm2wav vgmplay.desktop ================================================ FILE: VGMPlay/ChipMapper.c ================================================ // ChipMapper.c - Handles Chip Write (including OPL Hardware Support) #include #include #include #include "stdbool.h" //#define DISABLE_HW_SUPPORT // disable support for OPL hardware #ifdef __NetBSD__ // Thanks to nextvolume #warning "Disabling OPL Mapper functionality, current code does not work on NetBSD" #define DISABLE_HW_SUPPORT // Current code does not work on NetBSD #endif #ifdef WIN32 #include #include // for QueryPerformance### #else #ifndef DISABLE_HW_SUPPORT #include #ifdef __APPLE__ #include #else #include #endif #endif // DISABLE_HW_SUPPORT #include #endif #ifdef __APPLE__ #define ioperm(x,y,z) #define outb(x,y) #define inb(x) #endif #include "chips/mamedef.h" #include "chips/ChipIncl.h" #ifndef DISABLE_HW_SUPPORT unsigned char OpenPortTalk(void); void ClosePortTalk(void); #ifdef WIN32 void outportb(unsigned short PortAddress, unsigned char byte); unsigned char inportb(unsigned short PortAddress); #endif // WIN32 #endif // DISABLE_HW_SUPPORT #include "ChipMapper.h" #ifndef DISABLE_HW_SUPPORT INLINE UINT8 OPL_HW_GetStatus(void); INLINE void OPL_HW_WaitDelay(INT64 StartTime, float Delay); // SN76496 OPL Translaton void start_sn76496_opl(UINT8 ChipID, int clock, int stereo); void sn76496_write_opl(UINT8 ChipID, offs_t offset, UINT8 data); void sn76496_stereo_opl(UINT8 ChipID, offs_t offset, UINT8 data); void sn76496_refresh_t6w28_opl(UINT8 ChipID); // YM2413 OPL Translaton void start_ym2413_opl(UINT8 ChipID); void ym2413_w_opl(UINT8 ChipID, offs_t offset, UINT8 data); // Meka YM2413 OPL Translation int FM_OPL_Init (void *userdata); void FM_OPL_Close (void); void FM_OPL_Write (int Register, int Value); // AY8910 OPL Translation void ay8910_write_opl(UINT8 ChipID, UINT8 r, UINT8 v); void start_ay8910_opl(UINT8 ChipID, int clock, UINT8 chip_type); extern UINT8 OPL_MODE; extern UINT8 OPL_CHIPS; extern bool WINNT_MODE; extern UINT16 FMPort; extern UINT8 PlayingMode; extern bool FMBreakFade; extern bool FMOPL2Pan; extern float FinalVol; #ifdef WIN32 #define INP_9X _inp #define OUTP_9X _outp #define INP_NT inportb #define OUTP_NT outportb #endif // Delays in usec (Port Reads - or microsec) #define DELAY_OPL2_REG 3.3f #define DELAY_OPL2_DATA 23.0f #define DELAY_OPL3_REG 0.0f //#define DELAY_OPL3_DATA 0.28f // fine for ISA cards (like SoundBlaster 16) #define DELAY_OPL3_DATA 13.3f // required for PCI cards (CMI8738) #ifdef WIN32 INT64 HWusTime; #endif #define YM2413_EC_DEFAULT 0x00 #define YM2413_EC_MEKA 0x01 typedef struct chip_mapping_info { UINT8 ChipType; UINT8 ChipID; UINT8 RegBase; UINT8 ChnBase; UINT32 ChipOpt[0x10]; } CHIP_MAP; UINT8 ChipCount = 0x00; UINT8 SelChip; UINT8 ChipArr[0x100]; CHIP_MAP ChipMap[0x10]; bool RegChnUsage[0x20]; UINT8 OPLReg[0x200]; UINT8 OPLRegBak[0x200]; UINT8 OPLRegForce[0x200]; bool SkipMode = false; bool OpenedFM = false; #endif // DISABLE_HW_SUPPORT UINT8 YM2413_EMU_CORE; UINT32 OptArr[0x10]; #ifndef WIN32 unsigned char OpenPortTalk(void) { #ifndef DISABLE_HW_SUPPORT int retval; retval = ioperm(FMPort, 0x04, 1); return retval & 0xFF; #else return 0xFF; // not supported #endif } void ClosePortTalk(void) { #ifndef DISABLE_HW_SUPPORT ioperm(FMPort, 0x04, 0); #endif return; } #elif defined(DISABLE_HW_SUPPORT) unsigned char OpenPortTalk(void) { return 0xFF; // not supported } void ClosePortTalk(void) { return; } #endif // WIN32 void open_fm_option(UINT8 ChipType, UINT8 OptType, UINT32 OptVal) { OptArr[OptType & 0x0F] = OptVal; return; } void opl_chip_reset(void) { #ifndef DISABLE_HW_SUPPORT UINT16 Reg; float FnlVolBak; FnlVolBak = FinalVol; FinalVol = 1.0f; memset(OPLRegForce, 0x01, 0x200); OPL_HW_WriteReg(0x105, 0x01); // OPL3 Enable OPL_HW_WriteReg(0x001, 0x20); // Test Register OPL_HW_WriteReg(0x002, 0x00); // Timer 1 OPL_HW_WriteReg(0x003, 0x00); // Timer 2 OPL_HW_WriteReg(0x004, 0x00); // IRQ Mask Clear OPL_HW_WriteReg(0x104, 0x00); // 4-Op-Mode Disable OPL_HW_WriteReg(0x008, 0x00); // Keyboard Split // make sure all internal calulations finish sound generation for (Reg = 0x00; Reg < 0x09; Reg ++) { OPL_HW_WriteReg(0x0C0 | Reg, 0x00); // silence all notes (OPL3) OPL_HW_WriteReg(0x1C0 | Reg, 0x00); } for (Reg = 0x00; Reg < 0x16; Reg ++) { if ((Reg & 0x07) >= 0x06) continue; OPL_HW_WriteReg(0x040 | Reg, 0x3F); // silence all notes (OPL2) OPL_HW_WriteReg(0x140 | Reg, 0x3F); OPL_HW_WriteReg(0x080 | Reg, 0xFF); // set Sustain/Release Rate to FASTEST OPL_HW_WriteReg(0x180 | Reg, 0xFF); OPL_HW_WriteReg(0x060 | Reg, 0xFF); OPL_HW_WriteReg(0x160 | Reg, 0xFF); OPL_HW_WriteReg(0x020 | Reg, 0x00); // NULL the rest OPL_HW_WriteReg(0x120 | Reg, 0x00); OPL_HW_WriteReg(0x0E0 | Reg, 0x00); OPL_HW_WriteReg(0x1E0 | Reg, 0x00); } OPL_HW_WriteReg(0x0BD, 0x00); // Rhythm Mode for (Reg = 0x00; Reg < 0x09; Reg ++) { OPL_HW_WriteReg(0x0B0 | Reg, 0x00); // turn all notes off (-> Release Phase) OPL_HW_WriteReg(0x1B0 | Reg, 0x00); OPL_HW_WriteReg(0x0A0 | Reg, 0x00); OPL_HW_WriteReg(0x1A0 | Reg, 0x00); } // although this would be a more proper reset, it sometimes produces clicks /*for (Reg = 0x020; Reg <= 0x0FF; Reg ++) OPL_HW_WriteReg(Reg, 0x00); for (Reg = 0x120; Reg <= 0x1FF; Reg ++) OPL_HW_WriteReg(Reg, 0x00);*/ // Now do a proper reset of all other registers. for (Reg = 0x040; Reg < 0x0A0; Reg ++) { if ((Reg & 0x07) >= 0x06 || (Reg & 0x1F) >= 0x18) continue; OPL_HW_WriteReg(0x000 | Reg, 0x00); OPL_HW_WriteReg(0x100 | Reg, 0x00); } for (Reg = 0x00; Reg < 0x09; Reg ++) { OPL_HW_WriteReg(0x0C0 | Reg, 0x30); // must be 30 to make OPL2 VGMs sound on OPL3 OPL_HW_WriteReg(0x1C0 | Reg, 0x30); // if they don't send the C0 reg } memset(OPLRegForce, 0x01, 0x200); FinalVol = FnlVolBak; #endif // DISABLE_HW_SUPPORT return; } void open_real_fm(void) { #ifndef DISABLE_HW_SUPPORT UINT8 CurChip; CHIP_MAP* CurMap; UINT8 CurC2; bool OldSM; //SkipMode = false; OldSM = SkipMode; opl_chip_reset(); OpenedFM = true; for (CurChip = 0x00; CurChip < ChipCount; CurChip ++) { SelChip = CurChip; CurMap = ChipMap + SelChip; switch(CurMap->ChipType) { case 0x00: // SN76496 and T6W28 if (CurMap->ChipOpt[0x0F] & 0x80000000) { // Avoid Bugs CurMap->RegBase = (CurMap->ChipOpt[0x0F] >> 8) & 0xFF; CurMap->ChnBase = (CurMap->ChipOpt[0x0F] >> 0) & 0xFF; } start_sn76496_opl(CurMap->ChipID, CurMap->ChipOpt[0x00], CurMap->ChipOpt[0x05]); if (CurMap->ChipOpt[0x00] & 0x80000000) { // Set up T6W28 for (CurC2 = 0x00; CurC2 < CurChip; CurC2 ++) { if (ChipMap[CurC2].ChipType == CurMap->ChipType) { CurMap->ChipOpt[0x0F] = 0x80000000 | (CurMap->RegBase << 8) | (CurMap->ChnBase << 0); CurMap->RegBase = ChipMap[CurC2].RegBase; CurMap->ChnBase = ChipMap[CurC2].ChnBase; sn76496_refresh_t6w28_opl(ChipMap[CurC2].ChipID); break; } } } break; case 0x01: // YM2413 switch(YM2413_EMU_CORE) { case YM2413_EC_DEFAULT: start_ym2413_opl(CurMap->ChipID); break; case YM2413_EC_MEKA: FM_OPL_Init(NULL); break; } break; case 0x09: // YM3812 break; case 0x0A: // YM3526 break; case 0x0B: // Y8950 break; case 0x0C: // YMF262 break; case 0x0D: // YMF278B break; case 0x12: // AY8910 start_ay8910_opl(CurMap->ChipID, CurMap->ChipOpt[0x00], CurMap->ChipOpt[0x01]); break; } } #endif // DISABLE_HW_SUPPORT return; } void setup_real_fm(UINT8 ChipType, UINT8 ChipID) { #ifndef DISABLE_HW_SUPPORT CHIP_MAP* CurMap; UINT8 CurChip; UINT8 CurSet; UINT8 CurChn; bool ExitLoop; SelChip = ChipCount; ChipArr[(ChipType << 1) | (ChipID & 0x01)] = SelChip; CurMap = ChipMap + SelChip; CurMap->ChipType = ChipType; CurMap->ChipID = ChipID & 0x7F; switch(ChipType) { case 0x00: // SN76496 and T6W28 case 0x12: // AY8910 ExitLoop = false; for (CurSet = 0x00; CurSet < 0x02; CurSet ++) { for (CurChn = 0x00; CurChn < 0x09; CurChn ++) { if (! RegChnUsage[(CurSet << 4) | CurChn]) { ExitLoop = true; break; } } if (ExitLoop) break; } CurSet %= 0x02; CurChn %= 0x09; CurMap->RegBase = CurSet; CurMap->ChnBase = CurChn; memcpy(CurMap->ChipOpt, OptArr, 0x10 * sizeof(UINT32)); CurMap->ChipOpt[0x0F] = 0x00; if (ChipType == 0x00) { for (CurChn = 0x00; CurChn < 0x04; CurChn ++) { RegChnUsage[(CurMap->RegBase << 4) | (CurMap->ChnBase + CurChn)] = true; } } else { for (CurChn = 0x00; CurChn < 0x03; CurChn ++) { RegChnUsage[(CurMap->RegBase << 4) | (CurMap->ChnBase + CurChn)] = true; } } break; case 0x01: // YM2413 CurMap->RegBase = 0x00; CurMap->ChnBase = 0x00; for (CurChip = 0x00; CurChip < SelChip; CurChip ++) { if (ChipMap[CurChip].ChipType == 0x01) { CurMap->RegBase = 0x01; break; } ChipMap[CurChip].RegBase = 0x01; } for (CurChn = 0x00; CurChn < 0x09; CurChn ++) { RegChnUsage[(CurMap->RegBase << 4) | CurChn] = true; } break; case 0x09: // YM3812 for (CurSet = 0x00; CurSet < 0x02; CurSet ++) { if (! RegChnUsage[(CurSet << 4) | 0x00]) break; } CurSet %= 0x02; CurMap->RegBase = CurSet; CurMap->ChnBase = 0x00; break; case 0x0A: // YM3526 for (CurSet = 0x00; CurSet < 0x02; CurSet ++) { if (! RegChnUsage[(CurSet << 4) | 0x00]) break; } CurSet %= 0x02; CurMap->RegBase = CurSet; CurMap->ChnBase = 0x00; break; case 0x0B: // Y8950 for (CurSet = 0x00; CurSet < 0x02; CurSet ++) { if (! RegChnUsage[(CurSet << 4) | 0x00]) break; } CurSet %= 0x02; CurMap->RegBase = CurSet; CurMap->ChnBase = 0x00; break; case 0x0C: // YMF262 CurMap->RegBase = 0x00; CurMap->ChnBase = 0x00; break; case 0x0D: // YMF278B CurMap->RegBase = 0x00; CurMap->ChnBase = 0x00; break; } ChipCount ++; #endif // DISABLE_HW_SUPPORT return; } void close_real_fm(void) { #ifndef DISABLE_HW_SUPPORT UINT8 CurChip; UINT8 CurChn; UINT8 CurOp; UINT16 Reg; UINT8 RegOp; bool SoftFade; SoftFade = (FMBreakFade && FinalVol > 0.01f); for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { for (CurChn = 0x00; CurChn < 0x09; CurChn ++) { // Make sure that the ReleaseRate takes effect ... for (CurOp = 0x00; CurOp < 0x02; CurOp ++) { if (! CurOp && ! (OPLReg[(CurChip << 8) | (0xC0 | CurChn)] & 0x01)) continue; RegOp = (CurChn / 0x03) * 0x08 | (CurChn % 0x03) + (CurOp * 0x03); Reg = (CurChip << 8) | 0x80 | RegOp; if (SoftFade) { if ((OPLReg[Reg] & 0x0F) < 0x03) // Force a soft fading OPL_HW_WriteReg(Reg, (OPLReg[Reg] & 0xF0) | 0x04); } else { // stop sound immediately after Note-Off OPL_HW_WriteReg(Reg, (OPLReg[Reg] & 0xF0) | 0x0F); } } // ... and turn off all notes. if (SoftFade) { // soft way (turn off and let fade) Reg = (CurChip << 8) | (0xB0 | CurChn); OPL_HW_WriteReg(Reg, OPLReg[Reg] & ~0x20); } else { // hard way (turn off and set frequency to zero) Reg = (CurChip << 8) | (0xA0 | CurChn); OPL_HW_WriteReg(Reg | 0x00, 0x00); // A0 - Frequency LSB OPL_HW_WriteReg(Reg | 0x10, 0x00); // B0 - Frequency MSB / Block / Key On } } // Set Values that are compatible with Windows' FM Driver OPL_HW_WriteReg(0x104, 0x00); // 4-Op-Mode Disable OPL_HW_WriteReg(0x001, 0x00); // Wave Select Disable OPL_HW_WriteReg(0x002, 0x00); // Timer 1 OPL_HW_WriteReg(0x003, 0x00); // Timer 2 OPL_HW_WriteReg(0x004, 0x00); // IRQ mask clear OPL_HW_WriteReg(0x008, 0x00); // Keyboard Split OPL_HW_WriteReg(0x0BD, 0xC0); // Rhythm Mode } OpenedFM = false; ChipCount = 0x00; memset(RegChnUsage, 0x00, sizeof(bool) * 0x20); #endif // DISABLE_HW_SUPPORT return; } void chip_reg_write(UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data) { #ifndef DISABLE_HW_SUPPORT bool ModeFM; UINT8 CurChip; switch(PlayingMode) { case 0x00: ModeFM = false; break; case 0x01: ModeFM = true; break; case 0x02: ModeFM = false; for (CurChip = 0x00; CurChip < ChipCount; CurChip ++) { if (ChipMap[CurChip].ChipType == ChipType && ChipMap[CurChip].ChipID == ChipID) { ModeFM = true; break; } } break; } if (! ModeFM) { #endif // DISABLE_HW_SUPPORT switch(ChipType) { case 0x00: // SN76496 sn764xx_w(ChipID, Port, Data); break; case 0x01: // YM2413 ym2413_w(ChipID, 0x00, Offset); ym2413_w(ChipID, 0x01, Data); break; case 0x02: // YM2612 ym2612_w(ChipID, (Port << 1) | 0x00, Offset); ym2612_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x03: // YM2151 ym2151_w(ChipID, 0x00, Offset); ym2151_w(ChipID, 0x01, Data); break; case 0x04: // SegaPCM break; case 0x05: // RF5C68 rf5c68_w(ChipID, Offset, Data); break; case 0x06: // YM2203 ym2203_w(ChipID, 0x00, Offset); ym2203_w(ChipID, 0x01, Data); break; case 0x07: // YM2608 ym2608_w(ChipID, (Port << 1) | 0x00, Offset); ym2608_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x08: // YM2610/YM2610B ym2610_w(ChipID, (Port << 1) | 0x00, Offset); ym2610_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x09: // YM3812 ym3812_w(ChipID, 0x00, Offset); ym3812_w(ChipID, 0x01, Data); break; case 0x0A: // YM3526 ym3526_w(ChipID, 0x00, Offset); ym3526_w(ChipID, 0x01, Data); break; case 0x0B: // Y8950 y8950_w(ChipID, 0x00, Offset); y8950_w(ChipID, 0x01, Data); break; case 0x0C: // YMF262 ymf262_w(ChipID, (Port << 1) | 0x00, Offset); ymf262_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x0D: // YMF278B ymf278b_w(ChipID, (Port << 1) | 0x00, Offset); ymf278b_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x0E: // YMF271 ymf271_w(ChipID, (Port << 1) | 0x00, Offset); ymf271_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x0F: // YMZ280B ymz280b_w(ChipID, 0x00, Offset); ymz280b_w(ChipID, 0x01, Data); break; case 0x10: // RF5C164 rf5c164_w(ChipID, Offset, Data); break; case 0x11: // PWM pwm_chn_w(ChipID, Port, (Offset << 8) | (Data << 0)); break; case 0x12: // AY8910 ayxx_w(ChipID, 0x00, Offset); ayxx_w(ChipID, 0x01, Data); break; case 0x13: // GameBoy gb_sound_w(ChipID, Offset, Data); break; case 0x14: // NES APU nes_w(ChipID, Offset, Data); break; case 0x15: // MultiPCM multipcm_w(ChipID, Offset, Data); break; case 0x16: // UPD7759 upd7759_write(ChipID, Offset, Data); break; case 0x17: // OKIM6258 okim6258_write(ChipID, Offset, Data); break; case 0x18: // OKIM6295 okim6295_w(ChipID, Offset, Data); break; case 0x19: // K051649 / SCC1 k051649_w(ChipID, (Port << 1) | 0x00, Offset); k051649_w(ChipID, (Port << 1) | 0x01, Data); break; case 0x1A: // K054539 k054539_w(ChipID, (Port << 8) | (Offset << 0), Data); break; case 0x1B: // HuC6280 c6280_w(ChipID, Offset, Data); break; case 0x1C: // C140 c140_w(ChipID, (Port << 8) | (Offset << 0), Data); break; case 0x1D: // K053260 k053260_w(ChipID, Offset, Data); break; case 0x1E: // Pokey pokey_w(ChipID, Offset, Data); break; case 0x1F: // QSound qsound_w(ChipID, 0x00, Port); // Data MSB qsound_w(ChipID, 0x01, Offset); // Data LSB qsound_w(ChipID, 0x02, Data); // Register break; case 0x20: // YMF292/SCSP scsp_w(ChipID, (Port << 8) | (Offset << 0), Data); break; case 0x21: // WonderSwan ws_audio_port_write(ChipID, 0x80 | Offset, Data); break; case 0x22: // VSU VSU_Write(ChipID, (Port << 8) | (Offset << 0), Data); break; case 0x23: // SAA1099 saa1099_control_w(ChipID, 0, Offset); saa1099_data_w(ChipID, 0, Data); break; case 0x24: // ES5503 es5503_w(ChipID, Offset, Data); break; case 0x25: // ES5506 if (Port & 0x80) es550x_w16(ChipID, Port & 0x7F, (Offset << 8) | (Data << 0)); else es550x_w(ChipID, Port, Data); break; case 0x26: // X1-010 seta_sound_w(ChipID, (Port << 8) | (Offset << 0), Data); break; case 0x27: // C352 c352_w(ChipID, Port, (Offset << 8) | (Data << 0)); break; case 0x28: // GA20 irem_ga20_w(ChipID, Offset, Data); break; // case 0x##: // OKIM6376 // break; } #ifndef DISABLE_HW_SUPPORT } else { if (! OpenedFM) return; SelChip = ChipArr[(ChipType << 1) | (ChipID & 0x01)]; switch(ChipType) { case 0x00: // SN76496 switch(Port) { case 0x00: sn76496_write_opl(ChipID, 0x00, Data); break; case 0x01: sn76496_stereo_opl(ChipID, 0x00, Data); break; } break; case 0x01: // YM2413 switch(YM2413_EMU_CORE) { case YM2413_EC_DEFAULT: ym2413_w_opl(ChipID, 0x00, Offset); ym2413_w_opl(ChipID, 0x01, Data); break; case YM2413_EC_MEKA: FM_OPL_Write(Offset, Data); break; } break; case 0x09: // YM3812 if ((Offset & 0xF0) == 0xC0 && (! FMOPL2Pan || ! (Data & 0x30))) Data |= 0x30; else if ((Offset & 0xF0) == 0xE0) Data &= 0xF3; OPL_RegMapper((ChipID << 8) | Offset, Data); break; case 0x0A: // YM3526 if ((Offset & 0xF0) == 0xC0 && (! FMOPL2Pan || ! (Data & 0x30))) Data |= 0x30; else if ((Offset & 0xF0) == 0xE0) Data &= 0xF0; OPL_RegMapper((ChipID << 8) | Offset, Data); break; case 0x0B: // Y8950 if (Offset == 0x07 || (Offset >= 0x09 && Offset <= 0x17)) break; if ((Offset & 0xF0) == 0xC0 && (! FMOPL2Pan || ! (Data & 0x30))) Data |= 0x30; else if ((Offset & 0xF0) == 0xE0) Data &= 0xF0; OPL_RegMapper((ChipID << 8) | Offset, Data); break; case 0x0C: // YMF262 OPL_RegMapper((Port << 8) | Offset, Data); break; case 0x12: // AY8910 ay8910_write_opl(ChipID, Offset, Data); break; } } #endif // DISABLE_HW_SUPPORT return; } void OPL_Hardware_Detecton(void) { #ifndef DISABLE_HW_SUPPORT UINT8 Status1; UINT8 Status2; #ifdef WIN32 //LARGE_INTEGER TempQud1; //LARGE_INTEGER TempQud2; LARGE_INTEGER TempQudFreq; //__int64 TempQudMid; HWusTime = 0; if (! WINNT_MODE) Status1 = 0x00; else #endif Status1 = OpenPortTalk(); if (Status1) { OPL_MODE = 0x00; printf("Error opening FM Port! Permission denied!\n"); goto FinishDetection; } OPL_MODE = 0x02; // must be set to activate OPL2-Delays // OPL2 Detection OPL_HW_WriteReg(0x04, 0x60); OPL_HW_WriteReg(0x04, 0x80); Status1 = OPL_HW_GetStatus(); Status1 &= 0xE0; OPL_HW_WriteReg(0x02, 0xFF); OPL_HW_WriteReg(0x04, 0x21); OPL_HW_WaitDelay(0, 80); Status2 = OPL_HW_GetStatus(); Status2 &= 0xE0; OPL_HW_WriteReg(0x04, 0x60); OPL_HW_WriteReg(0x04, 0x80); if (! ((Status1 == 0x00) && (Status2 == 0xC0))) { // Detection failed OPL_MODE = 0x00; printf("No OPL Chip detected!\n"); goto FinishDetection; } // OPL3 Detection Status1 = OPL_HW_GetStatus(); Status1 &= 0x06; if (! Status1) { OPL_MODE = 0x03; OPL_CHIPS = 0x01; printf("OPL 3 Chip found.\n"); goto FinishDetection; } // OPL2 Dual Chip Detection OPL_HW_WriteReg(0x104, 0x60); OPL_HW_WriteReg(0x104, 0x80); Status1 = OPL_HW_GetStatus(); Status1 &= 0xE0; OPL_HW_WriteReg(0x102, 0xFF); OPL_HW_WriteReg(0x104, 0x21); OPL_HW_WaitDelay(0, 80); Status2 = OPL_HW_GetStatus(); Status2 &= 0xE0; OPL_HW_WriteReg(0x104, 0x60); OPL_HW_WriteReg(0x104, 0x80); if ((Status1 == 0x00) && (Status2 == 0xC0)) { OPL_CHIPS = 0x02; printf("Dual OPL 2 Chip found.\n"); } else { OPL_CHIPS = 0x01; printf("OPL 2 Chip found.\n"); } FinishDetection: #ifdef WIN32 // note CPU time needed for 1 us QueryPerformanceFrequency(&TempQudFreq); HWusTime = TempQudFreq.QuadPart / 1000000; /*QueryPerformanceCounter(&TempQud1); OPL_HW_GetStatus(); QueryPerformanceCounter(&TempQud2); TempQudMid = TempQud2.QuadPart - TempQud1.QuadPart; QueryPerformanceCounter(&TempQud1); OPL_HW_GetStatus(); QueryPerformanceCounter(&TempQud2); TempQudMid += TempQud2.QuadPart - TempQud1.QuadPart; HWusTime = TempQudMid / 2; printf("Port Read Cycles: %I64u\tMSec Cycles: %I64u\n", HWusTime, TempQudFreq.QuadPart / 1000); printf("us per ms: %I64u\n", TempQudFreq.QuadPart / (HWusTime * 1000)); HWusTime = TempQudFreq.QuadPart / 1000000;*/ if (WINNT_MODE) #endif ClosePortTalk(); #endif // DISABLE_HW_SUPPORT return; } #ifndef DISABLE_HW_SUPPORT INLINE UINT8 OPL_HW_GetStatus(void) { UINT8 RetStatus; #ifdef WIN32 switch(WINNT_MODE) { case false: // Windows 95/98/ME RetStatus = INP_9X(FMPort); break; case true: // Windows NT/2000/XP/... RetStatus = INP_NT(FMPort); break; } #else RetStatus = inb(FMPort); #endif return RetStatus; } INLINE void OPL_HW_WaitDelay(INT64 StartTime, float Delay) { #ifdef WIN32 LARGE_INTEGER CurTime; INT64 EndTime; UINT16 CurUS; // waits Delay us if (HWusTime) { OPL_HW_GetStatus(); // read once, just to be safe EndTime = (INT64)(StartTime + HWusTime * Delay); do { QueryPerformanceCounter(&CurTime); } while(CurTime.QuadPart < EndTime); } else if (Delay >= 1.0f) { for (CurUS = 0x00; CurUS < Delay; CurUS ++) OPL_HW_GetStatus(); } else { OPL_HW_GetStatus(); // read once, just to be safe } #else struct timespec NanoTime; OPL_HW_GetStatus(); // read once, then wait should work if (Delay >= 1.0f) Delay -= 1.0f; // waits Delay us NanoTime.tv_sec = 0; // xx-- nsec should be 1000 * Delay, but somehow the resulting Delay is too short --xx NanoTime.tv_nsec = 1000 * Delay; nanosleep(&NanoTime, NULL); #endif return; } #endif // DISABLE_HW_SUPPORT void OPL_HW_WriteReg(UINT16 Reg, UINT8 Data) { #ifndef DISABLE_HW_SUPPORT UINT16 Port; #ifdef WIN32 LARGE_INTEGER StartTime; #endif UINT8 DataOld; UINT8 OpNum; UINT8 TempVol; float TempSng; Reg &= 0x01FF; if (SkipMode) { OPLReg[Reg] = Data; return; } // Register = Rhythm Control and RhmythmOn-Bit changed // -> set/reset Modulator Volume of Channels 7 and 8 (Volume for HH/CYM) if (Reg == 0x0BD && (OPLReg[Reg] ^ Data) & 0x20 && FinalVol != 1.0f) { OPLReg[Reg] = Data; OPLRegForce[Reg] = 0x01; OPL_HW_WriteReg(0x51, OPLReg[0x51]); // Ch 7 Mod TL OPL_HW_WriteReg(0x52, OPLReg[0x52]); // Ch 8 Mod TL } DataOld = Data; if ((Reg & 0xE0) == 0x40) { OpNum = (Reg & 0x07) / 0x03; // Modulator 0x00, Carrier 0x01 TempVol = ((Reg & 0x18) >> 3) * 0x03 + ((Reg & 0x07) % 0x03); if ((OPLReg[(Reg & 0x100) | 0xC0 | TempVol] & 0x01)) OpNum = 0x01; // Additive Syntheses - affects final volume if (! (Reg & 0x100) && TempVol >= 0x07 && (OPLReg[0xBD] & 0x20)) OpNum = 0x01; // used as Volume for Rhythm: Hi-Hat / Cymbal if (OpNum == 0x01 && FinalVol != 1.0f) { TempVol = Data & 0x3F; TempSng = (float)pow(2.0, -TempVol / 8.0); TempSng *= FinalVol; if (TempSng > 0.0f) TempSng = (float)(-8.0 * log(TempSng) / log(2.0)); else TempSng = 64.0f; if (TempSng < 0.0f) TempVol = 0x00; else if (TempSng >= 64.0f) TempVol = 0x3F; else TempVol = (UINT8)TempSng; Data = (Data & 0xC0) | TempVol; } } if (Data == DataOld && Data == OPLReg[Reg] && ! OPLRegForce[Reg]) return; // only write neccessary registers (avoid spamming) OPLReg[Reg] = DataOld; OPLRegForce[Reg] = (Data != DataOld) ? 0x01 : 0x00; // force next write Port = (Reg & 0x100) ? (FMPort + 0x02) : (FMPort + 0x00); // 1. Set Register // 2. wait some time (short delay) // 3. Write Data // 4. wait some time (long delay) #ifdef WIN32 QueryPerformanceCounter(&StartTime); if (! WINNT_MODE) // Windows 95/98/ME OUTP_9X(Port + 0x00, Reg & 0xFF); else // Windows NT/2000/XP/... OUTP_NT(Port + 0x00, Reg & 0xFF); switch(OPL_MODE) { case 0x02: OPL_HW_WaitDelay(StartTime.QuadPart, DELAY_OPL2_REG); break; case 0x03: OPL_HW_WaitDelay(StartTime.QuadPart, DELAY_OPL3_REG); break; } #else outb(Reg & 0xFF, Port + 0x00); switch(OPL_MODE) { case 0x02: OPL_HW_WaitDelay(0, DELAY_OPL2_REG); break; case 0x03: OPL_HW_WaitDelay(0, DELAY_OPL3_REG); break; } #endif // WIN32 #ifdef WIN32 QueryPerformanceCounter(&StartTime); if (! WINNT_MODE) // Windows 95/98/ME OUTP_9X(Port + 0x01, Data); else // Windows NT/2000/XP/... OUTP_NT(Port + 0x01, Data); switch(OPL_MODE) { case 0x02: OPL_HW_WaitDelay(StartTime.QuadPart, DELAY_OPL2_DATA); break; case 0x03: OPL_HW_WaitDelay(StartTime.QuadPart, DELAY_OPL3_DATA); break; } #else outb(Data, Port + 0x01); switch(OPL_MODE) { case 0x02: OPL_HW_WaitDelay(0, DELAY_OPL2_DATA); break; case 0x03: OPL_HW_WaitDelay(0, DELAY_OPL3_DATA); break; } #endif // WIN32 #endif // DISABLE_HW_SUPPORT return; } void OPL_RegMapper(UINT16 Reg, UINT8 Data) { #ifndef DISABLE_HW_SUPPORT UINT16 NewReg; UINT8 RegType; UINT8 Grp; UINT8 Chn; UINT8 Slot; UINT8 RegOp; switch(Reg & 0xE0) { case 0x00: RegType = 0x00; break; case 0x20: case 0x40: case 0x60: case 0x80: case 0xE0: if ((Reg & 0x07) < 0x06 && ! ((Reg & 0x18) == 0x18)) RegType = 0x01; else RegType = 0x00; break; case 0xA0: if ((Reg & 0x0F) < 0x09) RegType = 0x02; else RegType = 0x00; break; case 0xC0: if ((Reg & 0x1F) < 0x09) RegType = 0x02; else RegType = 0x00; break; } Grp = (Reg & 0x100) >> 8; switch(RegType) { case 0x01: Chn = ((Reg & 0x18) >> 3) * 0x03 + ((Reg & 0x07) % 0x03); Slot = (Reg & 0x07) / 0x03; break; case 0x02: Chn = Reg & 0x0F; break; } Grp += ChipMap[SelChip].RegBase; Grp %= 0x02; Chn += ChipMap[SelChip].ChnBase; Chn %= 0x09; switch(RegType) { case 0x00: NewReg = (Grp << 8) | (Reg & 0xFF); break; case 0x01: RegOp = (Chn / 0x03) * 0x08 | ((Slot * 0x03) + (Chn % 0x03)); NewReg = (Grp << 8) | (Reg & 0xE0) | RegOp; break; case 0x02: NewReg = (Grp << 8) | (Reg & 0xF0) | Chn; break; } OPL_HW_WriteReg(NewReg, Data); #endif // DISABLE_HW_SUPPORT return; } void RefreshVolume() { #ifndef DISABLE_HW_SUPPORT UINT8 CurChip; UINT8 CurChn; UINT16 RegVal; for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { for (CurChn = 0x00; CurChn < 0x09; CurChn ++) { RegVal = (CurChip << 8) | 0x40 | (CurChn / 0x03) * 0x08 | (CurChn % 0x03); OPL_HW_WriteReg(RegVal + 0x00, OPLReg[RegVal + 0x00]); OPL_HW_WriteReg(RegVal + 0x03, OPLReg[RegVal + 0x03]); } } return; #endif } void StartSkipping(void) { #ifndef DISABLE_HW_SUPPORT if (SkipMode) return; SkipMode = true; memcpy(OPLRegBak, OPLReg, 0x200); #endif return; } void StopSkipping(void) { #ifndef DISABLE_HW_SUPPORT UINT16 Reg; UINT16 OpReg; UINT8 RRBuffer[0x40]; if (! SkipMode) return; SkipMode = false; // At first, turn all notes off that need it memcpy(RRBuffer + 0x00, &OPLReg[0x080], 0x20); memcpy(RRBuffer + 0x20, &OPLReg[0x180], 0x20); for (Reg = 0xB0; Reg < 0xB9; Reg ++) { OpReg = Reg & 0x0F; OpReg = (OpReg / 3) * 0x08 + (OpReg % 3); if (! (OPLReg[0x100 | Reg] & 0x20)) { OPL_HW_WriteReg(0x180 + OpReg, (OPLReg[0x180 + OpReg] & 0xF0) | 0x0F); OPL_HW_WriteReg(0x183 + OpReg, (OPLReg[0x183 + OpReg] & 0xF0) | 0x0F); OPLRegForce[0x180 + OpReg] |= 0x01; OPLRegForce[0x183 + OpReg] |= 0x01; OPLRegForce[0x100 | Reg] |= (OPLReg[0x100 | Reg] != OPLRegBak[0x100 | Reg]); OPL_HW_WriteReg(0x100 | Reg, OPLReg[0x100 | Reg]); } if (! (OPLReg[0x000 | Reg] & 0x20)) { OPL_HW_WriteReg(0x080 + OpReg, (OPLReg[0x080 + OpReg] & 0xF0) | 0x0F); OPL_HW_WriteReg(0x083 + OpReg, (OPLReg[0x083 + OpReg] & 0xF0) | 0x0F); OPLRegForce[0x080 + OpReg] |= 0x01; OPLRegForce[0x083 + OpReg] |= 0x01; OPLRegForce[0x000 | Reg] |= (OPLReg[0x000 | Reg] != OPLRegBak[0x000 | Reg]); OPL_HW_WriteReg(0x000 | Reg, OPLReg[0x000 | Reg]); } } memcpy(&OPLReg[0x080], RRBuffer + 0x00, 0x20); memcpy(&OPLReg[0x180], RRBuffer + 0x20, 0x20); // Now the actual save restore. Reg = 0x105; // OPL3 Enable/Disable - this must be the very first thing sent OPLRegForce[Reg] |= (OPLReg[Reg] != OPLRegBak[Reg]); OPL_HW_WriteReg(Reg, OPLReg[Reg]); // Registers 0x00 to 0x1F and 0x100 to 0x11F MUST be sent first for (Reg = 0x00; Reg < 0x20; Reg ++) { // Write Port 1 first, so that Port 0 writes override them, if OPL3 mode is disabled OPLRegForce[0x100 | Reg] |= (OPLReg[0x100 | Reg] != OPLRegBak[0x100 | Reg]); OPL_HW_WriteReg(0x100 | Reg, OPLReg[0x100 | Reg]); OPLRegForce[0x000 | Reg] |= (OPLReg[0x000 | Reg] != OPLRegBak[0x000 | Reg]); OPL_HW_WriteReg(0x000 | Reg, OPLReg[0x000 | Reg]); } Reg = 0x200; do { Reg --; OPLRegForce[Reg] |= (OPLReg[Reg] != OPLRegBak[Reg]); OPL_HW_WriteReg(Reg, OPLReg[Reg]); if ((Reg & 0xFF) == 0xC0) Reg -= 0x20; // Skip the frequency-registers if ((Reg & 0xFF) == 0x20) Reg -= 0x20; // Skip the registers that are already sent } while(Reg); for (Reg = 0xA0; Reg < 0xC0; Reg ++) { // Writing to BA/BB on my YMF744 is like writing to B8/B9, so I need to filter such // writes out. if ((Reg & 0x0F) >= 0x09) continue; OPLRegForce[0x100 | Reg] |= (OPLReg[0x100 | Reg] != OPLRegBak[0x100 | Reg]); OPL_HW_WriteReg(0x100 | Reg, OPLReg[0x100 | Reg]); OPLRegForce[0x000 | Reg] |= (OPLReg[0x000 | Reg] != OPLRegBak[0x000 | Reg]); OPL_HW_WriteReg(0x000 | Reg, OPLReg[0x000 | Reg]); } Reg = 0x0BD; // Rhythm Register / Vibrato/Tremolo Depth OPLRegForce[Reg] |= (OPLReg[Reg] != OPLRegBak[Reg]); OPL_HW_WriteReg(Reg, OPLReg[Reg]); #endif return; } void ym2413opl_set_emu_core(UINT8 Emulator) { YM2413_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; return; } ================================================ FILE: VGMPlay/ChipMapper.h ================================================ void open_fm_option(UINT8 ChipType, UINT8 OptType, UINT32 OptVal); void opl_chip_reset(void); void open_real_fm(void); void reset_real_fm(void); void setup_real_fm(UINT8 ChipType, UINT8 ChipID); void close_real_fm(void); void chip_reg_write(UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data); void OPL_Hardware_Detecton(void); void OPL_HW_WriteReg(UINT16 Reg, UINT8 Data); void OPL_RegMapper(UINT16 Reg, UINT8 Data); void RefreshVolume(void); void StartSkipping(void); void StopSkipping(void); void ym2413opl_set_emu_core(UINT8 Emulator); ================================================ FILE: VGMPlay/Makefile ================================================ ######################## # # VGMPlay Makefile # (for GNU Make 3.81) # ######################## # TODO: Make this look less horrible. # (notice that you can just pass these as arguments when running make) # set to 1 if you build on Windows using MinGW. ifndef WINDOWS WINDOWS = 0 endif # set to 1 if you build on Mac OSX ifndef MACOSX MACOSX = 0 endif ifeq ($(WINDOWS), 1) # no libAO for Windows USE_LIBAO = 0 endif ifeq ($(MACOSX), 1) # Mac OSX requires libAO USE_LIBAO = 1 endif # disable Hardware OPL Support [enabled by default] ifndef DISABLE_HWOPL_SUPPORT DISABLE_HWOPL_SUPPORT = 1 endif # set to 1 if you want to use libao instead of OSS for sound streaming under Linux [enabled by default] ifndef USE_LIBAO USE_LIBAO = 1 endif # set to 1 for debug builds ifndef DEBUG DEBUG = 0 endif # set to 1 for swprintf fix on older MinGW versions ifndef OLD_SWPRINTF OLD_SWPRINTF = 0 endif # Byte Order # 0 = undefined (use endian-safe code, might be slightly slower) # 1 = Little Endian (Intel) [default] # 2 = Big Endian (Motorola) ifndef BYTE_ORDER BYTE_ORDER = 1 endif # set to 0 to compile without dbus support on linux ifndef USE_DBUS USE_DBUS = 1 endif ifeq ($(WINDOWS), 1) USE_DBUS = 0 endif ifeq ($(MACOSX), 1) USE_DBUS = 0 endif CC = gcc ifndef PREFIX PREFIX = /usr/local #PREFIX = $(HOME)/.local endif MANPREFIX = $(PREFIX)/share/man # -- VGMPlay specific Compile Flags -- MAINFLAGS := -DCONSOLE_MODE -DADDITIONAL_FORMATS -DSET_CONSOLE_TITLE ifeq ($(WINDOWS), 1) # MinGW defines __WINDOWS__, Visual Studio defines WIN32 MAINFLAGS += -DWIN32 endif ifeq ($(OLD_SWPRINTF), 1) MAINFLAGS += -DOLD_SWPRINTF endif ifeq ($(USE_LIBAO), 1) MAINFLAGS += -DUSE_LIBAO endif EMUFLAGS := -DENABLE_ALL_CORES # -- Byte Order Optimizations -- ifeq ($(BYTE_ORDER), 1) # Intel Byte Order MAINFLAGS += -DVGM_LITTLE_ENDIAN EMUFLAGS += -DVGM_LITTLE_ENDIAN else ifeq ($(BYTE_ORDER), 2) # Motorola Byte Order MAINFLAGS += -DVGM_BIG_ENDIAN EMUFLAGS += -DVGM_BIG_ENDIAN else # undefined byte order endif endif ifeq ($(DISABLE_HWOPL_SUPPORT), 1) MAINFLAGS += -DDISABLE_HW_SUPPORT endif ifeq ($(DEBUG), 0) # -- General Compile Flags -- CFLAGS := -O3 -g0 -Wno-unused-variable -Wno-unused-value -Wno-unused-but-set-variable $(CFLAGS) else CFLAGS := -g -Wall $(CFLAGS) endif # libm (math library) and libz (zlib) LDFLAGS := -lm -lz $(LDFLAGS) ifeq ($(WINDOWS), 1) # for Windows, add kernel32 and winmm (Multimedia APIs) LDFLAGS += -lkernel32 -lwinmm else # if UNIX ifeq ($(MACOSX), 1) # You might want to include additional paths using -I/some/path here, # in case some libraries (like libao) can't be found. #CFLAGS += -I/some/path else # for Linux, add librt (clock stuff) and libpthread (threads) LDFLAGS += -lrt -lpthread -pthread #DBus ifeq ($(USE_DBUS), 1) CFLAGS += $(shell pkg-config --cflags dbus-1) -std=c99 LDFLAGS += $(shell pkg-config --libs dbus-1) MAINFLAGS += -DUSE_DBUS endif endif MAINFLAGS += -pthread -DSHARE_PREFIX=\"$(PREFIX)\" endif ifeq ($(USE_LIBAO), 1) LDFLAGS += -lao endif # add Library Path, if defined ifdef LD_LIBRARY_PATH LDFLAGS += -L $(LD_LIBRARY_PATH) endif SRC = . OBJ = obj EMUSRC = $(SRC)/chips EMUOBJ = $(OBJ)/chips OBJDIRS = \ $(OBJ) \ $(EMUOBJ) MAINOBJS = \ $(OBJ)/VGMPlay.o \ $(OBJ)/VGMPlay_AddFmts.o \ $(OBJ)/Stream.o \ $(OBJ)/ChipMapper.o ifeq ($(WINDOWS), 1) ifeq ($(DISABLE_HWOPL_SUPPORT), 0) MAINOBJS += $(OBJ)/pt_ioctl.o endif endif EMUOBJS = \ $(EMUOBJ)/262intf.o \ $(EMUOBJ)/2151intf.o \ $(EMUOBJ)/2203intf.o \ $(EMUOBJ)/2413intf.o \ $(EMUOBJ)/2608intf.o \ $(EMUOBJ)/2610intf.o \ $(EMUOBJ)/2612intf.o \ $(EMUOBJ)/3526intf.o \ $(EMUOBJ)/3812intf.o \ $(EMUOBJ)/8950intf.o \ $(EMUOBJ)/adlibemu_opl2.o \ $(EMUOBJ)/adlibemu_opl3.o \ $(EMUOBJ)/ay8910.o \ $(EMUOBJ)/ay_intf.o \ $(EMUOBJ)/c140.o \ $(EMUOBJ)/c352.o \ $(EMUOBJ)/c6280.o \ $(EMUOBJ)/c6280intf.o \ $(EMUOBJ)/dac_control.o \ $(EMUOBJ)/es5503.o \ $(EMUOBJ)/es5506.o \ $(EMUOBJ)/emu2149.o \ $(EMUOBJ)/emu2413.o \ $(EMUOBJ)/fm2612.o \ $(EMUOBJ)/fm.o \ $(EMUOBJ)/fmopl.o \ $(EMUOBJ)/gb.o \ $(EMUOBJ)/iremga20.o \ $(EMUOBJ)/k051649.o \ $(EMUOBJ)/k053260.o \ $(EMUOBJ)/k054539.o \ $(EMUOBJ)/multipcm.o \ $(EMUOBJ)/nes_apu.o \ $(EMUOBJ)/nes_intf.o \ $(EMUOBJ)/np_nes_apu.o \ $(EMUOBJ)/np_nes_dmc.o \ $(EMUOBJ)/np_nes_fds.o \ $(EMUOBJ)/okim6258.o \ $(EMUOBJ)/okim6295.o \ $(EMUOBJ)/Ootake_PSG.o \ $(EMUOBJ)/opll.o \ $(EMUOBJ)/opm.o \ $(EMUOBJ)/panning.o \ $(EMUOBJ)/pokey.o \ $(EMUOBJ)/pwm.o \ $(EMUOBJ)/qsound_ctr.o \ $(EMUOBJ)/qsound_mame.o \ $(EMUOBJ)/qsound_intf.o \ $(EMUOBJ)/rf5c68.o \ $(EMUOBJ)/saa1099.o \ $(EMUOBJ)/segapcm.o \ $(EMUOBJ)/scd_pcm.o \ $(EMUOBJ)/scsp.o \ $(EMUOBJ)/scspdsp.o \ $(EMUOBJ)/sn76489.o \ $(EMUOBJ)/sn76496.o \ $(EMUOBJ)/sn764intf.o \ $(EMUOBJ)/upd7759.o \ $(EMUOBJ)/vsu.o \ $(EMUOBJ)/ws_audio.o \ $(EMUOBJ)/x1_010.o \ $(EMUOBJ)/ym2151.o \ $(EMUOBJ)/ym2413.o \ $(EMUOBJ)/ym2612.o \ $(EMUOBJ)/ym3438.o \ $(EMUOBJ)/ymdeltat.o \ $(EMUOBJ)/ymf262.o \ $(EMUOBJ)/ymf271.o \ $(EMUOBJ)/ymf278b.o \ $(EMUOBJ)/ymz280b.o \ $(EMUOBJ)/ay8910_opl.o \ $(EMUOBJ)/sn76496_opl.o \ $(EMUOBJ)/ym2413hd.o \ $(EMUOBJ)/ym2413_opl.o VGMPLAY_OBJS = \ $(OBJ)/VGMPlayUI.o ifeq ($(USE_DBUS), 1) VGMPLAY_OBJS += $(OBJ)/dbus.o else ifeq ($(WINDOWS), 1) VGMPLAY_OBJS += $(OBJ)/mmkeys_Win.o $(OBJ)/dbus_stub.o else VGMPLAY_OBJS += $(OBJ)/mmkeys_stub.o $(OBJ)/dbus_stub.o endif VGM2PCM_OBJS = \ $(OBJ)/vgm2pcm.o VGM2WAV_OBJS = \ $(OBJ)/vgm2wav.o EXTRA_OBJS = $(VGMPLAY_OBJS) $(VGM2PCM_OBJS) $(VGM2WAV_OBJS) all: vgmplay vgm2pcm vgm2wav vgmplay: $(EMUOBJS) $(MAINOBJS) $(VGMPLAY_OBJS) ifneq ($(WINDOWS), 1) ifneq ($(MACOSX), 1) @echo Generating xdg desktop entry @sed "s/@BIN_PATH@/$(subst /,\/,$(DESTDIR)$(PREFIX)/bin/)/" xdg/vgmplay.desktop.in > xdg/vgmplay.desktop endif endif @echo Linking vgmplay ... @$(CC) $(VGMPLAY_OBJS) $(MAINOBJS) $(EMUOBJS) $(LDFLAGS) -o vgmplay @echo Done. vgm2pcm: $(EMUOBJS) $(MAINOBJS) $(VGM2PCM_OBJS) @echo Linking vgm2pcm ... @$(CC) $(VGM2PCM_OBJS) $(MAINOBJS) $(EMUOBJS) $(LDFLAGS) -o vgm2pcm @echo Done. vgm2wav: $(EMUOBJS) $(MAINOBJS) $(VGM2WAV_OBJS) @echo Linking vgm2wav ... @$(CC) $(VGM2WAV_OBJS) $(MAINOBJS) $(EMUOBJS) $(LDFLAGS) -o vgm2wav @echo Done. # compile the chip-emulator c-files $(EMUOBJ)/%.o: $(EMUSRC)/%.c @echo Compiling $< ... @mkdir -p $(@D) @$(CC) $(CFLAGS) $(EMUFLAGS) -c $< -o $@ # compile the main c-files $(OBJ)/%.o: $(SRC)/%.c @echo Compiling $< ... @mkdir -p $(@D) @$(CC) $(CFLAGS) $(MAINFLAGS) -c $< -o $@ clean: @rm -f xdg/vgmplay.desktop @echo Deleting object files ... @rm -f $(MAINOBJS) $(EMUOBJS) $(EXTRA_OBJS) @echo Deleting executable files ... @rm -f vgmplay vgm2pcm vgm2wav @echo Done. # Thanks to ZekeSulastin and nextvolume for the install and uninstall routines. install: all install -m 755 vgmplay $(DESTDIR)$(PREFIX)/bin/vgmplay install -m 755 vgm2pcm $(DESTDIR)$(PREFIX)/bin/vgm2pcm install -m 755 vgm2wav $(DESTDIR)$(PREFIX)/bin/vgm2wav mkdir -m 755 -p $(DESTDIR)$(MANPREFIX)/man1 install -m 644 vgmplay.1 $(DESTDIR)$(MANPREFIX)/man1/vgmplay.1 mkdir -m 755 -p $(DESTDIR)$(PREFIX)/share/vgmplay install -m 644 VGMPlay.ini $(DESTDIR)$(PREFIX)/share/vgmplay/vgmplay.ini ifneq ($(WINDOWS), 1) ifneq ($(MACOSX), 1) xdg-icon-resource install --novendor --size 128 xdg/icons/vgmplay-128.png vgmplay xdg-icon-resource install --novendor --size 64 xdg/icons/vgmplay-64.png vgmplay xdg-icon-resource install --novendor --size 32 xdg/icons/vgmplay-32.png vgmplay xdg-icon-resource install --novendor --size 16 xdg/icons/vgmplay-16.png vgmplay xdg-mime install --novendor xdg/vgmplay-mime.xml xdg-desktop-menu install --novendor xdg/vgmplay.desktop ifeq ($(DISABLE_HWOPL_SUPPORT), 0) setcap CAP_SYS_RAWIO+ep $(DESTDIR)$(PREFIX)/bin/vgmplay || true endif endif endif # install ROMs rom_install: install -m 644 yrw801.rom $(DESTDIR)$(PREFIX)/share/vgmplay/yrw801.rom # Install the "vgm-player" wrapper play_install: install install -m 755 vgm-player $(DESTDIR)$(PREFIX)/bin/vgm-player uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/vgmplay rm -f $(DESTDIR)$(PREFIX)/bin/vgm2pcm rm -f $(DESTDIR)$(PREFIX)/bin/vgm2wav rm -f $(DESTDIR)$(PREFIX)/bin/vgm-player rm -f $(DESTDIR)$(MANPREFIX)/man1/vgmplay.1 rm -rf $(DESTDIR)$(PREFIX)/share/vgmplay .PHONY: all clean install uninstall ================================================ FILE: VGMPlay/PortTalk_IOCTL.h ================================================ /******************************************************************************/ /* */ /* PortTalk Driver for Windows NT/2000/XP */ /* Version 2.0, 12th January 2002 */ /* http://www.beyondlogic.org */ /* */ /* Copyright 2002 Craig Peacock. Craig.Peacock@beyondlogic.org */ /* Any publication or distribution of this code in source form is prohibited */ /* without prior written permission of the copyright holder. This source code */ /* is provided "as is", without any guarantee made as to its suitability or */ /* fitness for any particular use. Permission is herby granted to modify or */ /* enhance this sample code to produce a derivative program which may only be */ /* distributed in compiled object form only. */ /******************************************************************************/ #define PORTTALK_TYPE 40000 /* 32768-65535 are reserved for customers */ // The IOCTL function codes from 0x800 to 0xFFF are for customer use. #define IOCTL_IOPM_RESTRICT_ALL_ACCESS \ CTL_CODE(PORTTALK_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_IOPM_ALLOW_EXCUSIVE_ACCESS \ CTL_CODE(PORTTALK_TYPE, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SET_IOPM \ CTL_CODE(PORTTALK_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_ENABLE_IOPM_ON_PROCESSID \ CTL_CODE(PORTTALK_TYPE, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_READ_PORT_UCHAR \ CTL_CODE(PORTTALK_TYPE, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WRITE_PORT_UCHAR \ CTL_CODE(PORTTALK_TYPE, 0x905, METHOD_BUFFERED, FILE_ANY_ACCESS) ================================================ FILE: VGMPlay/Stream.c ================================================ // Stream.c: C Source File for Sound Output // // Thanks to nextvolume for NetBSD support #define _GNU_SOURCE #include #include "stdbool.h" #include #ifdef WIN32 #include #ifdef USE_LIBAO #error "Sorry, but this doesn't work yet!" #endif // USE_LIBAO #else #include #include #include #include #ifdef USE_LIBAO #include #else #ifdef __NetBSD__ #include #elif defined(__linux__) #include #endif // __NETBSD__ #endif // USE_LIBAO #endif // WIN32 #include "chips/mamedef.h" // for UINT8 etc. #include "VGMPlay.h" // neccessary for VGMPlay_Intf.h #include "VGMPlay_Intf.h" #include "Stream.h" #ifndef WIN32 typedef struct { UINT16 wFormatTag; UINT16 nChannels; UINT32 nSamplesPerSec; UINT32 nAvgBytesPerSec; UINT16 nBlockAlign; UINT16 wBitsPerSample; UINT16 cbSize; } WAVEFORMATEX; // from MSDN Help #define WAVE_FORMAT_PCM 0x0001 #endif #ifdef WIN32 static DWORD WINAPI WaveOutThread(void* Arg); static void BufCheck(void); #else void WaveOutCallbackFnc(void); #endif UINT16 AUDIOBUFFERU = AUDIOBUFFERS; // used AudioBuffers WAVEFORMATEX WaveFmt; extern UINT32 SampleRate; extern volatile bool PauseThread; volatile bool StreamPause; extern bool ThreadPauseEnable; extern volatile bool ThreadPauseConfrm; UINT32 BlockLen; #ifdef WIN32 static HWAVEOUT hWaveOut; static WAVEHDR WaveHdrOut[AUDIOBUFFERS]; static HANDLE hWaveOutThread; //static DWORD WaveOutCallbackThrID; #else static INT32 hWaveOut; #endif static bool WaveOutOpen; UINT32 BUFFERSIZE; // Buffer Size in Bytes UINT32 SMPL_P_BUFFER; static char BufferOut[AUDIOBUFFERS][BUFSIZE_MAX]; static volatile bool CloseThread; bool SoundLog; static FILE* hFile; UINT32 SndLogLen; UINT32 BlocksSent; UINT32 BlocksPlayed; char SoundLogFile[MAX_PATH]; #ifdef USE_LIBAO ao_device* dev_ao; #endif INLINE int fputLE32(UINT32 Value, FILE* hFile) { #ifdef VGM_LITTLE_ENDIAN return fwrite(&Value, 0x04, 1, hFile); #else int RetVal; int ResVal; RetVal = fputc((Value & 0x000000FF) >> 0, hFile); RetVal = fputc((Value & 0x0000FF00) >> 8, hFile); RetVal = fputc((Value & 0x00FF0000) >> 16, hFile); RetVal = fputc((Value & 0xFF000000) >> 24, hFile); ResVal = (RetVal != EOF) ? 0x04 : 0x00; return ResVal; #endif } INLINE int fputLE16(UINT16 Value, FILE* hFile) { #ifdef VGM_LITTLE_ENDIAN return fwrite(&Value, 0x02, 1, hFile); #else int RetVal; int ResVal; RetVal = fputc((Value & 0x00FF) >> 0, hFile); RetVal = fputc((Value & 0xFF00) >> 8, hFile); ResVal = (RetVal != EOF) ? 0x02 : 0x00; return ResVal; #endif } UINT8 SaveFile(UINT32 FileLen, const void* TempData) { //char ResultStr[0x100]; UINT32 DataLen; if (TempData == NULL) { switch(FileLen) { case 0x00000000: if (hFile != NULL) return 0xD0; // file already open SndLogLen = 0; hFile = fopen(SoundLogFile,"wb"); if (hFile == NULL) return 0xFF; // Save Error fseek(hFile, 0x00000000, SEEK_SET); fputLE32(0x46464952, hFile); // 'RIFF' fputLE32(0x00000000, hFile); // RIFF chunk length (dummy) fputLE32(0x45564157, hFile); // 'WAVE' fputLE32(0x20746D66, hFile); // 'fmt ' DataLen = 0x00000010; fputLE32(DataLen, hFile); // format chunk legth #ifdef VGM_LITTLE_ENDIAN fwrite(&WaveFmt, DataLen, 1, hFile); #else fputLE16(WaveFmt.wFormatTag, hFile); // 0x00 fputLE16(WaveFmt.nChannels, hFile); // 0x02 fputLE32(WaveFmt.nSamplesPerSec, hFile); // 0x04 fputLE32(WaveFmt.nAvgBytesPerSec, hFile); // 0x08 fputLE16(WaveFmt.nBlockAlign, hFile); // 0x0C fputLE16(WaveFmt.wBitsPerSample, hFile); // 0x0E //fputLE16(WaveFmt.cbSize, hFile); // 0x10 (DataLen is 0x10, so leave this out) #endif fputLE32(0x61746164, hFile); // 'data' fputLE32(0x00000000, hFile); // data chunk length (dummy) break; case 0xFFFFFFFF: if (hFile == NULL) return 0x80; // no file opened DataLen = SndLogLen * SAMPLESIZE; fseek(hFile, 0x0028, SEEK_SET); fputLE32(DataLen, hFile); // data chunk length fseek(hFile, 0x0004, SEEK_SET); fputLE32(DataLen + 0x24, hFile); // RIFF chunk length fclose(hFile); hFile = NULL; break; } } else { if (hFile == NULL) return 0x80; // no file opened //fseek(hFile, 0x00000000, SEEK_END); //TempVal[0x0] = ftell(hFile); //TempVal[0x1] = fwrite(TempData, 1, FileLen, hFile); #ifdef VGM_LITTLE_ENDIAN SndLogLen += fwrite(TempData, SAMPLESIZE, FileLen, hFile); #else { UINT32 CurSmpl; const UINT16* SmplData; SmplData = (UINT16*)TempData; DataLen = SAMPLESIZE * FileLen / 0x02; for (CurSmpl = 0x00; CurSmpl < DataLen; CurSmpl ++) SndLogLen += fputLE16(SmplData[CurSmpl], hFile); } #endif //sprintf(ResultStr, "Position:\t%d\nBytes written:\t%d\nFile Length:\t%u\nPointer:\t%p", // TempVal[0], TempVal[1], FileLen, TempData); //AfxMessageBox(ResultStr); } return 0x00; } UINT8 SoundLogging(UINT8 Mode) { UINT8 RetVal; RetVal = (UINT8)SoundLog; switch(Mode) { case 0x00: SoundLog = false; break; case 0x01: SoundLog = true; if (WaveOutOpen && hFile == NULL) SaveFile(0x00000000, NULL); break; case 0xFF: break; default: RetVal = 0xA0; break; } return RetVal; } UINT8 StartStream(UINT8 DeviceID) { UINT32 RetVal; #ifdef USE_LIBAO ao_sample_format ao_fmt; #else #ifdef WIN32 UINT16 Cnt; HANDLE WaveOutThreadHandle; DWORD WaveOutThreadID; //char TestStr[0x80]; #elif defined(__NetBSD__) struct audio_info AudioInfo; #else UINT32 ArgVal; #endif #endif // ! USE_LIBAO if (WaveOutOpen) return 0xD0; // Thread is already active // Init Audio WaveFmt.wFormatTag = WAVE_FORMAT_PCM; WaveFmt.nChannels = 2; WaveFmt.nSamplesPerSec = SampleRate; WaveFmt.wBitsPerSample = 16; WaveFmt.nBlockAlign = WaveFmt.wBitsPerSample * WaveFmt.nChannels / 8; WaveFmt.nAvgBytesPerSec = WaveFmt.nSamplesPerSec * WaveFmt.nBlockAlign; WaveFmt.cbSize = 0; if (DeviceID == 0xFF) return 0x00; #if defined(WIN32) || defined(USE_LIBAO) BUFFERSIZE = SampleRate / 100 * SAMPLESIZE; if (BUFFERSIZE > BUFSIZE_MAX) BUFFERSIZE = BUFSIZE_MAX; #else BUFFERSIZE = 1 << BUFSIZELD; #endif SMPL_P_BUFFER = BUFFERSIZE / SAMPLESIZE; if (AUDIOBUFFERU > AUDIOBUFFERS) AUDIOBUFFERU = AUDIOBUFFERS; PauseThread = true; ThreadPauseConfrm = false; CloseThread = false; StreamPause = false; #ifndef USE_LIBAO #ifdef WIN32 ThreadPauseEnable = true; WaveOutThreadHandle = CreateThread(NULL, 0x00, &WaveOutThread, NULL, 0x00, &WaveOutThreadID); if(WaveOutThreadHandle == NULL) return 0xC8; // CreateThread failed CloseHandle(WaveOutThreadHandle); RetVal = waveOutOpen(&hWaveOut, ((UINT)DeviceID - 1), &WaveFmt, 0x00, 0x00, CALLBACK_NULL); if(RetVal != MMSYSERR_NOERROR) #else ThreadPauseEnable = false; #ifdef __NetBSD__ hWaveOut = open("/dev/audio", O_WRONLY); #else hWaveOut = open("/dev/dsp", O_WRONLY); #endif if (hWaveOut < 0) #endif #else // ifdef USE_LIBAO ao_initialize(); ThreadPauseEnable = false; ao_fmt.bits = WaveFmt.wBitsPerSample; ao_fmt.rate = WaveFmt.nSamplesPerSec; ao_fmt.channels = WaveFmt.nChannels; ao_fmt.byte_format = AO_FMT_NATIVE; ao_fmt.matrix = NULL; dev_ao = ao_open_live(ao_default_driver_id(), &ao_fmt, NULL); if (dev_ao == NULL) #endif { CloseThread = true; return 0xC0; // waveOutOpen failed } WaveOutOpen = true; //sprintf(TestStr, "Buffer 0,0:\t%p\nBuffer 0,1:\t%p\nBuffer 1,0:\t%p\nBuffer 1,1:\t%p\n", // &BufferOut[0][0], &BufferOut[0][1], &BufferOut[1][0], &BufferOut[1][1]); //AfxMessageBox(TestStr); #ifndef USE_LIBAO #ifdef WIN32 for (Cnt = 0x00; Cnt < AUDIOBUFFERU; Cnt ++) { WaveHdrOut[Cnt].lpData = BufferOut[Cnt]; // &BufferOut[Cnt][0x00]; WaveHdrOut[Cnt].dwBufferLength = BUFFERSIZE; WaveHdrOut[Cnt].dwBytesRecorded = 0x00; WaveHdrOut[Cnt].dwUser = 0x00; WaveHdrOut[Cnt].dwFlags = 0x00; WaveHdrOut[Cnt].dwLoops = 0x00; WaveHdrOut[Cnt].lpNext = NULL; WaveHdrOut[Cnt].reserved = 0x00; RetVal = waveOutPrepareHeader(hWaveOut, &WaveHdrOut[Cnt], sizeof(WAVEHDR)); WaveHdrOut[Cnt].dwFlags |= WHDR_DONE; } #elif defined(__NetBSD__) AUDIO_INITINFO(&AudioInfo); AudioInfo.mode = AUMODE_PLAY; AudioInfo.play.sample_rate = WaveFmt.nSamplesPerSec; AudioInfo.play.channels = WaveFmt.nChannels; AudioInfo.play.precision = WaveFmt.wBitsPerSample; AudioInfo.play.encoding = AUDIO_ENCODING_SLINEAR; RetVal = ioctl(hWaveOut, AUDIO_SETINFO, &AudioInfo); if (RetVal) printf("Error setting audio information!\n"); #else ArgVal = (AUDIOBUFFERU << 16) | BUFSIZELD; RetVal = ioctl(hWaveOut, SNDCTL_DSP_SETFRAGMENT, &ArgVal); if (RetVal) printf("Error setting Fragment Size!\n"); ArgVal = AFMT_S16_NE; RetVal = ioctl(hWaveOut, SNDCTL_DSP_SETFMT, &ArgVal); if (RetVal) printf("Error setting Format!\n"); ArgVal = WaveFmt.nChannels; RetVal = ioctl(hWaveOut, SNDCTL_DSP_CHANNELS, &ArgVal); if (RetVal) printf("Error setting Channels!\n"); ArgVal = WaveFmt.nSamplesPerSec; RetVal = ioctl(hWaveOut, SNDCTL_DSP_SPEED, &ArgVal); if (RetVal) printf("Error setting Sample Rate!\n"); #endif #endif // USE_LIBAO if (SoundLog) SaveFile(0x00000000, NULL); PauseThread = false; return 0x00; } UINT8 StopStream(void) { UINT32 RetVal; #ifdef WIN32 UINT16 Cnt; #endif if (! WaveOutOpen) return 0xD8; // Thread is not active CloseThread = true; #ifdef WIN32 for (Cnt = 0; Cnt < 100; Cnt ++) { Sleep(1); if (hWaveOutThread == NULL) break; } #endif if (hFile != NULL) SaveFile(0xFFFFFFFF, NULL); WaveOutOpen = false; #ifndef USE_LIBAO #ifdef WIN32 RetVal = waveOutReset(hWaveOut); for (Cnt = 0x00; Cnt < AUDIOBUFFERU; Cnt ++) RetVal = waveOutUnprepareHeader(hWaveOut, &WaveHdrOut[Cnt], sizeof(WAVEHDR)); RetVal = waveOutClose(hWaveOut); if(RetVal != MMSYSERR_NOERROR) return 0xC4; // waveOutClose failed -- but why ??? #else close(hWaveOut); #endif #else // ifdef USE_LIBAO ao_close(dev_ao); ao_shutdown(); #endif return 0x00; } void PauseStream(bool PauseOn) { UINT32 RetVal; if (! WaveOutOpen) return; // Thread is not active #ifdef WIN32 switch(PauseOn) { case true: RetVal = waveOutPause(hWaveOut); break; case false: RetVal = waveOutRestart(hWaveOut); break; } StreamPause = PauseOn; #else PauseThread = PauseOn; #endif return; } //UINT32 FillBuffer(WAVE_16BS* Buffer, UINT32 BufferSize) // moved to VGMPlay.c #ifdef WIN32 static DWORD WINAPI WaveOutThread(void* Arg) { #ifdef NDEBUG UINT32 RetVal; #endif UINT16 CurBuf; WAVE_16BS* TempBuf; UINT32 WrtSmpls; //char TestStr[0x80]; bool DidBuffer; // a buffer was processed hWaveOutThread = GetCurrentThread(); #ifdef NDEBUG RetVal = SetThreadPriority(hWaveOutThread, THREAD_PRIORITY_TIME_CRITICAL); if (! RetVal) { // Error by setting priority // try a lower priority, because too low priorities cause sound stuttering RetVal = SetThreadPriority(hWaveOutThread, THREAD_PRIORITY_HIGHEST); } #endif BlocksSent = 0x00; BlocksPlayed = 0x00; while(! CloseThread) { while(PauseThread && ! CloseThread && ! (StreamPause && DidBuffer)) { ThreadPauseConfrm = true; Sleep(1); } if (CloseThread) break; BufCheck(); DidBuffer = false; for (CurBuf = 0x00; CurBuf < AUDIOBUFFERU; CurBuf ++) { if (WaveHdrOut[CurBuf].dwFlags & WHDR_DONE) { TempBuf = (WAVE_16BS*)WaveHdrOut[CurBuf].lpData; if (WaveHdrOut[CurBuf].dwUser & 0x01) BlocksPlayed ++; else WaveHdrOut[CurBuf].dwUser |= 0x01; WrtSmpls = FillBuffer(TempBuf, SMPL_P_BUFFER); WaveHdrOut[CurBuf].dwBufferLength = WrtSmpls * SAMPLESIZE; waveOutWrite(hWaveOut, &WaveHdrOut[CurBuf], sizeof(WAVEHDR)); if (SoundLog && hFile != NULL) SaveFile(WrtSmpls, TempBuf); DidBuffer = true; BlocksSent ++; BufCheck(); //CurBuf = 0x00; //break; } if (CloseThread) break; } Sleep(1); } hWaveOutThread = NULL; return 0x00000000; } static void BufCheck(void) { UINT16 CurBuf; for (CurBuf = 0x00; CurBuf < AUDIOBUFFERU; CurBuf ++) { if (WaveHdrOut[CurBuf].dwFlags & WHDR_DONE) { if (WaveHdrOut[CurBuf].dwUser & 0x01) { WaveHdrOut[CurBuf].dwUser &= ~0x01; BlocksPlayed ++; } } } return; } #else // #ifndef WIN32 void WaveOutLinuxCallBack(void) { UINT32 RetVal; UINT16 CurBuf; WAVE_16BS* TempBuf; UINT32 WrtSmpls; if (! WaveOutOpen) return; // Device not opened CurBuf = BlocksSent % AUDIOBUFFERU; TempBuf = (WAVE_16BS*)BufferOut[CurBuf]; WrtSmpls = FillBuffer(TempBuf, SMPL_P_BUFFER); #ifndef USE_LIBAO RetVal = write(hWaveOut, TempBuf, WrtSmpls * SAMPLESIZE); #else RetVal = ao_play(dev_ao, (char*)TempBuf, WrtSmpls * SAMPLESIZE); #endif if (SoundLog && hFile != NULL) SaveFile(WrtSmpls, TempBuf); BlocksSent ++; BlocksPlayed ++; return; } #endif ================================================ FILE: VGMPlay/Stream.h ================================================ // Stream.h: Header File for constants and structures related to Sound Output // #ifdef WIN32 #include #else #define MAX_PATH PATH_MAX #endif #define SAMPLESIZE sizeof(WAVE_16BS) #define BUFSIZE_MAX 0x1000 // Maximum Buffer Size in Bytes #ifndef WIN32 #define BUFSIZELD 11 // Buffer Size #endif #define AUDIOBUFFERS 200 // Maximum Buffer Count // Windows: BUFFERSIZE = SampleRate / 100 * SAMPLESIZE (44100 / 100 * 4 = 1764) // 1 Audio-Buffer = 10 msec, Min: 5 // Win95- / WinVista-safe: 500 msec // Linux: BUFFERSIZE = 1 << BUFSIZELD (1 << 11 = 2048) // 1 Audio-Buffer = 11.6 msec UINT8 SaveFile(UINT32 FileLen, const void* TempData); UINT8 SoundLogging(UINT8 Mode); UINT8 StartStream(UINT8 DeviceID); UINT8 StopStream(void); void PauseStream(bool PauseOn); ================================================ FILE: VGMPlay/VGMFile.h ================================================ // Header file for VGM file handling typedef struct _vgm_file_header { UINT32 fccVGM; UINT32 lngEOFOffset; UINT32 lngVersion; UINT32 lngHzPSG; UINT32 lngHzYM2413; UINT32 lngGD3Offset; UINT32 lngTotalSamples; UINT32 lngLoopOffset; UINT32 lngLoopSamples; UINT32 lngRate; UINT16 shtPSG_Feedback; UINT8 bytPSG_SRWidth; UINT8 bytPSG_Flags; UINT32 lngHzYM2612; UINT32 lngHzYM2151; UINT32 lngDataOffset; UINT32 lngHzSPCM; UINT32 lngSPCMIntf; UINT32 lngHzRF5C68; UINT32 lngHzYM2203; UINT32 lngHzYM2608; UINT32 lngHzYM2610; UINT32 lngHzYM3812; UINT32 lngHzYM3526; UINT32 lngHzY8950; UINT32 lngHzYMF262; UINT32 lngHzYMF278B; UINT32 lngHzYMF271; UINT32 lngHzYMZ280B; UINT32 lngHzRF5C164; UINT32 lngHzPWM; UINT32 lngHzAY8910; UINT8 bytAYType; UINT8 bytAYFlag; UINT8 bytAYFlagYM2203; UINT8 bytAYFlagYM2608; UINT8 bytVolumeModifier; UINT8 bytReserved2; INT8 bytLoopBase; UINT8 bytLoopModifier; UINT32 lngHzGBDMG; UINT32 lngHzNESAPU; UINT32 lngHzMultiPCM; UINT32 lngHzUPD7759; UINT32 lngHzOKIM6258; UINT8 bytOKI6258Flags; UINT8 bytK054539Flags; UINT8 bytC140Type; UINT8 bytReservedFlags; UINT32 lngHzOKIM6295; UINT32 lngHzK051649; UINT32 lngHzK054539; UINT32 lngHzHuC6280; UINT32 lngHzC140; UINT32 lngHzK053260; UINT32 lngHzPokey; UINT32 lngHzQSound; UINT32 lngHzSCSP; // UINT32 lngHzOKIM6376; //UINT8 bytReserved[0x04]; UINT32 lngExtraOffset; UINT32 lngHzWSwan; UINT32 lngHzVSU; UINT32 lngHzSAA1099; UINT32 lngHzES5503; UINT32 lngHzES5506; UINT8 bytES5503Chns; UINT8 bytES5506Chns; UINT8 bytC352ClkDiv; UINT8 bytESReserved; UINT32 lngHzX1_010; UINT32 lngHzC352; UINT32 lngHzGA20; } VGM_HEADER; typedef struct _vgm_header_extra { UINT32 DataSize; UINT32 Chp2ClkOffset; UINT32 ChpVolOffset; } VGM_HDR_EXTRA; typedef struct _vgm_extra_chip_data32 { UINT8 Type; UINT32 Data; } VGMX_CHIP_DATA32; typedef struct _vgm_extra_chip_data16 { UINT8 Type; UINT8 Flags; UINT16 Data; } VGMX_CHIP_DATA16; typedef struct _vgm_extra_chip_extra32 { UINT8 ChipCnt; VGMX_CHIP_DATA32* CCData; } VGMX_CHP_EXTRA32; typedef struct _vgm_extra_chip_extra16 { UINT8 ChipCnt; VGMX_CHIP_DATA16* CCData; } VGMX_CHP_EXTRA16; typedef struct _vgm_header_extra_data { VGMX_CHP_EXTRA32 Clocks; VGMX_CHP_EXTRA16 Volumes; } VGM_EXTRA; #define VOLUME_MODIF_WRAP 0xC0 typedef struct _vgm_gd3_tag { UINT32 fccGD3; UINT32 lngVersion; UINT32 lngTagLength; wchar_t* strTrackNameE; wchar_t* strTrackNameJ; wchar_t* strGameNameE; wchar_t* strGameNameJ; wchar_t* strSystemNameE; wchar_t* strSystemNameJ; wchar_t* strAuthorNameE; wchar_t* strAuthorNameJ; wchar_t* strReleaseDate; wchar_t* strCreator; wchar_t* strNotes; } GD3_TAG; typedef struct _vgm_pcm_bank_data { UINT32 DataSize; UINT8* Data; UINT32 DataStart; } VGM_PCM_DATA; typedef struct _vgm_pcm_bank { UINT32 BankCount; VGM_PCM_DATA* Bank; UINT32 DataSize; UINT8* Data; UINT32 DataPos; UINT32 BnkPos; } VGM_PCM_BANK; #define FCC_VGM 0x206D6756 // 'Vgm ' #define FCC_GD3 0x20336447 // 'Gd3 ' ================================================ FILE: VGMPlay/VGMPlay.c ================================================ // VGMPlay.c: C Source File of the Main Executable // // Line Size: 96 Chars // Tab Size: 4 Spaces /*3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999*/ // Mixer Muting ON: // Mixer's FM Volume is set to 0 or Mute -> absolutely muted // (sometimes it can take some time to get the Mixer Control under Windows) // Mixer Muting OFF: // FM Volume is set to 0 through commands -> very very low volume level ~0.4% // (faster way) //#define MIXER_MUTING // These defines enable additional features. // ADDITIONAL_FORMATS enables CMF and DRO support. // CONSOLE_MODE switches between VGMPlay and in_vgm mode. // in_vgm mode can also be used for custom players. // //#define ADDITIONAL_FORMATS //#define CONSOLE_MODE //#define VGM_LITTLE_ENDIAN // enable optimizations for Little Endian systems //#define VGM_BIG_ENDIAN // enable optimizations for Big Endian systems #define _GNU_SOURCE #include #include #include #include #include "stdbool.h" #include // for pow() #ifdef WIN32 #ifndef NO_WCHAR_FILENAMES // includes for manual wide-character ZLib open #include // for _O_RDONLY and _O_BINARY #include // for _wopen and _close #endif #include // for _inp() #include #else //ifdef UNIX #include // for PATH_MAX #include // for pthread functions // (suitable?) Apple substitute for clock_gettime() //#ifdef __MACH__ #if 0 // not required in Mac OS X 10.12 and later #include #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 0 int clock_gettime(int clk_id, struct timespec *t) { mach_timebase_info_data_t timebase; mach_timebase_info(&timebase); uint64_t time; time = mach_absolute_time(); double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); t->tv_sec = seconds; t->tv_nsec = nseconds; return 0; } #else #include // for clock_gettime() #endif #include // for usleep() #define MAX_PATH PATH_MAX #define Sleep(msec) usleep(msec * 1000) #undef MIXER_MUTING // I didn't get the Mixer Control to work under Linux #ifdef MIXER_MUTING #include #include #include #endif #endif // WIN32/UNIX #include #include "chips/mamedef.h" // integer types for fast integer calculation // the bit number is unused (it's an orientation) #define FUINT8 unsigned int #define FUINT16 unsigned int #include "VGMPlay.h" #include "VGMPlay_Intf.h" #ifdef CONSOLE_MODE #include "Stream.h" #endif #include "chips/ChipIncl.h" unsigned char OpenPortTalk(void); void ClosePortTalk(void); #include "ChipMapper.h" typedef void (*strm_func)(UINT8 ChipID, stream_sample_t **outputs, int samples); typedef struct chip_audio_attributes CAUD_ATTR; struct chip_audio_attributes { UINT32 SmpRate; UINT16 Volume; UINT8 ChipType; UINT8 ChipID; // 0 - 1st chip, 1 - 2nd chip, etc. // Resampler Type: // 00 - Old // 01 - Upsampling // 02 - Copy // 03 - Downsampling UINT8 Resampler; strm_func StreamUpdate; UINT32 SmpP; // Current Sample (Playback Rate) UINT32 SmpLast; // Sample Number Last UINT32 SmpNext; // Sample Number Next WAVE_32BS LSmpl; // Last Sample WAVE_32BS NSmpl; // Next Sample CAUD_ATTR* Paired; }; typedef struct chip_audio_struct { CAUD_ATTR SN76496; CAUD_ATTR YM2413; CAUD_ATTR YM2612; CAUD_ATTR YM2151; CAUD_ATTR SegaPCM; CAUD_ATTR RF5C68; CAUD_ATTR YM2203; CAUD_ATTR YM2608; CAUD_ATTR YM2610; CAUD_ATTR YM3812; CAUD_ATTR YM3526; CAUD_ATTR Y8950; CAUD_ATTR YMF262; CAUD_ATTR YMF278B; CAUD_ATTR YMF271; CAUD_ATTR YMZ280B; CAUD_ATTR RF5C164; CAUD_ATTR PWM; CAUD_ATTR AY8910; CAUD_ATTR GameBoy; CAUD_ATTR NES; CAUD_ATTR MultiPCM; CAUD_ATTR UPD7759; CAUD_ATTR OKIM6258; CAUD_ATTR OKIM6295; CAUD_ATTR K051649; CAUD_ATTR K054539; CAUD_ATTR HuC6280; CAUD_ATTR C140; CAUD_ATTR K053260; CAUD_ATTR Pokey; CAUD_ATTR QSound; CAUD_ATTR SCSP; CAUD_ATTR WSwan; CAUD_ATTR VSU; CAUD_ATTR SAA1099; CAUD_ATTR ES5503; CAUD_ATTR ES5506; CAUD_ATTR X1_010; CAUD_ATTR C352; CAUD_ATTR GA20; // CAUD_ATTR OKIM6376; } CHIP_AUDIO; typedef struct chip_aud_list CA_LIST; struct chip_aud_list { CAUD_ATTR* CAud; CHIP_OPTS* COpts; CA_LIST* next; }; typedef struct daccontrol_data { bool Enable; UINT8 Bank; } DACCTRL_DATA; typedef struct pcmbank_table { UINT8 ComprType; UINT8 CmpSubType; UINT8 BitDec; UINT8 BitCmp; UINT16 EntryCount; void* Entries; } PCMBANK_TBL; // Function Prototypes (prototypes in comments are defined in VGMPlay_Intf.h) //void VGMPlay_Init(void); //void VGMPlay_Init2(void); //void VGMPlay_Deinit(void); //char* FindFile(const char* FileName) INLINE UINT16 ReadLE16(const UINT8* Data); INLINE UINT16 ReadBE16(const UINT8* Data); INLINE UINT32 ReadLE24(const UINT8* Data); INLINE UINT32 ReadLE32(const UINT8* Data); INLINE int gzgetLE16(gzFile hFile, UINT16* RetValue); INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue); static UINT32 gcd(UINT32 x, UINT32 y); //void PlayVGM(void); //void StopVGM(void); //void RestartVGM(void); //void PauseVGM(bool Pause); //void SeekVGM(bool Relative, INT32 PlayBkSamples); //void RefreshMuting(void); //void RefreshPanning(void); //void RefreshPlaybackOptions(void); //UINT32 GetGZFileLength(const char* FileName); //UINT32 GetGZFileLengthW(const wchar_t* FileName); static UINT32 GetGZFileLength_Internal(FILE* hFile); //bool OpenVGMFile(const char* FileName); static bool OpenVGMFile_Internal(gzFile hFile, UINT32 FileSize); static void ReadVGMHeader(gzFile hFile, VGM_HEADER* RetVGMHead); static UINT8 ReadGD3Tag(gzFile hFile, UINT32 GD3Offset, GD3_TAG* RetGD3Tag); static void ReadChipExtraData32(UINT32 StartOffset, VGMX_CHP_EXTRA32* ChpExtra); static void ReadChipExtraData16(UINT32 StartOffset, VGMX_CHP_EXTRA16* ChpExtra); //void CloseVGMFile(void); //void FreeGD3Tag(GD3_TAG* TagData); static wchar_t* MakeEmptyWStr(void); static wchar_t* ReadWStrFromFile(gzFile hFile, UINT32* FilePos, UINT32 EOFPos); //UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); static UINT32 GetVGMFileInfo_Internal(gzFile hFile, UINT32 FileSize, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator); //UINT32 CalcSampleMSec(UINT64 Value, UINT8 Mode); //UINT32 CalcSampleMSecExt(UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead); //const char* GetChipName(UINT8 ChipID); //const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType); //UINT32 GetChipClock(VGM_HEADER* FileHead, UINT8 ChipID, UINT8* RetSubType); static UINT16 GetChipVolume(VGM_HEADER* FileHead, UINT8 ChipID, UINT8 ChipNum, UINT8 ChipCnt); static void RestartPlaying(void); static void Chips_GeneralActions(UINT8 Mode); INLINE INT32 SampleVGM2Pbk_I(INT32 SampleVal); // inline functions INLINE INT32 SamplePbk2VGM_I(INT32 SampleVal); //INT32 SampleVGM2Playback(INT32 SampleVal); // non-inline functions //INT32 SamplePlayback2VGM(INT32 SampleVal); static UINT8 StartThread(void); static UINT8 StopThread(void); #if defined(WIN32) && defined(MIXER_MUTING) static bool GetMixerControl(void); #endif static bool SetMuteControl(bool mute); static void InterpretFile(UINT32 SampleCount); static void AddPCMData(UINT8 Type, UINT32 DataSize, const UINT8* Data); //INLINE FUINT16 ReadBits(UINT8* Data, UINT32* Pos, FUINT8* BitPos, FUINT8 BitsToRead); static bool DecompressDataBlk(VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data); static UINT8 GetDACFromPCMBank(void); static UINT8* GetPointerFromPCMBank(UINT8 Type, UINT32 DataPos); static void ReadPCMTable(UINT32 DataSize, const UINT8* Data); static void InterpretVGM(UINT32 SampleCount); #ifdef ADDITIONAL_FORMATS extern void InterpretOther(UINT32 SampleCount); #endif static void GeneralChipLists(void); static void SetupResampler(CAUD_ATTR* CAA); static void ChangeChipSampleRate(void* DataPtr, UINT32 NewSmplRate); INLINE INT16 Limit2Short(INT32 Value); static void null_update(UINT8 ChipID, stream_sample_t **outputs, int samples); static void dual_opl2_stereo(UINT8 ChipID, stream_sample_t **outputs, int samples); static void ResampleChipStream(CA_LIST* CLst, WAVE_32BS* RetSample, UINT32 Length); static INT32 RecalcFadeVolume(void); //UINT32 FillBuffer(WAVE_16BS* Buffer, UINT32 BufferSize) #ifdef WIN32 DWORD WINAPI PlayingThread(void* Arg); #else UINT64 TimeSpec2Int64(const struct timespec* ts); void* PlayingThread(void* Arg); #endif // Options Variables UINT32 SampleRate; // Note: also used by some sound cores to determinate the chip sample rate UINT32 VGMMaxLoop; UINT32 VGMPbRate; // in Hz, ignored if this value or VGM's lngRate Header value is 0 #ifdef ADDITIONAL_FORMATS extern UINT32 CMFMaxLoop; #endif UINT32 FadeTime; UINT32 PauseTime; // current Pause Time float VolumeLevel; bool SurroundSound; UINT8 HardStopOldVGMs; bool FadeRAWLog; //bool FullBufFill; // Fill Buffer until it's full bool PauseEmulate; bool DoubleSSGVol; UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both UINT8 CHIP_SAMPLING_MODE; INT32 CHIP_SAMPLE_RATE; UINT16 FMPort; bool FMForce; //bool FMAccurate; bool FMBreakFade; float FMVol; bool FMOPL2Pan; CHIPS_OPTION ChipOpts[0x02]; UINT8 OPL_MODE; UINT8 OPL_CHIPS; #ifdef WIN32 bool WINNT_MODE; #endif stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; char* AppPaths[8]; bool AutoStopSkip; UINT8 FileMode; VGM_HEADER VGMHead; VGM_HDR_EXTRA VGMHeadX; VGM_EXTRA VGMH_Extra; UINT32 VGMDataLen; UINT8* VGMData; GD3_TAG VGMTag; #define PCM_BANK_COUNT 0x40 VGM_PCM_BANK PCMBank[PCM_BANK_COUNT]; PCMBANK_TBL PCMTbl; UINT8 DacCtrlUsed; UINT8 DacCtrlUsg[0xFF]; DACCTRL_DATA DacCtrl[0xFF]; #ifdef WIN32 HANDLE hPlayThread; #else pthread_t hPlayThread; #endif bool PlayThreadOpen; volatile bool PauseThread; static volatile bool CloseThread; bool ThreadPauseEnable; volatile bool ThreadPauseConfrm; bool ThreadNoWait; // don't reset the timer CHIP_AUDIO ChipAudio[0x02]; CAUD_ATTR CA_Paired[0x02][0x03]; float MasterVol; CA_LIST ChipListBuffer[0x200]; CA_LIST* ChipListAll; // all chips needed for playback (in general) CA_LIST* ChipListPause; // all chips needed for EmulateWhilePaused //CA_LIST* ChipListOpt; // ChipListAll minus muted chips CA_LIST* CurChipList; // pointer to Pause or All [Opt] #define SMPL_BUFSIZE 0x100 static INT32* StreamBufs[0x02]; #ifdef MIXER_MUTING #ifdef WIN32 HMIXER hmixer; MIXERCONTROL mixctrl; #else int hmixer; UINT16 mixer_vol; #endif #else //#ifndef MIXER_MUTING float VolumeBak; #endif UINT32 VGMPos; INT32 VGMSmplPos; INT32 VGMSmplPlayed; INT32 VGMSampleRate; static UINT32 VGMPbRateMul; static UINT32 VGMPbRateDiv; static UINT32 VGMSmplRateMul; static UINT32 VGMSmplRateDiv; static UINT32 PauseSmpls; bool VGMEnd; bool EndPlay; bool PausePlay; bool FadePlay; bool ForceVGMExec; UINT8 PlayingMode; bool UseFM; UINT32 PlayingTime; UINT32 FadeStart; UINT32 VGMMaxLoopM; UINT32 VGMCurLoop; float VolumeLevelM; float FinalVol; bool ResetPBTimer; static bool Interpreting; #ifdef CONSOLE_MODE extern bool ErrorHappened; extern UINT8 CmdList[0x100]; #endif UINT8 IsVGMInit; UINT16 Last95Drum; // for optvgm debugging UINT16 Last95Max; // for optvgm debugging UINT32 Last95Freq; // for optvgm debugging void VGMPlay_Init(void) { UINT8 CurChip; UINT8 CurCSet; UINT8 CurChn; CHIP_OPTS* TempCOpt; CAUD_ATTR* TempCAud; SampleRate = 44100; FadeTime = 5000; PauseTime = 0; HardStopOldVGMs = 0x00; FadeRAWLog = false; VolumeLevel = 1.0f; //FullBufFill = false; FMPort = 0x0000; FMForce = false; //FMAccurate = false; FMBreakFade = false; FMVol = 0.0f; FMOPL2Pan = false; SurroundSound = false; VGMMaxLoop = 0x02; VGMPbRate = 0; #ifdef ADDITIONAL_FORMATS CMFMaxLoop = 0x01; #endif ResampleMode = 0x00; CHIP_SAMPLING_MODE = 0x00; CHIP_SAMPLE_RATE = 0x00000000; PauseEmulate = false; DoubleSSGVol = false; for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { TempCAud = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCAud ++) { TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet] + CurChip; TempCOpt->Disabled = false; TempCOpt->EmuCore = 0x00; TempCOpt->SpecialFlags = 0x00; TempCOpt->ChnCnt = 0x00; TempCOpt->ChnMute1 = 0x00; TempCOpt->ChnMute2 = 0x00; TempCOpt->ChnMute3 = 0x00; TempCOpt->Panning = NULL; // Set up some important fields to prevent in_vgm from crashing // when clicking on Muting checkboxes after init. TempCAud->ChipType = 0xFF; TempCAud->ChipID = CurCSet; TempCAud->Paired = NULL; } ChipOpts[CurCSet].GameBoy.SpecialFlags = 0x0003; // default options, 0x8000 skips the option write and keeps NSFPlay's default values // TODO: Is this really necessary?? ChipOpts[CurCSet].NES.SpecialFlags = 0x8000 | (0x00 << 12) | (0x3B << 4) | (0x01 << 2) | (0x03 << 0); ChipOpts[CurCSet].SCSP.SpecialFlags = 0x0001; // bypass SCSP DSP TempCAud = CA_Paired[CurCSet]; for (CurChip = 0x00; CurChip < 0x03; CurChip ++, TempCAud ++) { TempCAud->ChipType = 0xFF; TempCAud->ChipID = CurCSet; TempCAud->Paired = NULL; } // currently the only chips with Panning support are // SN76496 and YM2413, it should be not a problem that it's hardcoded. TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet].SN76496; TempCOpt->ChnCnt = 0x04; TempCOpt->Panning = (INT16*)malloc(sizeof(INT16) * TempCOpt->ChnCnt); for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) TempCOpt->Panning[CurChn] = 0x00; TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet].YM2413; TempCOpt->ChnCnt = 0x0E; // 0x09 + 0x05 TempCOpt->Panning = (INT16*)malloc(sizeof(INT16) * TempCOpt->ChnCnt); for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) TempCOpt->Panning[CurChn] = 0x00; } for (CurChn = 0; CurChn < 8; CurChn ++) AppPaths[CurChn] = NULL; AppPaths[0] = ""; FileMode = 0xFF; PausePlay = false; #ifdef _DEBUG if (sizeof(CHIP_AUDIO) != sizeof(CAUD_ATTR) * CHIP_COUNT) { fprintf(stderr, "Fatal Error! ChipAudio structure invalid!\n"); getchar(); exit(-1); } if (sizeof(CHIPS_OPTION) != sizeof(CHIP_OPTS) * CHIP_COUNT) { fprintf(stderr, "Fatal Error! ChipOpts structure invalid!\n"); getchar(); exit(-1); } #endif return; } void VGMPlay_Init2(void) { // has to be called after the configuration is loaded if (FMPort) { #if defined(WIN32) && defined(_MSC_VER) __try { // should work well with WinXP Compatibility Mode _inp(FMPort); WINNT_MODE = false; } __except(EXCEPTION_EXECUTE_HANDLER) { WINNT_MODE = true; } #endif if (! OPL_MODE) // OPL not forced OPL_Hardware_Detecton(); if (! OPL_MODE) // no OPL chip found FMPort = 0x0000; // disable FM } if (FMPort) { // prepare FM Hardware Access and open MIDI Mixer #ifdef WIN32 #ifdef MIXER_MUTING mixerOpen(&hmixer, 0x00, 0x00, 0x00, 0x00); GetMixerControl(); #endif if (WINNT_MODE) { if (OpenPortTalk()) return; } #else //#ifndef WIN32 #ifdef MIXER_MUTING hmixer = open("/dev/mixer", O_RDWR); #endif if (OpenPortTalk()) return; #endif } StreamBufs[0x00] = (INT32*)malloc(SMPL_BUFSIZE * sizeof(INT32)); StreamBufs[0x01] = (INT32*)malloc(SMPL_BUFSIZE * sizeof(INT32)); if (CHIP_SAMPLE_RATE <= 0) CHIP_SAMPLE_RATE = SampleRate; PlayingMode = 0xFF; return; } void VGMPlay_Deinit(void) { UINT8 CurChip; UINT8 CurCSet; CHIP_OPTS* TempCOpt; if (FMPort) { #ifdef MIXER_MUTING #ifdef WIN32 mixerClose(hmixer); #else close(hmixer); #endif #endif } free(StreamBufs[0x00]); StreamBufs[0x00] = NULL; free(StreamBufs[0x01]); StreamBufs[0x01] = NULL; for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet] + CurChip; if (TempCOpt->Panning != NULL) { free(TempCOpt->Panning); TempCOpt->Panning = NULL; } } } return; } // Note: Caller must free the returned string. char* FindFile(const char* FileName) { char* FullName; char** CurPath; UINT32 NameLen; UINT32 PathLen; UINT32 FullLen; FILE* hFile; NameLen = strlen(FileName); //printf("Find File: %s\n", FileName); // go to end of the list + get size of largest path // (The first entry has the lowest priority.) PathLen = 0; CurPath = AppPaths; while(*CurPath != NULL) { FullLen = strlen(*CurPath); if (FullLen > PathLen) PathLen = FullLen; CurPath ++; } CurPath --; FullLen = PathLen + NameLen; FullName = (char*)malloc(FullLen + 1); hFile = NULL; for (; CurPath >= AppPaths; CurPath --) { strcpy(FullName, *CurPath); strcat(FullName, FileName); //printf("Trying path: %s\n", FullName); hFile = fopen(FullName, "r"); if (hFile != NULL) break; } if (hFile != NULL) { fclose(hFile); //printf("Success!\n"); return FullName; // The caller has to free the string. } else { free(FullName); //printf("Fail!\n"); return NULL; } } char* FindFile_List(const char** FileNameList) { char* FullName; const char** CurFile; char** CurPath; char* PathPtr; UINT32 NameLen; UINT32 PathLen; UINT32 FullLen; FILE* hFile; NameLen = 0; CurFile = FileNameList; while(*CurFile != NULL) { FullLen = strlen(*CurFile); if (FullLen > NameLen) NameLen = FullLen; CurFile ++; } // go to end of the list + get size of largest path // (The first entry has the lowest priority.) PathLen = 0; CurPath = AppPaths; while(*CurPath != NULL) { FullLen = strlen(*CurPath); if (FullLen > PathLen) PathLen = FullLen; CurPath ++; } CurPath --; FullLen = PathLen + NameLen; FullName = (char*)malloc(FullLen + 1); hFile = NULL; while(CurPath >= AppPaths) { strcpy(FullName, *CurPath); PathPtr = FullName + strlen(FullName); CurFile = FileNameList; while(*CurFile != NULL) { strcpy(PathPtr, *CurFile); //printf("Trying path: %s\n", FullName); hFile = fopen(FullName, "r"); if (hFile != NULL) break; CurFile ++; } if (hFile != NULL) break; CurPath --; } if (hFile != NULL) { fclose(hFile); //printf("Success!\n"); return FullName; // The caller has to free the string. } else { free(FullName); //printf("Fail!\n"); return NULL; } } INLINE UINT16 ReadLE16(const UINT8* Data) { // read 16-Bit Word (Little Endian/Intel Byte Order) #ifdef VGM_LITTLE_ENDIAN return *(UINT16*)Data; #else return (Data[0x01] << 8) | (Data[0x00] << 0); #endif } INLINE UINT16 ReadBE16(const UINT8* Data) { // read 16-Bit Word (Big Endian/Motorola Byte Order) #ifdef VGM_BIG_ENDIAN return *(UINT16*)Data; #else return (Data[0x00] << 8) | (Data[0x01] << 0); #endif } INLINE UINT32 ReadLE24(const UINT8* Data) { // read 24-Bit Word (Little Endian/Intel Byte Order) #ifdef VGM_LITTLE_ENDIAN return (*(UINT32*)Data) & 0x00FFFFFF; #else return (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); #endif } INLINE UINT32 ReadLE32(const UINT8* Data) { // read 32-Bit Word (Little Endian/Intel Byte Order) #ifdef VGM_LITTLE_ENDIAN return *(UINT32*)Data; #else return (Data[0x03] << 24) | (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); #endif } INLINE int gzgetLE16(gzFile hFile, UINT16* RetValue) { #ifdef VGM_LITTLE_ENDIAN return gzread(hFile, RetValue, 0x02); #else int RetVal; UINT8 Data[0x02]; RetVal = gzread(hFile, Data, 0x02); *RetValue = (Data[0x01] << 8) | (Data[0x00] << 0); return RetVal; #endif } INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue) { #ifdef VGM_LITTLE_ENDIAN return gzread(hFile, RetValue, 0x04); #else int RetVal; UINT8 Data[0x04]; RetVal = gzread(hFile, Data, 0x04); *RetValue = (Data[0x03] << 24) | (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); return RetVal; #endif } static UINT32 gcd(UINT32 x, UINT32 y) { UINT32 shift; UINT32 diff; // Thanks to Wikipedia for this algorithm // http://en.wikipedia.org/wiki/Binary_GCD_algorithm if (! x || ! y) return x | y; for (shift = 0; ((x | y) & 1) == 0; shift ++) { x >>= 1; y >>= 1; } while((x & 1) == 0) x >>= 1; do { while((y & 1) == 0) y >>= 1; if (x < y) { y -= x; } else { diff = x - y; x = y; y = diff; } y >>= 1; } while(y); return x << shift; } void PlayVGM(void) { UINT8 CurChip; UINT8 FMVal; INT32 TempSLng; if (PlayingMode != 0xFF) return; //PausePlay = false; FadePlay = false; MasterVol = 1.0f; ForceVGMExec = false; AutoStopSkip = false; FadeStart = 0; ForceVGMExec = true; PauseThread = true; // FM Check FMVal = 0x00; if (FMPort) { // check usage of devices that use the FM port, and ones that don't use it for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { if (! GetChipClock(&VGMHead, CurChip, NULL)) continue; // supported chips are: // SN76496 (0x00, special behaviour), YM2413 (0x01) // YM3812 (0x09), YM3526 (0x0A), Y8950 (0x0B), YMF262 (0x0C) if (CurChip == 0x00 || CurChip == 0x12) // The SN76496 and AY8910 have a special state because of FMVal |= 0x04; // bad FM emulation and fast software emulation. else if (CurChip == 0x01 || (CurChip >= 0x09 && CurChip <= 0x0C)) FMVal |= 0x02; else FMVal |= 0x01; } if (! FMForce) { if (FMVal & 0x01) // one or more software emulators used? FMVal &= ~0x02; // use only software emulation if (FMVal == 0x04) FMVal = 0x01; // FM SN76496 emulaton is bad else if ((FMVal & 0x05) == 0x05) FMVal &= ~0x04; // prefer software SN76496 instead of unsynced output } FMVal = (FMVal & ~0x04) | ((FMVal & 0x04) >> 1); } switch(FMVal) { case 0x00: case 0x01: PlayingMode = 0x00; // Normal Mode break; case 0x02: PlayingMode = 0x01; // FM only Mode break; case 0x03: PlayingMode = 0x02; // Normal/FM mixed Mode (NOT in sync, Hardware is a lot faster) //PlayingMode = 0x00; // Force Normal Mode until I get them in sync - Mixed Mode // sounds terrible break; } UseFM = (PlayingMode > 0x00); if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) TempSLng = VGMHead.bytVolumeModifier; else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) TempSLng = VOLUME_MODIF_WRAP - 0x100; else TempSLng = VGMHead.bytVolumeModifier - 0x100; VolumeLevelM = (float)(VolumeLevel * pow(2.0, TempSLng / (double)0x20)); if (UseFM) { if (FMVol > 0.0f) VolumeLevelM = FMVol; else if (FMVol < 0.0f) VolumeLevelM *= -FMVol; } FinalVol = VolumeLevelM; if (! VGMMaxLoop) { VGMMaxLoopM = 0x00; } else { TempSLng = (VGMMaxLoop * VGMHead.bytLoopModifier + 0x08) / 0x10 - VGMHead.bytLoopBase; VGMMaxLoopM = (TempSLng >= 0x01) ? TempSLng : 0x01; } if (! VGMPbRate || ! VGMHead.lngRate) { VGMPbRateMul = 1; VGMPbRateDiv = 1; } else { // I prefer small Multiplers and Dividers, as they're used very often TempSLng = gcd(VGMHead.lngRate, VGMPbRate); VGMPbRateMul = VGMHead.lngRate / TempSLng; VGMPbRateDiv = VGMPbRate / TempSLng; } VGMSmplRateMul = SampleRate * VGMPbRateMul; VGMSmplRateDiv = VGMSampleRate * VGMPbRateDiv; // same as above - to speed up the VGM <-> Playback calculation TempSLng = gcd(VGMSmplRateMul, VGMSmplRateDiv); VGMSmplRateMul /= TempSLng; VGMSmplRateDiv /= TempSLng; PlayingTime = 0; EndPlay = false; VGMPos = VGMHead.lngDataOffset; VGMSmplPos = 0; VGMSmplPlayed = 0; VGMEnd = false; VGMCurLoop = 0x00; PauseSmpls = (PauseTime * SampleRate + 500) / 1000; if (VGMPos >= VGMHead.lngEOFOffset) VGMEnd = true; #ifdef CONSOLE_MODE memset(CmdList, 0x00, 0x100 * sizeof(UINT8)); #endif if (! PauseEmulate) { switch(PlayingMode) { case 0x00: //PauseStream(PausePlay); break; case 0x01: //PauseThread = PausePlay; SetMuteControl(PausePlay); break; case 0x02: //PauseStream(PausePlay); SetMuteControl(PausePlay); break; } } Chips_GeneralActions(0x00); // Start chips // also does Reset (0x01), Muting Mask (0x10) and Panning (0x20) if (UseFM) { // TODO: get FirstInit working //if (! FirstInit) { StartSkipping(); // don't apply OPL Reset to make Track changes smooth AutoStopSkip = true; } open_real_fm(); } switch(PlayingMode) { case 0x00: // the application controls the playback thread break; case 0x01: // FM Playback needs an independent thread ResetPBTimer = false; if (StartThread()) { fprintf(stderr, "Error starting Playing Thread!\n"); return; } #ifdef CONSOLE_MODE PauseStream(true); #endif break; case 0x02: // like Mode 0x00, but Hardware is also controlled (not synced) break; } Last95Drum = 0xFFFF; Last95Freq = 0; Last95Max = 0xFFFF; IsVGMInit = true; Interpreting = false; InterpretFile(0); IsVGMInit = false; PauseThread = false; AutoStopSkip = true; ForceVGMExec = false; return; } void StopVGM(void) { if (PlayingMode == 0xFF) return; if (! PauseEmulate) { if (UseFM && PausePlay) SetMuteControl(false); } switch(PlayingMode) { case 0x00: break; case 0x01: StopThread(); /*if (SmoothTrackChange) { if (ThreadPauseEnable) { ThreadPauseConfrm = false; PauseThread = true; while(! ThreadPauseConfrm) Sleep(1); // Wait until the Thread is finished } }*/ break; case 0x02: break; } Chips_GeneralActions(0x02); // Stop chips if (UseFM) close_real_fm(); PlayingMode = 0xFF; return; } void RestartVGM(void) { if (PlayingMode == 0xFF || ! VGMSmplPlayed) return; RestartPlaying(); return; } void PauseVGM(bool Pause) { if (PlayingMode == 0xFF || Pause == PausePlay) return; // now uses a temporary variable to avoid gaps (especially with DAC Stream Controller) if (! PauseEmulate) { if (Pause && ThreadPauseEnable) { ThreadPauseConfrm = false; PauseThread = true; } switch(PlayingMode) { case 0x00: #ifdef CONSOLE_MODE PauseStream(Pause); #endif break; case 0x01: ThreadNoWait = false; SetMuteControl(Pause); break; case 0x02: #ifdef CONSOLE_MODE PauseStream(Pause); #endif SetMuteControl(Pause); break; } if (Pause && ThreadPauseEnable) { while(! ThreadPauseConfrm) Sleep(1); // Wait until the Thread is finished } PauseThread = Pause; } PausePlay = Pause; return; } void SeekVGM(bool Relative, INT32 PlayBkSamples) { INT32 Samples; UINT32 LoopSmpls; if (PlayingMode == 0xFF || (Relative && ! PlayBkSamples)) return; LoopSmpls = VGMCurLoop * SampleVGM2Pbk_I(VGMHead.lngLoopSamples); if (! Relative) Samples = PlayBkSamples - (LoopSmpls + VGMSmplPlayed); else Samples = PlayBkSamples; ThreadNoWait = false; PauseThread = true; if (UseFM) StartSkipping(); if (Samples < 0) { Samples = LoopSmpls + VGMSmplPlayed + Samples; if (Samples < 0) Samples = 0; RestartPlaying(); } ForceVGMExec = true; InterpretFile(Samples); ForceVGMExec = false; #ifdef CONSOLE_MODE if (FadePlay && FadeStart) FadeStart += Samples; #endif if (UseFM) StopSkipping(); PauseThread = PausePlay; return; } void RefreshMuting(void) { Chips_GeneralActions(0x10); // set muting mask return; } void RefreshPanning(void) { Chips_GeneralActions(0x20); // set panning return; } void RefreshPlaybackOptions(void) { INT32 TempVol; UINT8 CurChip; CHIP_OPTS* TempCOpt1; CHIP_OPTS* TempCOpt2; if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) TempVol = VGMHead.bytVolumeModifier; else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) TempVol = VOLUME_MODIF_WRAP - 0x100; else TempVol = VGMHead.bytVolumeModifier - 0x100; VolumeLevelM = (float)(VolumeLevel * pow(2.0, TempVol / (double)0x20)); if (UseFM) { if (FMVol > 0.0f) VolumeLevelM = FMVol; else if (FMVol < 0.0f) VolumeLevelM *= -FMVol; } FinalVol = VolumeLevelM * MasterVol * MasterVol; //PauseSmpls = (PauseTime * SampleRate + 500) / 1000; if (PlayingMode == 0xFF) { TempCOpt1 = (CHIP_OPTS*)&ChipOpts[0x00]; TempCOpt2 = (CHIP_OPTS*)&ChipOpts[0x01]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt1 ++, TempCOpt2 ++) { TempCOpt2->EmuCore = TempCOpt1->EmuCore; TempCOpt2->SpecialFlags = TempCOpt1->SpecialFlags; } } return; } UINT32 GetGZFileLength(const char* FileName) { FILE* hFile; UINT32 FileSize; hFile = fopen(FileName, "rb"); if (hFile == NULL) return 0xFFFFFFFF; FileSize = GetGZFileLength_Internal(hFile); fclose(hFile); return FileSize; } #ifndef NO_WCHAR_FILENAMES UINT32 GetGZFileLengthW(const wchar_t* FileName) { FILE* hFile; UINT32 FileSize; hFile = _wfopen(FileName, L"rb"); if (hFile == NULL) return 0xFFFFFFFF; FileSize = GetGZFileLength_Internal(hFile); fclose(hFile); return FileSize; } #endif static UINT32 GetGZFileLength_Internal(FILE* hFile) { UINT32 FileSize; UINT16 gzHead; size_t RetVal; RetVal = fread(&gzHead, 0x02, 0x01, hFile); if (RetVal >= 1) { gzHead = ReadBE16((UINT8*)&gzHead); if (gzHead != 0x1F8B) { RetVal = 0; // no .gz signature - treat as normal file } else { // .gz File fseek(hFile, -4, SEEK_END); // Note: In the error case it falls back to fseek/ftell. RetVal = fread(&FileSize, 0x04, 0x01, hFile); #ifndef VGM_LITTLE_ENDIAN FileSize = ReadLE32((UINT8*)&FileSize); #endif } } if (RetVal <= 0) { // normal file fseek(hFile, 0x00, SEEK_END); FileSize = ftell(hFile); } return FileSize; } bool OpenVGMFile(const char* FileName) { gzFile hFile; UINT32 FileSize; bool RetVal; FileSize = GetGZFileLength(FileName); hFile = gzopen(FileName, "rb"); if (hFile == NULL) return false; RetVal = OpenVGMFile_Internal(hFile, FileSize); gzclose(hFile); return RetVal; } #ifndef NO_WCHAR_FILENAMES bool OpenVGMFileW(const wchar_t* FileName) { gzFile hFile; UINT32 FileSize; bool RetVal; #if ZLIB_VERNUM < 0x1270 int fDesc; FileSize = GetGZFileLengthW(FileName); fDesc = _wopen(FileName, _O_RDONLY | _O_BINARY); hFile = gzdopen(fDesc, "rb"); if (hFile == NULL) { _close(fDesc); return false; } #else FileSize = GetGZFileLengthW(FileName); hFile = gzopen_w(FileName, "rb"); if (hFile == NULL) return false; #endif RetVal = OpenVGMFile_Internal(hFile, FileSize); gzclose(hFile); return RetVal; } #endif static bool OpenVGMFile_Internal(gzFile hFile, UINT32 FileSize) { UINT32 fccHeader; UINT32 CurPos; UINT32 HdrLimit; //gzseek(hFile, 0x00, SEEK_SET); gzrewind(hFile); gzgetLE32(hFile, &fccHeader); if (fccHeader != FCC_VGM) return false; if (FileMode != 0xFF) CloseVGMFile(); FileMode = 0x00; VGMDataLen = FileSize; gzseek(hFile, 0x00, SEEK_SET); //gzrewind(hFile); ReadVGMHeader(hFile, &VGMHead); if (VGMHead.fccVGM != FCC_VGM) { fprintf(stderr, "VGM signature matched on the first read, but not on the second one!\n"); fprintf(stderr, "This is a known zlib bug where gzseek fails. Please install a fixed zlib.\n"); return false; } VGMSampleRate = 44100; if (! VGMDataLen) VGMDataLen = VGMHead.lngEOFOffset; if (! VGMHead.lngEOFOffset || VGMHead.lngEOFOffset > VGMDataLen) { fprintf(stderr, "Warning! Invalid EOF Offset 0x%02X! (should be: 0x%02X)\n", VGMHead.lngEOFOffset, VGMDataLen); VGMHead.lngEOFOffset = VGMDataLen; } if (VGMHead.lngLoopOffset && ! VGMHead.lngLoopSamples) { // 0-Sample-Loops causes the program to hangs in the playback routine fprintf(stderr, "Warning! Ignored Zero-Sample-Loop!\n"); VGMHead.lngLoopOffset = 0x00000000; } if (VGMHead.lngDataOffset < 0x00000040) { fprintf(stderr, "Warning! Invalid Data Offset 0x%02X!\n", VGMHead.lngDataOffset); VGMHead.lngDataOffset = 0x00000040; } memset(&VGMHeadX, 0x00, sizeof(VGM_HDR_EXTRA)); memset(&VGMH_Extra, 0x00, sizeof(VGM_EXTRA)); // Read Data VGMDataLen = VGMHead.lngEOFOffset; VGMData = (UINT8*)malloc(VGMDataLen); if (VGMData == NULL) return false; //gzseek(hFile, 0x00, SEEK_SET); gzrewind(hFile); gzread(hFile, VGMData, VGMDataLen); // Read Extra Header Data if (VGMHead.lngExtraOffset) { UINT32* TempPtr; CurPos = VGMHead.lngExtraOffset; TempPtr = (UINT32*)&VGMHeadX; // Read Header Size VGMHeadX.DataSize = ReadLE32(&VGMData[CurPos]); if (VGMHeadX.DataSize > sizeof(VGM_HDR_EXTRA)) VGMHeadX.DataSize = sizeof(VGM_HDR_EXTRA); HdrLimit = CurPos + VGMHeadX.DataSize; CurPos += 0x04; TempPtr ++; // Read all relative offsets of this header and make them absolute. for (; CurPos < HdrLimit; CurPos += 0x04, TempPtr ++) { *TempPtr = ReadLE32(&VGMData[CurPos]); if (*TempPtr) *TempPtr += CurPos; } ReadChipExtraData32(VGMHeadX.Chp2ClkOffset, &VGMH_Extra.Clocks); ReadChipExtraData16(VGMHeadX.ChpVolOffset, &VGMH_Extra.Volumes); } // Read GD3 Tag HdrLimit = ReadGD3Tag(hFile, VGMHead.lngGD3Offset, &VGMTag); if (HdrLimit == 0x10) { VGMHead.lngGD3Offset = 0x00000000; //return false; } if (! VGMHead.lngGD3Offset) { // replace all NULL pointers with empty strings VGMTag.strTrackNameE = MakeEmptyWStr(); VGMTag.strTrackNameJ = MakeEmptyWStr(); VGMTag.strGameNameE = MakeEmptyWStr(); VGMTag.strGameNameJ = MakeEmptyWStr(); VGMTag.strSystemNameE = MakeEmptyWStr(); VGMTag.strSystemNameJ = MakeEmptyWStr(); VGMTag.strAuthorNameE = MakeEmptyWStr(); VGMTag.strAuthorNameJ = MakeEmptyWStr(); VGMTag.strReleaseDate = MakeEmptyWStr(); VGMTag.strCreator = MakeEmptyWStr(); VGMTag.strNotes = MakeEmptyWStr(); } return true; } static void ReadVGMHeader(gzFile hFile, VGM_HEADER* RetVGMHead) { VGM_HEADER CurHead; UINT32 CurPos; UINT32 HdrLimit; gzread(hFile, &CurHead, sizeof(VGM_HEADER)); #ifndef VGM_LITTLE_ENDIAN { UINT8* TempPtr; // Warning: Lots of pointer casting ahead! for (CurPos = 0x00; CurPos < sizeof(VGM_HEADER); CurPos += 0x04) { TempPtr = (UINT8*)&CurHead + CurPos; switch(CurPos) { case 0x28: // 0x28 [16-bit] SN76496 Feedback Mask // 0x2A [ 8-bit] SN76496 Shift Register Width // 0x2B [ 8-bit] SN76496 Flags *(UINT16*)TempPtr = ReadLE16(TempPtr); break; case 0x78: // 78-7B [8-bit] AY8910 Type/Flags case 0x7C: // 7C-7F [8-bit] Volume/Loop Modifiers case 0x94: // 94-97 [8-bit] various flags break; default: // everything else is 32-bit *(UINT32*)TempPtr = ReadLE32(TempPtr); break; } } } #endif // Header preperations if (CurHead.lngVersion < 0x00000101) { CurHead.lngRate = 0; } if (CurHead.lngVersion < 0x00000110) { CurHead.shtPSG_Feedback = 0x0000; CurHead.bytPSG_SRWidth = 0x00; CurHead.lngHzYM2612 = CurHead.lngHzYM2413; CurHead.lngHzYM2151 = CurHead.lngHzYM2413; } if (CurHead.lngVersion < 0x00000150) { CurHead.lngDataOffset = 0x00000000; // If I would aim to be very strict, I would uncomment these few lines, // but I sometimes use v1.51 Flags with v1.50 for better compatibility. // (Some hyper-strict players refuse to play v1.51 files, even if there's // no new chip used.) //} //if (CurHead.lngVersion < 0x00000151) //{ CurHead.bytPSG_Flags = 0x00; CurHead.lngHzSPCM = 0x0000; CurHead.lngSPCMIntf = 0x00000000; // all others are zeroed by memset } if (CurHead.lngHzPSG) { if (! CurHead.shtPSG_Feedback) CurHead.shtPSG_Feedback = 0x0009; if (! CurHead.bytPSG_SRWidth) CurHead.bytPSG_SRWidth = 0x10; } // relative -> absolute addresses if (CurHead.lngEOFOffset) CurHead.lngEOFOffset += 0x00000004; if (CurHead.lngGD3Offset) CurHead.lngGD3Offset += 0x00000014; if (CurHead.lngLoopOffset) CurHead.lngLoopOffset += 0x0000001C; if (CurHead.lngVersion < 0x00000150) CurHead.lngDataOffset = 0x0000000C; //if (CurHead.lngDataOffset < 0x0000000C) // CurHead.lngDataOffset = 0x0000000C; if (CurHead.lngDataOffset) CurHead.lngDataOffset += 0x00000034; CurPos = CurHead.lngDataOffset; // should actually check v1.51 (first real usage of DataOffset) // v1.50 is checked to support things like the Volume Modifiers in v1.50 files if (CurHead.lngVersion < 0x00000150 /*0x00000151*/) CurPos = 0x40; if (! CurPos) CurPos = 0x40; HdrLimit = sizeof(VGM_HEADER); if (HdrLimit > CurPos) memset((UINT8*)&CurHead + CurPos, 0x00, HdrLimit - CurPos); if (! CurHead.bytLoopModifier) CurHead.bytLoopModifier = 0x10; if (CurHead.lngExtraOffset) { CurHead.lngExtraOffset += 0xBC; CurPos = CurHead.lngExtraOffset; if (CurPos < HdrLimit) memset((UINT8*)&CurHead + CurPos, 0x00, HdrLimit - CurPos); } if (CurHead.lngGD3Offset >= CurHead.lngEOFOffset) CurHead.lngGD3Offset = 0x00; if (CurHead.lngLoopOffset >= CurHead.lngEOFOffset) CurHead.lngLoopOffset = 0x00; if (CurHead.lngDataOffset >= CurHead.lngEOFOffset) CurHead.lngDataOffset = 0x40; if (CurHead.lngExtraOffset >= CurHead.lngEOFOffset) CurHead.lngExtraOffset = 0x00; *RetVGMHead = CurHead; return; } static UINT8 ReadGD3Tag(gzFile hFile, UINT32 GD3Offset, GD3_TAG* RetGD3Tag) { UINT32 CurPos; UINT32 TempLng; UINT8 ResVal; ResVal = 0x00; // Read GD3 Tag if (GD3Offset) { gzseek(hFile, GD3Offset, SEEK_SET); gzgetLE32(hFile, &TempLng); if (TempLng != FCC_GD3) { GD3Offset = 0x00000000; ResVal = 0x10; // invalid GD3 offset } } if (RetGD3Tag == NULL) return ResVal; if (! GD3Offset) { RetGD3Tag->fccGD3 = 0x00000000; RetGD3Tag->lngVersion = 0x00000000; RetGD3Tag->lngTagLength = 0x00000000; RetGD3Tag->strTrackNameE = NULL; RetGD3Tag->strTrackNameJ = NULL; RetGD3Tag->strGameNameE = NULL; RetGD3Tag->strGameNameJ = NULL; RetGD3Tag->strSystemNameE = NULL; RetGD3Tag->strSystemNameJ = NULL; RetGD3Tag->strAuthorNameE = NULL; RetGD3Tag->strAuthorNameJ = NULL; RetGD3Tag->strReleaseDate = NULL; RetGD3Tag->strCreator = NULL; RetGD3Tag->strNotes = NULL; } else { //CurPos = GD3Offset; //gzseek(hFile, CurPos, SEEK_SET); //CurPos += gzgetLE32(hFile, &RetGD3Tag->fccGD3); CurPos = GD3Offset + 0x04; // Save some back seeking, yay! RetGD3Tag->fccGD3 = TempLng; // (That costs lots of CPU in .gz files.) CurPos += gzgetLE32(hFile, &RetGD3Tag->lngVersion); CurPos += gzgetLE32(hFile, &RetGD3Tag->lngTagLength); TempLng = CurPos + RetGD3Tag->lngTagLength; RetGD3Tag->strTrackNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strTrackNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strGameNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strGameNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strSystemNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strSystemNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strAuthorNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strAuthorNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strReleaseDate = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strCreator = ReadWStrFromFile(hFile, &CurPos, TempLng); RetGD3Tag->strNotes = ReadWStrFromFile(hFile, &CurPos, TempLng); } return ResVal; } static void ReadChipExtraData32(UINT32 StartOffset, VGMX_CHP_EXTRA32* ChpExtra) { UINT32 CurPos; UINT8 CurChp; VGMX_CHIP_DATA32* TempCD; if (! StartOffset || StartOffset >= VGMDataLen) { ChpExtra->ChipCnt = 0x00; ChpExtra->CCData = NULL; return; } CurPos = StartOffset; ChpExtra->ChipCnt = VGMData[CurPos]; if (ChpExtra->ChipCnt) ChpExtra->CCData = (VGMX_CHIP_DATA32*)malloc(sizeof(VGMX_CHIP_DATA32) * ChpExtra->ChipCnt); else ChpExtra->CCData = NULL; CurPos ++; for (CurChp = 0x00; CurChp < ChpExtra->ChipCnt; CurChp ++) { TempCD = &ChpExtra->CCData[CurChp]; TempCD->Type = VGMData[CurPos + 0x00]; TempCD->Data = ReadLE32(&VGMData[CurPos + 0x01]); CurPos += 0x05; } return; } static void ReadChipExtraData16(UINT32 StartOffset, VGMX_CHP_EXTRA16* ChpExtra) { UINT32 CurPos; UINT8 CurChp; VGMX_CHIP_DATA16* TempCD; if (! StartOffset || StartOffset >= VGMDataLen) { ChpExtra->ChipCnt = 0x00; ChpExtra->CCData = NULL; return; } CurPos = StartOffset; ChpExtra->ChipCnt = VGMData[CurPos]; if (ChpExtra->ChipCnt) ChpExtra->CCData = (VGMX_CHIP_DATA16*)malloc(sizeof(VGMX_CHIP_DATA16) * ChpExtra->ChipCnt); else ChpExtra->CCData = NULL; CurPos ++; for (CurChp = 0x00; CurChp < ChpExtra->ChipCnt; CurChp ++) { TempCD = &ChpExtra->CCData[CurChp]; TempCD->Type = VGMData[CurPos + 0x00]; TempCD->Flags = VGMData[CurPos + 0x01]; TempCD->Data = ReadLE16(&VGMData[CurPos + 0x02]); CurPos += 0x04; } return; } void CloseVGMFile(void) { if (FileMode == 0xFF) return; VGMHead.fccVGM = 0x00; free(VGMH_Extra.Clocks.CCData); VGMH_Extra.Clocks.CCData = NULL; free(VGMH_Extra.Volumes.CCData); VGMH_Extra.Volumes.CCData = NULL; free(VGMData); VGMData = NULL; if (FileMode == 0x00) FreeGD3Tag(&VGMTag); FileMode = 0xFF; return; } void FreeGD3Tag(GD3_TAG* TagData) { if (TagData == NULL) return; TagData->fccGD3 = 0x00; free(TagData->strTrackNameE); TagData->strTrackNameE = NULL; free(TagData->strTrackNameJ); TagData->strTrackNameJ = NULL; free(TagData->strGameNameE); TagData->strGameNameE = NULL; free(TagData->strGameNameJ); TagData->strGameNameJ = NULL; free(TagData->strSystemNameE); TagData->strSystemNameE = NULL; free(TagData->strSystemNameJ); TagData->strSystemNameJ = NULL; free(TagData->strAuthorNameE); TagData->strAuthorNameE = NULL; free(TagData->strAuthorNameJ); TagData->strAuthorNameJ = NULL; free(TagData->strReleaseDate); TagData->strReleaseDate = NULL; free(TagData->strCreator); TagData->strCreator = NULL; free(TagData->strNotes); TagData->strNotes = NULL; return; } static wchar_t* MakeEmptyWStr(void) { wchar_t* Str; Str = (wchar_t*)malloc(0x01 * sizeof(wchar_t)); Str[0x00] = L'\0'; return Str; } static wchar_t* ReadWStrFromFile(gzFile hFile, UINT32* FilePos, UINT32 EOFPos) { // Note: Works with Windows (16-bit wchar_t) as well as Linux (32-bit wchar_t) UINT32 CurPos; wchar_t* TextStr; wchar_t* TempStr; UINT32 StrLen; UINT16 UnicodeChr; CurPos = *FilePos; if (CurPos >= EOFPos) return NULL; TextStr = (wchar_t*)malloc((EOFPos - CurPos) / 0x02 * sizeof(wchar_t)); if (TextStr == NULL) return NULL; if ((UINT32)gztell(hFile) != CurPos) gzseek(hFile, CurPos, SEEK_SET); TempStr = TextStr - 1; StrLen = 0x00; do { TempStr ++; gzgetLE16(hFile, &UnicodeChr); *TempStr = (wchar_t)UnicodeChr; CurPos += 0x02; StrLen ++; if (CurPos >= EOFPos) { *TempStr = L'\0'; break; } } while(*TempStr != L'\0'); TextStr = (wchar_t*)realloc(TextStr, StrLen * sizeof(wchar_t)); *FilePos = CurPos; return TextStr; } UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) { gzFile hFile; UINT32 FileSize; UINT32 RetVal; FileSize = GetGZFileLength(FileName); hFile = gzopen(FileName, "rb"); if (hFile == NULL) return 0x00; RetVal = GetVGMFileInfo_Internal(hFile, FileSize, RetVGMHead, RetGD3Tag); gzclose(hFile); return RetVal; } #ifndef NO_WCHAR_FILENAMES UINT32 GetVGMFileInfoW(const wchar_t* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) { gzFile hFile; UINT32 FileSize; UINT32 RetVal; #if ZLIB_VERNUM < 0x1270 int fDesc; FileSize = GetGZFileLengthW(FileName); fDesc = _wopen(FileName, _O_RDONLY | _O_BINARY); hFile = gzdopen(fDesc, "rb"); if (hFile == NULL) { _close(fDesc); return 0x00; } #else FileSize = GetGZFileLengthW(FileName); hFile = gzopen_w(FileName, "rb"); if (hFile == NULL) return 0x00; #endif RetVal = GetVGMFileInfo_Internal(hFile, FileSize, RetVGMHead, RetGD3Tag); gzclose(hFile); return RetVal; } #endif static UINT32 GetVGMFileInfo_Internal(gzFile hFile, UINT32 FileSize, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) { // this is a copy-and-paste from OpenVGM, just a little stripped UINT32 fccHeader; UINT32 TempLng; VGM_HEADER TempHead; //gzseek(hFile, 0x00, SEEK_SET); gzrewind(hFile); gzgetLE32(hFile, &fccHeader); if (fccHeader != FCC_VGM) return 0x00; if (RetVGMHead == NULL && RetGD3Tag == NULL) return FileSize; //gzseek(hFile, 0x00, SEEK_SET); gzrewind(hFile); ReadVGMHeader(hFile, &TempHead); if (! TempHead.lngEOFOffset || TempHead.lngEOFOffset > FileSize) TempHead.lngEOFOffset = FileSize; if (TempHead.lngDataOffset < 0x00000040) TempHead.lngDataOffset = 0x00000040; /*if (TempHead.lngGD3Offset) { gzseek(hFile, TempHead.lngGD3Offset, SEEK_SET); gzgetLE32(hFile, &fccHeader); if (fccHeader != FCC_GD3) TempHead.lngGD3Offset = 0x00000000; //return 0x00; }*/ if (RetVGMHead != NULL) *RetVGMHead = TempHead; // Read GD3 Tag if (RetGD3Tag != NULL) TempLng = ReadGD3Tag(hFile, TempHead.lngGD3Offset, RetGD3Tag); return FileSize; } INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator) { return (UINT32)((Number * Numerator + Denominator / 2) / Denominator); } UINT32 CalcSampleMSec(UINT64 Value, UINT8 Mode) { // Mode: // Bit 0 (01): Calculation Mode // 0 - Sample2MSec // 1 - MSec2Sample // Bit 1 (02): Calculation Samlpe Rate // 0 - current playback rate // 1 - 44.1 KHz (VGM native) UINT32 SmplRate; UINT32 PbMul; UINT32 PbDiv; UINT32 RetVal; if (! (Mode & 0x02)) { SmplRate = SampleRate; PbMul = 1; PbDiv = 1; } else { SmplRate = VGMSampleRate; PbMul = VGMPbRateMul; PbDiv = VGMPbRateDiv; } switch(Mode & 0x01) { case 0x00: RetVal = MulDivRound(Value, (UINT64)1000 * PbMul, (UINT64)SmplRate * PbDiv); break; case 0x01: RetVal = MulDivRound(Value, (UINT64)SmplRate * PbDiv, (UINT64)1000 * PbMul); break; } return RetVal; } UINT32 CalcSampleMSecExt(UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead) { // Note: This function was NOT tested with non-VGM formats! // Mode: see function above UINT32 SmplRate; UINT32 PbMul; UINT32 PbDiv; UINT32 RetVal; if (! (Mode & 0x02)) { SmplRate = SampleRate; PbMul = 1; PbDiv = 1; } else { // TODO: make it work for non-VGM formats // (i.e. get VGMSampleRate information from FileHead) // // But currently GetVGMFileInfo doesn't support them, it doesn't matter either way SmplRate = 44100; if (! VGMPbRate || ! FileHead->lngRate) { PbMul = 1; PbDiv = 1; } else { PbMul = FileHead->lngRate; PbDiv = VGMPbRate; } } switch(Mode & 0x01) { case 0x00: RetVal = MulDivRound(Value, 1000 * PbMul, SmplRate * PbDiv); break; case 0x01: RetVal = MulDivRound(Value, SmplRate * PbDiv, 1000 * PbMul); break; } return RetVal; } #if 0 static UINT32 EncryptChipName(void* DstBuf, const void* SrcBuf, UINT32 Length) { // using nineko's awesome encryption algorithm // http://forums.sonicretro.org/index.php?showtopic=25300 // based on C code by sasuke const UINT8* SrcPos; UINT8* DstPos; UINT32 CurPos; UINT8 CryptShift; // Src Bit/Dst Byte UINT8 PlainShift; // Src Byte/Dst Bit if (Length & 0x07) return 0x00; // Length MUST be a multiple of 8 SrcPos = (const UINT8*)SrcBuf; DstPos = (UINT8*)DstBuf; for (CurPos = 0; CurPos < Length; CurPos += 8, SrcPos += 8, DstPos += 8) { for (CryptShift = 0; CryptShift < 8; CryptShift ++) { DstPos[CryptShift] = 0x00; for (PlainShift = 0; PlainShift < 8; PlainShift ++) { if (SrcPos[PlainShift] & (1 << CryptShift)) DstPos[CryptShift] |= (1 << PlainShift); } } } return Length; } #endif const char* GetChipName(UINT8 ChipID) { const char* CHIP_STRS[CHIP_COUNT] = { "SN76496", "YM2413", "YM2612", "YM2151", "SegaPCM", "RF5C68", "YM2203", "YM2608", "YM2610", "YM3812", "YM3526", "Y8950", "YMF262", "YMF278B", "YMF271", "YMZ280B", "RF5C164", "PWM", "AY8910", "GameBoy", "NES APU", "YMW258", "uPD7759", "OKIM6258", "OKIM6295", "K051649", "K054539", "HuC6280", "C140", "K053260", "Pokey", "QSound", "SCSP", "WSwan", "VSU", "SAA1099", "ES5503", "ES5506", "X1-010", "C352", "GA20"}; /*if (ChipID == 0x21) { static char TempStr[0x08]; UINT32 TempData[2]; //EncryptChipName(TempData, "WSwan", 0x08); TempData[0] = 0x1015170F; TempData[1] = 0x001F1C07; EncryptChipName(TempStr, TempData, 0x08); return TempStr; // "WSwan" }*/ if (ChipID < CHIP_COUNT) return CHIP_STRS[ChipID]; else return NULL; } const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType) { const char* RetStr; if ((ChipID & 0x7F) >= CHIP_COUNT) return NULL; RetStr = NULL; switch(ChipID & 0x7F) { case 0x00: if (! (ChipID & 0x80)) { switch(SubType) { case 0x01: RetStr = "SN76489"; break; case 0x02: RetStr = "SN76489A"; break; case 0x03: RetStr = "SN76494"; break; case 0x04: RetStr = "SN76496"; break; case 0x05: RetStr = "SN94624"; break; case 0x06: RetStr = "SEGA PSG"; break; case 0x07: RetStr = "NCR8496"; break; case 0x08: RetStr = "PSSJ-3"; break; default: RetStr = "SN76496"; break; } } else { RetStr = "T6W28"; } break; case 0x01: if (ChipID & 0x80) RetStr = "VRC7"; break; case 0x02: if (! (ChipID & 0x80)) RetStr = "YM2612"; else RetStr = "YM3438"; break; case 0x04: RetStr = "Sega PCM"; break; case 0x08: if (! (ChipID & 0x80)) RetStr = "YM2610"; else RetStr = "YM2610B"; break; case 0x12: // AY8910 switch(SubType) { case 0x00: RetStr = "AY-3-8910"; break; case 0x01: RetStr = "AY-3-8912"; break; case 0x02: RetStr = "AY-3-8913"; break; case 0x03: RetStr = "AY8930"; break; case 0x04: RetStr = "AY-3-8914"; break; case 0x10: RetStr = "YM2149"; break; case 0x11: RetStr = "YM3439"; break; case 0x12: RetStr = "YMZ284"; break; case 0x13: RetStr = "YMZ294"; break; } break; case 0x13: RetStr = "GB DMG"; break; case 0x14: if (! (ChipID & 0x80)) RetStr = "NES APU"; else RetStr = "NES APU + FDS"; break; case 0x19: if (! (ChipID & 0x80)) RetStr = "K051649"; else RetStr = "K052539"; break; case 0x1C: switch(SubType) { case 0x00: case 0x01: RetStr = "C140"; break; case 0x02: RetStr = "C219"; break; } break; case 0x21: RetStr = "WonderSwan"; break; case 0x22: RetStr = "VSU-VUE"; break; case 0x25: if (! (ChipID & 0x80)) RetStr = "ES5505"; else RetStr = "ES5506"; break; case 0x28: RetStr = "Irem GA20"; break; } // catch all default-cases if (RetStr == NULL) RetStr = GetChipName(ChipID & 0x7F); return RetStr; } UINT32 GetChipClock(VGM_HEADER* FileHead, UINT8 ChipID, UINT8* RetSubType) { UINT32 Clock; UINT8 SubType; UINT8 CurChp; bool AllowBit31; SubType = 0x00; AllowBit31 = 0x00; switch(ChipID & 0x7F) { case 0x00: Clock = FileHead->lngHzPSG; AllowBit31 = 0x01; // T6W28 Mode if (RetSubType != NULL && ! (Clock & 0x80000000)) // The T6W28 is handled differently. { switch(FileHead->bytPSG_SRWidth) { case 0x0F: // 0x4000 if (FileHead->bytPSG_Flags & 0x08) // Clock Divider == 1? SubType = 0x05; // SN94624 else SubType = 0x01; // SN76489 break; case 0x10: // 0x8000 if (FileHead->shtPSG_Feedback == 0x0009) SubType = 0x06; // SEGA PSG else if (FileHead->shtPSG_Feedback == 0x0022) { if (FileHead->bytPSG_Flags & 0x10) // if Tandy noise mode enabled SubType = (FileHead->bytPSG_Flags & 0x02) ? 0x07 : 0x08; // NCR8496 / PSSJ-3 else SubType = 0x07; // NCR8496 } break; case 0x11: // 0x10000 if (FileHead->bytPSG_Flags & 0x08) // Clock Divider == 1? SubType = 0x03; // SN76494 else SubType = 0x02; // SN76489A/SN76496 break; } /* FbMask Noise Taps Negate Stereo Dv Freq0 Fb SR Flags 01 SN76489 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE 03 0F 07 (02|04|00|01) [unverified] 02 SN76489A 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE 0C 11 05 (00|04|00|01) 03 SN76494 0x10000, 0x04, 0x08, FALSE, FALSE, 1, TRUE 0C 11 0D (00|04|08|01) 04 SN76496 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE 0C 11 05 (00|04|00|01) [same as SN76489A] 05 SN94624 0x4000, 0x01, 0x02, TRUE, FALSE, 1, TRUE 03 0F 0F (02|04|08|01) [unverified, SN76489A without /8] 06 GameGear PSG 0x8000, 0x01, 0x08, TRUE, TRUE, 8, FALSE 09 10 02 (02|00|00|00) 06 SEGA VDP PSG 0x8000, 0x01, 0x08, TRUE, FALSE, 8, FALSE 09 10 06 (02|04|00|00) 07 NCR8496 0x8000, 0x02, 0x20, TRUE, FALSE, 8, TRUE 22 10 07 (02|04|00|01) 08 PSSJ-3 0x8000, 0x02, 0x20, FALSE, FALSE, 8, TRUE 22 10 05 (00|04|00|01) 01 U8106 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE 03 0F 07 (02|04|00|01) [unverified, same as SN76489] 02 Y2404 0x10000, 0x04, 0x08, FALSE, FALSE; 8, TRUE 0C 11 05 (00|04|00|01) [unverified, same as SN76489A] -- T6W28 0x10000, 0x04, 0x08, ????, FALSE, 8, ???? 0C 11 ?? (??|??|00|01) [It IS stereo, but not in GameGear way]. */ } break; case 0x01: Clock = FileHead->lngHzYM2413; AllowBit31 = 0x01; // VRC7 Mode break; case 0x02: Clock = FileHead->lngHzYM2612; AllowBit31 = 0x01; // YM3438 Mode break; case 0x03: Clock = FileHead->lngHzYM2151; break; case 0x04: Clock = FileHead->lngHzSPCM; break; case 0x05: if (ChipID & 0x80) return 0; Clock = FileHead->lngHzRF5C68; break; case 0x06: Clock = FileHead->lngHzYM2203; break; case 0x07: Clock = FileHead->lngHzYM2608; break; case 0x08: Clock = FileHead->lngHzYM2610; AllowBit31 = 0x01; // YM2610B Mode break; case 0x09: Clock = FileHead->lngHzYM3812; AllowBit31 = 0x01; // Dual OPL2, panned to the L/R speakers break; case 0x0A: Clock = FileHead->lngHzYM3526; break; case 0x0B: Clock = FileHead->lngHzY8950; break; case 0x0C: Clock = FileHead->lngHzYMF262; break; case 0x0D: Clock = FileHead->lngHzYMF278B; break; case 0x0E: Clock = FileHead->lngHzYMF271; break; case 0x0F: Clock = FileHead->lngHzYMZ280B; break; case 0x10: if (ChipID & 0x80) return 0; Clock = FileHead->lngHzRF5C164; AllowBit31 = 0x01; // hack for Cosmic Fantasy Stories break; case 0x11: if (ChipID & 0x80) return 0; Clock = FileHead->lngHzPWM; break; case 0x12: Clock = FileHead->lngHzAY8910; SubType = FileHead->bytAYType; break; case 0x13: Clock = FileHead->lngHzGBDMG; break; case 0x14: Clock = FileHead->lngHzNESAPU; AllowBit31 = 0x01; // FDS Enable break; case 0x15: Clock = FileHead->lngHzMultiPCM; break; case 0x16: Clock = FileHead->lngHzUPD7759; AllowBit31 = 0x01; // Master/Slave Bit break; case 0x17: Clock = FileHead->lngHzOKIM6258; break; case 0x18: Clock = FileHead->lngHzOKIM6295; AllowBit31 = 0x01; // Pin 7 State break; case 0x19: Clock = FileHead->lngHzK051649; AllowBit31 = 0x01; // SCC/SCC+ Bit break; case 0x1A: Clock = FileHead->lngHzK054539; break; case 0x1B: Clock = FileHead->lngHzHuC6280; break; case 0x1C: Clock = FileHead->lngHzC140; SubType = FileHead->bytC140Type; break; case 0x1D: Clock = FileHead->lngHzK053260; break; case 0x1E: Clock = FileHead->lngHzPokey; break; case 0x1F: if (ChipID & 0x80) return 0; Clock = FileHead->lngHzQSound; break; case 0x20: Clock = FileHead->lngHzSCSP; break; case 0x21: Clock = FileHead->lngHzWSwan; break; case 0x22: Clock = FileHead->lngHzVSU; break; case 0x23: Clock = FileHead->lngHzSAA1099; break; case 0x24: Clock = FileHead->lngHzES5503; break; case 0x25: Clock = FileHead->lngHzES5506; AllowBit31 = 0x01; // ES5505/5506 switch break; case 0x26: Clock = FileHead->lngHzX1_010; break; case 0x27: Clock = FileHead->lngHzC352; AllowBit31 = 0x01; // disable rear channels break; case 0x28: Clock = FileHead->lngHzGA20; break; default: return 0; } if (ChipID & 0x80) { VGMX_CHP_EXTRA32* TempCX; if (! (Clock & 0x40000000)) return 0; ChipID &= 0x7F; TempCX = &VGMH_Extra.Clocks; for (CurChp = 0x00; CurChp < TempCX->ChipCnt; CurChp ++) { if (TempCX->CCData[CurChp].Type == ChipID) { if (TempCX->CCData[CurChp].Data) Clock = TempCX->CCData[CurChp].Data; break; } } } if (RetSubType != NULL) *RetSubType = SubType; if (AllowBit31) return Clock & 0xBFFFFFFF; else return Clock & 0x3FFFFFFF; } static UINT16 GetChipVolume(VGM_HEADER* FileHead, UINT8 ChipID, UINT8 ChipNum, UINT8 ChipCnt) { // ChipID: ID of Chip // Bit 7 - Is Paired Chip // ChipNum: chip number (0 - first chip, 1 - second chip) // ChipCnt: chip volume divider (number of used chips) const UINT16 CHIP_VOLS[CHIP_COUNT] = { 0x80, 0x200/*0x155*/, 0x100, 0x100, 0x180, 0xB0, 0x100, 0x80, // 00-07 0x80, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x98, // 08-0F 0x80, 0xE0/*0xCD*/, 0x100, 0xC0, 0x100, 0x40, 0x11E, 0x1C0, // 10-17 0x100/*110*/, 0xA0, 0x100, 0x100, 0x100, 0xB3, 0x100, 0x100, // 18-1F 0x20, 0x100, 0x100, 0x100, 0x40, 0x20, 0x100, 0x40, // 20-27 0x280}; UINT16 Volume; UINT8 CurChp; VGMX_CHP_EXTRA16* TempCX; VGMX_CHIP_DATA16* TempCD; Volume = CHIP_VOLS[ChipID & 0x7F]; switch(ChipID) { case 0x00: // SN76496 // if T6W28, set Volume Divider to 01 if (GetChipClock(&VGMHead, (ChipID << 7) | ChipID, NULL) & 0x80000000) { // The T6W28 consists of 2 "half" chips. ChipNum = 0x01; ChipCnt = 0x01; } break; case 0x18: // OKIM6295 // CP System 1 patch if (VGMTag.strSystemNameE != NULL && ! wcsncmp(VGMTag.strSystemNameE, L"CP", 0x02)) Volume = 110; break; case 0x86: // YM2203's AY Volume /= 2; break; case 0x87: // YM2608's AY // The YM2608 outputs twice as loud as the YM2203 here. //Volume *= 1; break; case 0x88: // YM2610's AY //Volume *= 1; break; } if (ChipCnt > 1) Volume /= ChipCnt; TempCX = &VGMH_Extra.Volumes; TempCD = TempCX->CCData; for (CurChp = 0x00; CurChp < TempCX->ChipCnt; CurChp ++, TempCD ++) { if (TempCD->Type == ChipID && (TempCD->Flags & 0x01) == ChipNum) { // Bit 15 - absolute/relative volume // 0 - absolute // 1 - relative (0x0100 = 1.0, 0x80 = 0.5, etc.) if (TempCD->Data & 0x8000) Volume = (Volume * (TempCD->Data & 0x7FFF) + 0x80) >> 8; else { Volume = TempCD->Data; if ((ChipID & 0x80) && DoubleSSGVol) Volume *= 2; } break; } } return Volume; } static void RestartPlaying(void) { bool OldPThread; OldPThread = PauseThread; if (ThreadPauseEnable) { ThreadNoWait = false; ThreadPauseConfrm = false; PauseThread = true; while(! ThreadPauseConfrm) Sleep(1); // Wait until the Thread is finished } Interpreting = true; // Avoid any Thread-Call VGMPos = VGMHead.lngDataOffset; VGMSmplPos = 0; VGMSmplPlayed = 0; VGMEnd = false; EndPlay = false; VGMCurLoop = 0x00; PauseSmpls = (PauseTime * SampleRate + 500) / 1000; Chips_GeneralActions(0x01); // Reset Chips // also does Muting Mask (0x10) and Panning (0x20) if (UseFM) { open_real_fm(); // reset OPL chip and reload settings StartSkipping(); AutoStopSkip = true; } Last95Drum = 0xFFFF; Last95Freq = 0; Interpreting = false; ForceVGMExec = true; IsVGMInit = true; InterpretFile(0); IsVGMInit = false; ForceVGMExec = false; #ifndef CONSOLE_MODE FadePlay = false; MasterVol = 1.0f; FadeStart = 0; FinalVol = VolumeLevelM; PlayingTime = 0; #endif PauseThread = OldPThread; return; } static void Chips_GeneralActions(UINT8 Mode) { UINT32 AbsVol; //UINT16 ChipVol; CAUD_ATTR* CAA; CHIP_OPTS* COpt; UINT8 ChipCnt; UINT8 CurChip; UINT8 CurCSet; // Chip Set UINT32 MaskVal; UINT32 ChipClk; switch(Mode) { case 0x00: // Start Chips for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) { CAA->SmpRate = 0x00; CAA->Volume = 0x00; CAA->ChipType = 0xFF; CAA->ChipID = CurCSet; CAA->Resampler = 0x00; CAA->StreamUpdate = &null_update; CAA->Paired = NULL; } CAA = CA_Paired[CurCSet]; for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) { CAA->SmpRate = 0x00; CAA->Volume = 0x00; CAA->ChipType = 0xFF; CAA->ChipID = CurCSet; CAA->Resampler = 0x00; CAA->StreamUpdate = &null_update; CAA->Paired = NULL; } } // Initialize Sound Chips AbsVol = 0x00; if (VGMHead.lngHzPSG) { //ChipVol = UseFM ? 0x00 : 0x80; sn764xx_set_emu_core(ChipOpts[0x00].SN76496.EmuCore); ChipOpts[0x01].SN76496.EmuCore = ChipOpts[0x00].SN76496.EmuCore; ChipCnt = (VGMHead.lngHzPSG & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].SN76496; CAA->ChipType = 0x00; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); ChipClk &= ~0x80000000; ChipClk |= VGMHead.lngHzPSG & ((CurChip & 0x01) << 31); if (! UseFM) { CAA->SmpRate = device_start_sn764xx(CurChip, ChipClk, VGMHead.bytPSG_SRWidth, VGMHead.shtPSG_Feedback, (VGMHead.bytPSG_Flags & 0x02) >> 1, (VGMHead.bytPSG_Flags & 0x04) >> 2, (VGMHead.bytPSG_Flags & 0x08) >> 3, (VGMHead.bytPSG_Flags & 0x01) >> 0); CAA->StreamUpdate = &sn764xx_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); if (! CurChip || ! (ChipClk & 0x80000000)) AbsVol += CAA->Volume; } else { open_fm_option(CAA->ChipType, 0x00, ChipClk); open_fm_option(CAA->ChipType, 0x01, VGMHead.bytPSG_SRWidth); open_fm_option(CAA->ChipType, 0x02, VGMHead.shtPSG_Feedback); open_fm_option(CAA->ChipType, 0x04, (VGMHead.bytPSG_Flags & 0x02) >> 1); open_fm_option(CAA->ChipType, 0x05, (VGMHead.bytPSG_Flags & 0x04) >> 2); open_fm_option(CAA->ChipType, 0x06, (VGMHead.bytPSG_Flags & 0x08) >> 3); open_fm_option(CAA->ChipType, 0x07, (VGMHead.bytPSG_Flags & 0x01) >> 0); setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } if (VGMHead.lngHzPSG & 0x80000000) ChipCnt = 0x01; } if (VGMHead.lngHzYM2413) { //ChipVol = UseFM ? 0x00 : 0x200/*0x155*/; if (! UseFM) ym2413_set_emu_core(ChipOpts[0x00].YM2413.EmuCore); else ym2413opl_set_emu_core(ChipOpts[0x00].YM2413.EmuCore); ChipOpts[0x01].YM2413.EmuCore = ChipOpts[0x00].YM2413.EmuCore; ChipCnt = (VGMHead.lngHzYM2413 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2413; CAA->ChipType = 0x01; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_ym2413(CurChip, ChipClk); CAA->StreamUpdate = &ym2413_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); // WHY has this chip such a low volume??? //AbsVol += (CAA->Volume + 1) * 3 / 4; AbsVol += CAA->Volume / 2; } else { setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzYM2612) { //ChipVol = 0x100; ym2612_set_emu_core(ChipOpts[0x00].YM2612.EmuCore); ym2612_set_options((UINT8)ChipOpts[0x00].YM2612.SpecialFlags); ChipOpts[0x01].YM2612.EmuCore = ChipOpts[0x00].YM2612.EmuCore; ChipOpts[0x01].YM2612.SpecialFlags = ChipOpts[0x00].YM2612.SpecialFlags; ChipCnt = (VGMHead.lngHzYM2612 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2612; CAA->ChipType = 0x02; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ym2612(CurChip, ChipClk); CAA->StreamUpdate = &ym2612_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzYM2151) { //ChipVol = 0x100; ym2151_set_emu_core(ChipOpts[0x00].YM2151.EmuCore); ChipCnt = (VGMHead.lngHzYM2151 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2151; CAA->ChipType = 0x03; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ym2151(CurChip, ChipClk); CAA->StreamUpdate = &ym2151_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzSPCM) { //ChipVol = 0x180; ChipCnt = (VGMHead.lngHzSPCM & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].SegaPCM; CAA->ChipType = 0x04; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_segapcm(CurChip, ChipClk, VGMHead.lngSPCMIntf); CAA->StreamUpdate = &SEGAPCM_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzRF5C68) { //ChipVol = 0xB0; // that's right according to MAME, but it's almost too loud ChipCnt = 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].RF5C68; CAA->ChipType = 0x05; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_rf5c68(CurChip, ChipClk); CAA->StreamUpdate = &rf5c68_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzYM2203) { //ChipVol = 0x100; ym2203_set_ay_emu_core(ChipOpts[0x00].YM2203.EmuCore); ChipOpts[0x01].YM2203.EmuCore = ChipOpts[0x00].YM2203.EmuCore; ChipOpts[0x01].YM2203.SpecialFlags = ChipOpts[0x00].YM2203.SpecialFlags; ChipCnt = (VGMHead.lngHzYM2203 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2203; COpt = &ChipOpts[CurChip].YM2203; CAA->ChipType = 0x06; CAA->Paired = &CA_Paired[CurChip][0x00]; CAA->Paired->ChipType = 0x80 | CAA->ChipType; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ym2203(CurChip, ChipClk, COpt->SpecialFlags & 0x01, VGMHead.bytAYFlagYM2203, (int*)&CAA->Paired->SmpRate); CAA->StreamUpdate = &ym2203_stream_update; CAA->Paired->StreamUpdate = &ym2203_stream_update_ay; ym2203_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA, CAA->Paired); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); CAA->Paired->Volume = GetChipVolume(&VGMHead, CAA->Paired->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume + CAA->Paired->Volume; } } if (VGMHead.lngHzYM2608) { //ChipVol = 0x80; ym2608_set_ay_emu_core(ChipOpts[0x00].YM2608.EmuCore); ChipOpts[0x01].YM2608.EmuCore = ChipOpts[0x00].YM2608.EmuCore; ChipOpts[0x01].YM2608.SpecialFlags = ChipOpts[0x00].YM2608.SpecialFlags; ChipCnt = (VGMHead.lngHzYM2608 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2608; COpt = &ChipOpts[CurChip].YM2608; CAA->ChipType = 0x07; CAA->Paired = &CA_Paired[CurChip][0x01]; CAA->Paired->ChipType = 0x80 | CAA->ChipType; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ym2608(CurChip, ChipClk, COpt->SpecialFlags & 0x01, VGMHead.bytAYFlagYM2608, (int*)&CAA->Paired->SmpRate); CAA->StreamUpdate = &ym2608_stream_update; CAA->Paired->StreamUpdate = &ym2608_stream_update_ay; ym2608_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA, CAA->Paired); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); CAA->Paired->Volume = GetChipVolume(&VGMHead, CAA->Paired->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume + CAA->Paired->Volume; //CAA->Volume = ChipVol; //CAA->Paired->Volume = ChipVol * 2; } } if (VGMHead.lngHzYM2610) { //ChipVol = 0x80; ym2610_set_ay_emu_core(ChipOpts[0x00].YM2610.EmuCore); ChipOpts[0x01].YM2610.EmuCore = ChipOpts[0x00].YM2610.EmuCore; ChipOpts[0x01].YM2610.SpecialFlags = ChipOpts[0x00].YM2610.SpecialFlags; ChipCnt = (VGMHead.lngHzYM2610 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM2610; COpt = &ChipOpts[CurChip].YM2610; CAA->ChipType = 0x08; CAA->Paired = &CA_Paired[CurChip][0x02]; CAA->Paired->ChipType = 0x80 | CAA->ChipType; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ym2610(CurChip, ChipClk, COpt->SpecialFlags & 0x01, (int*)&CAA->Paired->SmpRate); CAA->StreamUpdate = (ChipClk & 0x80000000) ? ym2610b_stream_update : ym2610_stream_update; CAA->Paired->StreamUpdate = &ym2610_stream_update_ay; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); CAA->Paired->Volume = GetChipVolume(&VGMHead, CAA->Paired->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume + CAA->Paired->Volume; //CAA->Volume = ChipVol; //CAA->Paired->Volume = ChipVol * 2; } } if (VGMHead.lngHzYM3812) { //ChipVol = UseFM ? 0x00 : 0x100; ym3812_set_emu_core(ChipOpts[0x00].YM3812.EmuCore); ChipOpts[0x01].YM3812.EmuCore = ChipOpts[0x00].YM3812.EmuCore; ChipCnt = (VGMHead.lngHzYM3812 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM3812; CAA->ChipType = 0x09; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_ym3812(CurChip, ChipClk); CAA->StreamUpdate = (ChipClk & 0x80000000) ? dual_opl2_stereo : ym3812_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); if (! CurChip || ! (ChipClk & 0x80000000)) AbsVol += CAA->Volume * 2; } else { setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzYM3526) { //ChipVol = UseFM ? 0x00 : 0x100; ChipCnt = (VGMHead.lngHzYM3526 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YM3526; CAA->ChipType = 0x0A; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_ym3526(CurChip, ChipClk); CAA->StreamUpdate = &ym3526_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } else { setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzY8950) { //ChipVol = UseFM ? 0x00 : 0x100; ChipCnt = (VGMHead.lngHzY8950 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].Y8950; CAA->ChipType = 0x0B; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_y8950(CurChip, ChipClk); CAA->StreamUpdate = &y8950_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } else { setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzYMF262) { //ChipVol = UseFM ? 0x00 : 0x100; ymf262_set_emu_core(ChipOpts[0x00].YMF262.EmuCore); ChipOpts[0x01].YMF262.EmuCore = ChipOpts[0x00].YMF262.EmuCore; ChipCnt = (VGMHead.lngHzYMF262 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YMF262; CAA->ChipType = 0x0C; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_ymf262(CurChip, ChipClk); CAA->StreamUpdate = &ymf262_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } else { setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzYMF278B) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzYMF278B & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YMF278B; CAA->ChipType = 0x0D; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ymf278b(CurChip, ChipClk); CAA->StreamUpdate = &ymf278b_pcm_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; //good as long as it only uses WaveTable Synth } } if (VGMHead.lngHzYMF271) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzYMF271 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YMF271; CAA->ChipType = 0x0E; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ymf271(CurChip, ChipClk); CAA->StreamUpdate = &ymf271_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzYMZ280B) { //ChipVol = 0x98; ChipCnt = (VGMHead.lngHzYMZ280B & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].YMZ280B; CAA->ChipType = 0x0F; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_ymz280b(CurChip, ChipClk); CAA->StreamUpdate = &ymz280b_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += (CAA->Volume * 0x20 / 0x13); } } if (VGMHead.lngHzRF5C164) { //ChipVol = 0x80; ChipCnt = 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].RF5C164; CAA->ChipType = 0x10; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_rf5c164(CurChip, ChipClk); CAA->StreamUpdate = &rf5c164_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } } if (VGMHead.lngHzPWM) { //ChipVol = 0xE0; // 0xCD ChipCnt = 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].PWM; CAA->ChipType = 0x11; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_pwm(CurChip, ChipClk); CAA->StreamUpdate = &pwm_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzAY8910) { //ChipVol = 0x100; ayxx_set_emu_core(ChipOpts[0x00].AY8910.EmuCore); ChipOpts[0x01].AY8910.EmuCore = ChipOpts[0x00].AY8910.EmuCore; ChipCnt = (VGMHead.lngHzAY8910 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].AY8910; CAA->ChipType = 0x12; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); if (! UseFM) { CAA->SmpRate = device_start_ayxx(CurChip, ChipClk, VGMHead.bytAYType, VGMHead.bytAYFlag); CAA->StreamUpdate = &ayxx_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } else { open_fm_option(CAA->ChipType, 0x00, ChipClk); open_fm_option(CAA->ChipType, 0x01, VGMHead.bytAYType); open_fm_option(CAA->ChipType, 0x02, VGMHead.bytAYFlag); setup_real_fm(CAA->ChipType, CurChip); CAA->SmpRate = 0x00000000; CAA->Volume = 0x0000; } } } if (VGMHead.lngHzGBDMG) { //ChipVol = 0xC0; gameboy_sound_set_options((UINT8)ChipOpts[0x00].GameBoy.SpecialFlags); ChipOpts[0x01].GameBoy.SpecialFlags = ChipOpts[0x00].GameBoy.SpecialFlags; ChipCnt = (VGMHead.lngHzGBDMG & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].GameBoy; CAA->ChipType = 0x13; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_gameboy_sound(CurChip, ChipClk); CAA->StreamUpdate = &gameboy_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } } if (VGMHead.lngHzNESAPU) { //ChipVol = 0x100; nes_set_emu_core(ChipOpts[0x00].NES.EmuCore); nes_set_options(ChipOpts[0x00].NES.SpecialFlags); ChipOpts[0x01].NES.EmuCore = ChipOpts[0x00].NES.EmuCore; ChipOpts[0x01].NES.SpecialFlags = ChipOpts[0x00].NES.SpecialFlags; ChipCnt = (VGMHead.lngHzNESAPU & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].NES; CAA->ChipType = 0x14; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_nes(CurChip, ChipClk); CAA->StreamUpdate = &nes_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } } if (VGMHead.lngHzMultiPCM) { //ChipVol = 0x40; ChipCnt = (VGMHead.lngHzMultiPCM & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].MultiPCM; CAA->ChipType = 0x15; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_multipcm(CurChip, ChipClk); CAA->StreamUpdate = &MultiPCM_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 4; } } if (VGMHead.lngHzUPD7759) { //ChipVol = 0x11E; ChipCnt = (VGMHead.lngHzUPD7759 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].UPD7759; CAA->ChipType = 0x16; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_upd7759(CurChip, ChipClk); CAA->StreamUpdate = &upd7759_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzOKIM6258) { //ChipVol = 0x1C0; okim6258_set_options(ChipOpts[0x00].OKIM6258.SpecialFlags); ChipOpts[0x01].OKIM6258.SpecialFlags = ChipOpts[0x00].OKIM6258.SpecialFlags; ChipCnt = (VGMHead.lngHzOKIM6258 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].OKIM6258; CAA->ChipType = 0x17; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_okim6258(CurChip, ChipClk, (VGMHead.bytOKI6258Flags & 0x03) >> 0, (VGMHead.bytOKI6258Flags & 0x04) >> 2, (VGMHead.bytOKI6258Flags & 0x08) >> 3); CAA->StreamUpdate = &okim6258_update; okim6258_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } } if (VGMHead.lngHzOKIM6295) { /*// Use the System Tag to decide between normal and CP System volume level. // I know, this is very hackish, but ATM there's no better solution. if (VGMTag.strSystemNameE != NULL && ! wcsncmp(VGMTag.strSystemNameE, L"CP", 0x02)) ChipVol = 110; else ChipVol = 0x100;*/ ChipCnt = (VGMHead.lngHzOKIM6295 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].OKIM6295; CAA->ChipType = 0x18; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_okim6295(CurChip, ChipClk); CAA->StreamUpdate = &okim6295_update; okim6295_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 2; } } if (VGMHead.lngHzK051649) { //ChipVol = 0xA0; ChipCnt = (VGMHead.lngHzK051649 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].K051649; CAA->ChipType = 0x19; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_k051649(CurChip, ChipClk); CAA->StreamUpdate = &k051649_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzK054539) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzK054539 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].K054539; CAA->ChipType = 0x1A; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_k054539(CurChip, ChipClk); CAA->StreamUpdate = &k054539_update; k054539_init_flags(CurChip, VGMHead.bytK054539Flags); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzHuC6280) { //ChipVol = 0x100; c6280_set_emu_core(ChipOpts[0x00].HuC6280.EmuCore); ChipOpts[0x01].HuC6280.EmuCore = ChipOpts[0x00].HuC6280.EmuCore; ChipCnt = (VGMHead.lngHzHuC6280 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].HuC6280; CAA->ChipType = 0x1B; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_c6280(CurChip, ChipClk); CAA->StreamUpdate = &c6280_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzC140) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzC140 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].C140; CAA->ChipType = 0x1C; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_c140(CurChip, ChipClk, VGMHead.bytC140Type); CAA->StreamUpdate = &c140_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzK053260) { //ChipVol = 0xB3; ChipCnt = (VGMHead.lngHzK053260 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].K053260; CAA->ChipType = 0x1D; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_k053260(CurChip, ChipClk); CAA->StreamUpdate = &k053260_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzPokey) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzPokey & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].Pokey; CAA->ChipType = 0x1E; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_pokey(CurChip, ChipClk); CAA->StreamUpdate = &pokey_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzQSound) { qsound_set_emu_core(ChipOpts[0x00].QSound.EmuCore); ChipOpts[0x01].QSound.EmuCore = ChipOpts[0x00].QSound.EmuCore; //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzQSound & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].QSound; CAA->ChipType = 0x1F; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_qsound(CurChip, ChipClk); CAA->StreamUpdate = &qsound_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzSCSP) { scsp_set_options((UINT8)ChipOpts[0x00].SCSP.SpecialFlags); ChipOpts[0x01].SCSP.SpecialFlags = ChipOpts[0x00].SCSP.SpecialFlags; //ChipVol = 0x20; ChipCnt = (VGMHead.lngHzSCSP & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].SCSP; CAA->ChipType = 0x20; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_scsp(CurChip, ChipClk); CAA->StreamUpdate = &SCSP_Update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 8; } } if (VGMHead.lngHzWSwan) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzWSwan & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].WSwan; CAA->ChipType = 0x21; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = ws_audio_init(CurChip, ChipClk); CAA->StreamUpdate = &ws_audio_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzVSU) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzVSU & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].VSU; CAA->ChipType = 0x22; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_vsu(CurChip, ChipClk); CAA->StreamUpdate = &vsu_stream_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzSAA1099) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzSAA1099 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].SAA1099; CAA->ChipType = 0x23; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_saa1099(CurChip, ChipClk); CAA->StreamUpdate = &saa1099_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzES5503) { //ChipVol = 0x40; ChipCnt = (VGMHead.lngHzES5503 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].ES5503; CAA->ChipType = 0x24; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_es5503(CurChip, ChipClk, VGMHead.bytES5503Chns); CAA->StreamUpdate = &es5503_pcm_update; es5503_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 8; } } if (VGMHead.lngHzES5506) { //ChipVol = 0x20; ChipCnt = (VGMHead.lngHzES5506 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].ES5506; CAA->ChipType = 0x25; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_es5506(CurChip, ChipClk, VGMHead.bytES5506Chns); CAA->StreamUpdate = &es5506_update; es5506_set_srchg_cb(CurChip, &ChangeChipSampleRate, CAA); CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 16; } } if (VGMHead.lngHzX1_010) { //ChipVol = 0x100; ChipCnt = (VGMHead.lngHzX1_010 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].X1_010; CAA->ChipType = 0x26; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_x1_010(CurChip, ChipClk); CAA->StreamUpdate = &seta_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } if (VGMHead.lngHzC352) { c352_set_options((UINT8)ChipOpts[0x00].C352.SpecialFlags); ChipOpts[0x01].C352.SpecialFlags = ChipOpts[0x00].C352.SpecialFlags; //ChipVol = 0x40; ChipCnt = (VGMHead.lngHzC352 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].C352; CAA->ChipType = 0x27; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_c352(CurChip, ChipClk, VGMHead.bytC352ClkDiv * 4); CAA->StreamUpdate = &c352_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume * 8; } } if (VGMHead.lngHzGA20) { //ChipVol = 0x280; ChipCnt = (VGMHead.lngHzGA20 & 0x40000000) ? 0x02 : 0x01; for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) { CAA = &ChipAudio[CurChip].GA20; CAA->ChipType = 0x28; ChipClk = GetChipClock(&VGMHead, (CurChip << 7) | CAA->ChipType, NULL); CAA->SmpRate = device_start_iremga20(CurChip, ChipClk); CAA->StreamUpdate = &IremGA20_update; CAA->Volume = GetChipVolume(&VGMHead, CAA->ChipType, CurChip, ChipCnt); AbsVol += CAA->Volume; } } // Initialize DAC Control and PCM Bank DacCtrlUsed = 0x00; //memset(DacCtrlUsg, 0x00, 0x01 * 0xFF); for (CurChip = 0x00; CurChip < 0xFF; CurChip ++) { DacCtrl[CurChip].Enable = false; } //memset(DacCtrl, 0x00, sizeof(DACCTRL_DATA) * 0xFF); memset(PCMBank, 0x00, sizeof(VGM_PCM_BANK) * PCM_BANK_COUNT); memset(&PCMTbl, 0x00, sizeof(PCMBANK_TBL)); // Reset chips Chips_GeneralActions(0x01); while(AbsVol < 0x200 && AbsVol) { for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) CAA->Volume *= 2; CAA = CA_Paired[CurCSet]; for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) CAA->Volume *= 2; } AbsVol *= 2; } while(AbsVol > 0x300) { for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) CAA->Volume /= 2; CAA = CA_Paired[CurCSet]; for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) CAA->Volume /= 2; } AbsVol /= 2; } // Initialize Resampler for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) SetupResampler(CAA); CAA = CA_Paired[CurCSet]; for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) SetupResampler(CAA); } GeneralChipLists(); break; case 0x01: // Reset chips for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) { if (CAA->ChipType == 0xFF) // chip unused continue; else if (CAA->ChipType == 0x00 && ! UseFM) device_reset_sn764xx(CurCSet); else if (CAA->ChipType == 0x01 && ! UseFM) device_reset_ym2413(CurCSet); else if (CAA->ChipType == 0x02) device_reset_ym2612(CurCSet); else if (CAA->ChipType == 0x03) device_reset_ym2151(CurCSet); else if (CAA->ChipType == 0x04) device_reset_segapcm(CurCSet); else if (CAA->ChipType == 0x05) device_reset_rf5c68(CurCSet); else if (CAA->ChipType == 0x06) device_reset_ym2203(CurCSet); else if (CAA->ChipType == 0x07) device_reset_ym2608(CurCSet); else if (CAA->ChipType == 0x08) device_reset_ym2610(CurCSet); else if (CAA->ChipType == 0x09) { if (! UseFM) { device_reset_ym3812(CurCSet); } if (FileMode == 0x01) { chip_reg_write(0x09, CurCSet, 0x00, 0x01, 0x20); // Enable Waveform Select chip_reg_write(0x09, CurCSet, 0x00, 0xBD, 0xC0); // Disable Rhythm Mode } } else if (CAA->ChipType == 0x0A && ! UseFM) device_reset_ym3526(CurCSet); else if (CAA->ChipType == 0x0B && ! UseFM) device_reset_y8950(CurCSet); else if (CAA->ChipType == 0x0C) { if (! UseFM) { device_reset_ymf262(CurCSet); } if (FileMode >= 0x01) { chip_reg_write(0x0C, CurCSet, 0x01, 0x05, 0x01); // Enable OPL3-Mode chip_reg_write(0x0C, CurCSet, 0x00, 0xBD, 0xC0); // Disable Rhythm Mode chip_reg_write(0x0C, CurCSet, 0x01, 0x04, 0x00); // Disable 4-Op-Mode } } else if (CAA->ChipType == 0x0D) device_reset_ymf278b(CurCSet); else if (CAA->ChipType == 0x0E) device_reset_ymf271(CurCSet); else if (CAA->ChipType == 0x0F) device_reset_ymz280b(CurCSet); else if (CAA->ChipType == 0x10) device_reset_rf5c164(CurCSet); else if (CAA->ChipType == 0x11) device_reset_pwm(CurCSet); else if (CAA->ChipType == 0x12 && ! UseFM) device_reset_ayxx(CurCSet); else if (CAA->ChipType == 0x13) device_reset_gameboy_sound(CurCSet); else if (CAA->ChipType == 0x14) device_reset_nes(CurCSet); else if (CAA->ChipType == 0x15) device_reset_multipcm(CurCSet); else if (CAA->ChipType == 0x16) device_reset_upd7759(CurCSet); else if (CAA->ChipType == 0x17) device_reset_okim6258(CurCSet); else if (CAA->ChipType == 0x18) device_reset_okim6295(CurCSet); else if (CAA->ChipType == 0x19) device_reset_k051649(CurCSet); else if (CAA->ChipType == 0x1A) device_reset_k054539(CurCSet); else if (CAA->ChipType == 0x1B) device_reset_c6280(CurCSet); else if (CAA->ChipType == 0x1C) device_reset_c140(CurCSet); else if (CAA->ChipType == 0x1D) device_reset_k053260(CurCSet); else if (CAA->ChipType == 0x1E) device_reset_pokey(CurCSet); else if (CAA->ChipType == 0x1F) device_reset_qsound(CurCSet); else if (CAA->ChipType == 0x20) device_reset_scsp(CurCSet); else if (CAA->ChipType == 0x21) ws_audio_reset(CurCSet); else if (CAA->ChipType == 0x22) device_reset_vsu(CurCSet); else if (CAA->ChipType == 0x23) device_reset_saa1099(CurCSet); else if (CAA->ChipType == 0x24) device_reset_es5503(CurCSet); else if (CAA->ChipType == 0x25) device_reset_es5506(CurCSet); else if (CAA->ChipType == 0x26) device_reset_x1_010(CurCSet); else if (CAA->ChipType == 0x27) device_reset_c352(CurCSet); else if (CAA->ChipType == 0x28) device_reset_iremga20(CurCSet); } // end for CurChip } // end for CurCSet Chips_GeneralActions(0x10); // set muting mask Chips_GeneralActions(0x20); // set panning for (CurChip = 0x00; CurChip < DacCtrlUsed; CurChip ++) { CurCSet = DacCtrlUsg[CurChip]; device_reset_daccontrol(CurCSet); //DacCtrl[CurCSet].Enable = false; } //DacCtrlUsed = 0x00; //memset(DacCtrlUsg, 0x00, 0x01 * 0xFF); for (CurChip = 0x00; CurChip < PCM_BANK_COUNT; CurChip ++) { // reset PCM Bank, but not the data // (this way I don't need to decompress the data again when restarting) PCMBank[CurChip].DataPos = 0x00000000; PCMBank[CurChip].BnkPos = 0x00000000; } PCMTbl.EntryCount = 0x00; break; case 0x02: // Stop chips for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) { if (CAA->ChipType == 0xFF) // chip unused continue; else if (CAA->ChipType == 0x00 && ! UseFM) device_stop_sn764xx(CurCSet); else if (CAA->ChipType == 0x01 && ! UseFM) device_stop_ym2413(CurCSet); else if (CAA->ChipType == 0x02) device_stop_ym2612(CurCSet); else if (CAA->ChipType == 0x03) device_stop_ym2151(CurCSet); else if (CAA->ChipType == 0x04) device_stop_segapcm(CurCSet); else if (CAA->ChipType == 0x05) device_stop_rf5c68(CurCSet); else if (CAA->ChipType == 0x06) device_stop_ym2203(CurCSet); else if (CAA->ChipType == 0x07) device_stop_ym2608(CurCSet); else if (CAA->ChipType == 0x08) device_stop_ym2610(CurCSet); else if (CAA->ChipType == 0x09 && ! UseFM) device_stop_ym3812(CurCSet); else if (CAA->ChipType == 0x0A && ! UseFM) device_stop_ym3526(CurCSet); else if (CAA->ChipType == 0x0B && ! UseFM) device_stop_y8950(CurCSet); else if (CAA->ChipType == 0x0C && ! UseFM) device_stop_ymf262(CurCSet); else if (CAA->ChipType == 0x0D) device_stop_ymf278b(CurCSet); else if (CAA->ChipType == 0x0E) device_stop_ymf271(CurCSet); else if (CAA->ChipType == 0x0F) device_stop_ymz280b(CurCSet); else if (CAA->ChipType == 0x10) device_stop_rf5c164(CurCSet); else if (CAA->ChipType == 0x11) device_stop_pwm(CurCSet); else if (CAA->ChipType == 0x12 && ! UseFM) device_stop_ayxx(CurCSet); else if (CAA->ChipType == 0x13) device_stop_gameboy_sound(CurCSet); else if (CAA->ChipType == 0x14) device_stop_nes(CurCSet); else if (CAA->ChipType == 0x15) device_stop_multipcm(CurCSet); else if (CAA->ChipType == 0x16) device_stop_upd7759(CurCSet); else if (CAA->ChipType == 0x17) device_stop_okim6258(CurCSet); else if (CAA->ChipType == 0x18) device_stop_okim6295(CurCSet); else if (CAA->ChipType == 0x19) device_stop_k051649(CurCSet); else if (CAA->ChipType == 0x1A) device_stop_k054539(CurCSet); else if (CAA->ChipType == 0x1B) device_stop_c6280(CurCSet); else if (CAA->ChipType == 0x1C) device_stop_c140(CurCSet); else if (CAA->ChipType == 0x1D) device_stop_k053260(CurCSet); else if (CAA->ChipType == 0x1E) device_stop_pokey(CurCSet); else if (CAA->ChipType == 0x1F) device_stop_qsound(CurCSet); else if (CAA->ChipType == 0x20) device_stop_scsp(CurCSet); else if (CAA->ChipType == 0x21) ws_audio_done(CurCSet); else if (CAA->ChipType == 0x22) device_stop_vsu(CurCSet); else if (CAA->ChipType == 0x23) device_stop_saa1099(CurCSet); else if (CAA->ChipType == 0x24) device_stop_es5503(CurCSet); else if (CAA->ChipType == 0x25) device_stop_es5506(CurCSet); else if (CAA->ChipType == 0x26) device_stop_x1_010(CurCSet); else if (CAA->ChipType == 0x27) device_stop_c352(CurCSet); else if (CAA->ChipType == 0x28) device_stop_iremga20(CurCSet); CAA->ChipType = 0xFF; // mark as "unused" } // end for CurChip } // end for CurCSet for (CurChip = 0x00; CurChip < DacCtrlUsed; CurChip ++) { CurCSet = DacCtrlUsg[CurChip]; device_stop_daccontrol(CurCSet); DacCtrl[CurCSet].Enable = false; } DacCtrlUsed = 0x00; for (CurChip = 0x00; CurChip < PCM_BANK_COUNT; CurChip ++) { free(PCMBank[CurChip].Bank); free(PCMBank[CurChip].Data); } //memset(PCMBank, 0x00, sizeof(VGM_PCM_BANK) * PCM_BANK_COUNT); free(PCMTbl.Entries); //memset(&PCMTbl, 0x00, sizeof(PCMBANK_TBL)); break; case 0x10: // Set Muting Mask for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) { if (CAA->ChipType == 0xFF) // chip unused continue; else if (CAA->ChipType == 0x00 && ! UseFM) sn764xx_set_mute_mask(CurCSet, ChipOpts[CurCSet].SN76496.ChnMute1); else if (CAA->ChipType == 0x01 && ! UseFM) ym2413_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM2413.ChnMute1); else if (CAA->ChipType == 0x02) ym2612_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM2612.ChnMute1); else if (CAA->ChipType == 0x03) ym2151_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM2151.ChnMute1); else if (CAA->ChipType == 0x04) segapcm_set_mute_mask(CurCSet, ChipOpts[CurCSet].SegaPCM.ChnMute1); else if (CAA->ChipType == 0x05) rf5c68_set_mute_mask(CurCSet, ChipOpts[CurCSet].RF5C68.ChnMute1); else if (CAA->ChipType == 0x06) ym2203_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM2203.ChnMute1, ChipOpts[CurCSet].YM2203.ChnMute3); else if (CAA->ChipType == 0x07) { MaskVal = (ChipOpts[CurCSet].YM2608.ChnMute1 & 0x3F) << 0; MaskVal |= (ChipOpts[CurCSet].YM2608.ChnMute2 & 0x7F) << 6; ym2608_set_mute_mask(CurCSet, MaskVal, ChipOpts[CurCSet].YM2608.ChnMute3); } else if (CAA->ChipType == 0x08) { MaskVal = (ChipOpts[CurCSet].YM2610.ChnMute1 & 0x3F) << 0; MaskVal |= (ChipOpts[CurCSet].YM2610.ChnMute2 & 0x7F) << 6; ym2610_set_mute_mask(CurCSet, MaskVal, ChipOpts[CurCSet].YM2610.ChnMute3); } else if (CAA->ChipType == 0x09 && ! UseFM) ym3812_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM3812.ChnMute1); else if (CAA->ChipType == 0x0A && ! UseFM) ym3526_set_mute_mask(CurCSet, ChipOpts[CurCSet].YM3526.ChnMute1); else if (CAA->ChipType == 0x0B && ! UseFM) y8950_set_mute_mask(CurCSet, ChipOpts[CurCSet].Y8950.ChnMute1); else if (CAA->ChipType == 0x0C && ! UseFM) ymf262_set_mute_mask(CurCSet, ChipOpts[CurCSet].YMF262.ChnMute1); else if (CAA->ChipType == 0x0D) ymf278b_set_mute_mask(CurCSet, ChipOpts[CurCSet].YMF278B.ChnMute1, ChipOpts[CurCSet].YMF278B.ChnMute2); else if (CAA->ChipType == 0x0E) ymf271_set_mute_mask(CurCSet, ChipOpts[CurCSet].YMF271.ChnMute1); else if (CAA->ChipType == 0x0F) ymz280b_set_mute_mask(CurCSet, ChipOpts[CurCSet].YMZ280B.ChnMute1); else if (CAA->ChipType == 0x10) rf5c164_set_mute_mask(CurCSet, ChipOpts[CurCSet].RF5C164.ChnMute1); else if (CAA->ChipType == 0x11) ; // PWM - nothing to mute else if (CAA->ChipType == 0x12 && ! UseFM) ayxx_set_mute_mask(CurCSet, ChipOpts[CurCSet].AY8910.ChnMute1); else if (CAA->ChipType == 0x13) gameboy_sound_set_mute_mask(CurCSet, ChipOpts[CurCSet].GameBoy.ChnMute1); else if (CAA->ChipType == 0x14) nes_set_mute_mask(CurCSet, ChipOpts[CurCSet].NES.ChnMute1); else if (CAA->ChipType == 0x15) multipcm_set_mute_mask(CurCSet, ChipOpts[CurCSet].MultiPCM.ChnMute1); else if (CAA->ChipType == 0x16) ; // UPD7759 - nothing to mute else if (CAA->ChipType == 0x17) ; // OKIM6258 - nothing to mute else if (CAA->ChipType == 0x18) okim6295_set_mute_mask(CurCSet, ChipOpts[CurCSet].OKIM6295.ChnMute1); else if (CAA->ChipType == 0x19) k051649_set_mute_mask(CurCSet, ChipOpts[CurCSet].K051649.ChnMute1); else if (CAA->ChipType == 0x1A) k054539_set_mute_mask(CurCSet, ChipOpts[CurCSet].K054539.ChnMute1); else if (CAA->ChipType == 0x1B) c6280_set_mute_mask(CurCSet, ChipOpts[CurCSet].HuC6280.ChnMute1); else if (CAA->ChipType == 0x1C) c140_set_mute_mask(CurCSet, ChipOpts[CurCSet].C140.ChnMute1); else if (CAA->ChipType == 0x1D) k053260_set_mute_mask(CurCSet, ChipOpts[CurCSet].K053260.ChnMute1); else if (CAA->ChipType == 0x1E) pokey_set_mute_mask(CurCSet, ChipOpts[CurCSet].Pokey.ChnMute1); else if (CAA->ChipType == 0x1F) qsound_set_mute_mask(CurCSet, ChipOpts[CurCSet].QSound.ChnMute1); else if (CAA->ChipType == 0x20) scsp_set_mute_mask(CurCSet, ChipOpts[CurCSet].SCSP.ChnMute1); else if (CAA->ChipType == 0x21) ws_set_mute_mask(CurCSet, ChipOpts[CurCSet].WSwan.ChnMute1); else if (CAA->ChipType == 0x22) vsu_set_mute_mask(CurCSet, ChipOpts[CurCSet].VSU.ChnMute1); else if (CAA->ChipType == 0x23) saa1099_set_mute_mask(CurCSet, ChipOpts[CurCSet].SAA1099.ChnMute1); else if (CAA->ChipType == 0x24) es5503_set_mute_mask(CurCSet, ChipOpts[CurCSet].ES5503.ChnMute1); else if (CAA->ChipType == 0x25) es5506_set_mute_mask(CurCSet, ChipOpts[CurCSet].ES5506.ChnMute1); else if (CAA->ChipType == 0x26) x1_010_set_mute_mask(CurCSet, ChipOpts[CurCSet].X1_010.ChnMute1); else if (CAA->ChipType == 0x27) c352_set_mute_mask(CurCSet, ChipOpts[CurCSet].C352.ChnMute1); else if (CAA->ChipType == 0x28) iremga20_set_mute_mask(CurCSet, ChipOpts[CurCSet].GA20.ChnMute1); } // end for CurChip } // end for CurCSet break; case 0x20: // Set Panning for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) { if (CAA->ChipType == 0xFF) // chip unused continue; else if (CAA->ChipType == 0x00 && ! UseFM) sn764xx_set_panning(CurCSet, ChipOpts[CurCSet].SN76496.Panning); else if (CAA->ChipType == 0x01 && ! UseFM) ym2413_set_panning(CurCSet, ChipOpts[CurCSet].YM2413.Panning); } // end for CurChip } // end for CurCSet break; } return; } INLINE INT32 SampleVGM2Pbk_I(INT32 SampleVal) { return (INT32)((INT64)SampleVal * VGMSmplRateMul / VGMSmplRateDiv); } INLINE INT32 SamplePbk2VGM_I(INT32 SampleVal) { return (INT32)((INT64)SampleVal * VGMSmplRateDiv / VGMSmplRateMul); } INT32 SampleVGM2Playback(INT32 SampleVal) { return (INT32)((INT64)SampleVal * VGMSmplRateMul / VGMSmplRateDiv); } INT32 SamplePlayback2VGM(INT32 SampleVal) { return (INT32)((INT64)SampleVal * VGMSmplRateDiv / VGMSmplRateMul); } static UINT8 StartThread(void) { #ifdef WIN32 HANDLE PlayThreadHandle; DWORD PlayThreadID; //char TestStr[0x80]; if (PlayThreadOpen) return 0xD0; // Thread is already active PauseThread = true; ThreadNoWait = false; ThreadPauseConfrm = false; CloseThread = false; ThreadPauseEnable = true; PlayThreadHandle = CreateThread(NULL, 0x00, &PlayingThread, NULL, 0x00, &PlayThreadID); if (PlayThreadHandle == NULL) return 0xC8; // CreateThread failed CloseHandle(PlayThreadHandle); PlayThreadOpen = true; //PauseThread = false; is done after File Init return 0x00; #else UINT32 RetVal; PauseThread = true; ThreadNoWait = false; ThreadPauseConfrm = false; CloseThread = false; ThreadPauseEnable = true; RetVal = pthread_create(&hPlayThread, NULL, &PlayingThread, NULL); if (RetVal) return 0xC8; // CreateThread failed PlayThreadOpen = true; return 0x00; #endif } static UINT8 StopThread(void) { #ifdef WIN32 UINT16 Cnt; #endif if (! PlayThreadOpen) return 0xD8; // Thread is not active #ifdef WIN32 CloseThread = true; for (Cnt = 0; Cnt < 100; Cnt ++) { Sleep(1); if (hPlayThread == NULL) break; } #else CloseThread = true; pthread_join(hPlayThread, NULL); #endif PlayThreadOpen = false; ThreadPauseEnable = false; return 0x00; } #if defined(WIN32) && defined(MIXER_MUTING) //static bool GetMixerControl(HMIXEROBJ hmixer, MIXERCONTROL* mxc) static bool GetMixerControl(void) { // This function attempts to obtain a mixer control. Returns True if successful. MIXERLINECONTROLS mxlc; MIXERLINE mxl; HGLOBAL hmem; MMRESULT RetVal; mxl.cbStruct = sizeof(MIXERLINE); mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER; // Obtain a line corresponding to the component type RetVal = mixerGetLineInfo((HMIXEROBJ)hmixer, &mxl, MIXER_GETLINEINFOF_COMPONENTTYPE); if (RetVal != MMSYSERR_NOERROR) return false; mxlc.cbStruct = sizeof(MIXERLINECONTROLS); mxlc.dwLineID = mxl.dwLineID; mxlc.dwControlID = MIXERCONTROL_CONTROLTYPE_MUTE; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(MIXERCONTROL); // Allocate a buffer for the control hmem = GlobalAlloc(0x40, sizeof(MIXERCONTROL)); mxlc.pamxctrl = (MIXERCONTROL*)GlobalLock(hmem); mixctrl.cbStruct = sizeof(MIXERCONTROL); // Get the control RetVal = mixerGetLineControls((HMIXEROBJ)hmixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (RetVal != MMSYSERR_NOERROR) { GlobalFree(hmem); return false; } // Copy the control into the destination structure //memcpy(mixctrl, mxlc.pamxctrl, sizeof(MIXERCONTROL)); mixctrl = *mxlc.pamxctrl; GlobalFree(hmem); return true; } #endif //static bool SetMuteControl(HMIXEROBJ hmixer, MIXERCONTROL* mxc, bool mute) static bool SetMuteControl(bool mute) { #ifdef MIXER_MUTING #ifdef WIN32 MIXERCONTROLDETAILS mxcd; MIXERCONTROLDETAILS_UNSIGNED vol; HGLOBAL hmem; MMRESULT RetVal; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = mixctrl.dwControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); hmem = GlobalAlloc(0x40, sizeof(MIXERCONTROLDETAILS_UNSIGNED)); mxcd.paDetails = GlobalLock(hmem); vol.dwValue = mute; memcpy(mxcd.paDetails, &vol, sizeof(MIXERCONTROLDETAILS_UNSIGNED)); RetVal = mixerSetControlDetails((HMIXEROBJ)hmixer, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE); GlobalFree(hmem); if (RetVal != MMSYSERR_NOERROR) return false; return true; #else UINT16 mix_vol; int RetVal; ioctl(hmixer, MIXER_READ(SOUND_MIXER_SYNTH), &mix_vol); if (mix_vol) mixer_vol = mix_vol; mix_vol = mute ? 0x0000: mixer_vol; RetVal = ioctl(hmixer, MIXER_WRITE(SOUND_MIXER_SYNTH), &mix_vol); return ! RetVal; #endif #else //#indef MIXER_MUTING float TempVol; TempVol = MasterVol; if (TempVol > 0.0f) VolumeBak = TempVol; MasterVol = mute ? 0.0f : VolumeBak; FinalVol = VolumeLevelM * MasterVol * MasterVol; RefreshVolume(); return true; #endif } static void InterpretFile(UINT32 SampleCount) { UINT32 TempLng; UINT8 CurChip; //if (Interpreting && SampleCount == 1) // return; while(Interpreting) Sleep(1); if (DacCtrlUsed && SampleCount > 1) // handle skipping { for (CurChip = 0x00; CurChip < DacCtrlUsed; CurChip ++) { daccontrol_update(DacCtrlUsg[CurChip], SampleCount - 1); } } Interpreting = true; if (! FileMode) InterpretVGM(SampleCount); #ifdef ADDITIONAL_FORMATS else InterpretOther(SampleCount); #endif if (DacCtrlUsed && SampleCount) { // calling this here makes "Emulating while Paused" nicer for (CurChip = 0x00; CurChip < DacCtrlUsed; CurChip ++) { daccontrol_update(DacCtrlUsg[CurChip], 1); } } if (UseFM && FadePlay) { //TempLng = PlayingTime % (SampleRate / 5); //if (! TempLng) TempLng = PlayingTime / (SampleRate / 5) - (PlayingTime + SampleCount) / (SampleRate / 5); if (TempLng) RefreshVolume(); } if (AutoStopSkip && SampleCount) { StopSkipping(); AutoStopSkip = false; ResetPBTimer = true; } if (! PausePlay || ForceVGMExec) VGMSmplPlayed += SampleCount; PlayingTime += SampleCount; //if (FadePlay && ! FadeTime) // EndPlay = true; Interpreting = false; return; } static void AddPCMData(UINT8 Type, UINT32 DataSize, const UINT8* Data) { UINT32 CurBnk; VGM_PCM_BANK* TempPCM; VGM_PCM_DATA* TempBnk; UINT32 BankSize; bool RetVal; UINT8 BnkType; UINT8 CurDAC; BnkType = Type & 0x3F; if (BnkType >= PCM_BANK_COUNT || VGMCurLoop) return; if (Type == 0x7F) { ReadPCMTable(DataSize, Data); return; } TempPCM = &PCMBank[BnkType]; TempPCM->BnkPos ++; if (TempPCM->BnkPos <= TempPCM->BankCount) return; // Speed hack for restarting playback (skip already loaded blocks) CurBnk = TempPCM->BankCount; TempPCM->BankCount ++; if (Last95Max != 0xFFFF) Last95Max = TempPCM->BankCount; TempPCM->Bank = (VGM_PCM_DATA*)realloc(TempPCM->Bank, sizeof(VGM_PCM_DATA) * TempPCM->BankCount); if (! (Type & 0x40)) BankSize = DataSize; else BankSize = ReadLE32(&Data[0x01]); TempPCM->Data = realloc(TempPCM->Data, TempPCM->DataSize + BankSize); TempBnk = &TempPCM->Bank[CurBnk]; TempBnk->DataStart = TempPCM->DataSize; if (! (Type & 0x40)) { TempBnk->DataSize = DataSize; TempBnk->Data = TempPCM->Data + TempBnk->DataStart; memcpy(TempBnk->Data, Data, DataSize); } else { TempBnk->Data = TempPCM->Data + TempBnk->DataStart; RetVal = DecompressDataBlk(TempBnk, DataSize, Data); if (! RetVal) { TempBnk->Data = NULL; TempBnk->DataSize = 0x00; //return; goto RefreshDACStrm; // sorry for the goto, but I don't want to copy-paste the code } } if (BankSize != TempBnk->DataSize) fprintf(stderr, "Error reading Data Block! Data Size conflict!\n"); TempPCM->DataSize += BankSize; // realloc may've moved the Bank block, so refresh all DAC Streams RefreshDACStrm: for (CurDAC = 0x00; CurDAC < DacCtrlUsed; CurDAC ++) { if (DacCtrl[DacCtrlUsg[CurDAC]].Bank == BnkType) daccontrol_refresh_data(DacCtrlUsg[CurDAC], TempPCM->Data, TempPCM->DataSize); } return; } /*INLINE FUINT16 ReadBits(UINT8* Data, UINT32* Pos, FUINT8* BitPos, FUINT8 BitsToRead) { FUINT8 BitReadVal; UINT32 InPos; FUINT8 InVal; FUINT8 BitMask; FUINT8 InShift; FUINT8 OutBit; FUINT16 RetVal; InPos = *Pos; InShift = *BitPos; OutBit = 0x00; RetVal = 0x0000; while(BitsToRead) { BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; BitsToRead -= BitReadVal; BitMask = (1 << BitReadVal) - 1; InShift += BitReadVal; InVal = (Data[InPos] << InShift >> 8) & BitMask; if (InShift >= 8) { InShift -= 8; InPos ++; if (InShift) InVal |= (Data[InPos] << InShift >> 8) & BitMask; } RetVal |= InVal << OutBit; OutBit += BitReadVal; } *Pos = InPos; *BitPos = InShift; return RetVal; } static void DecompressDataBlk(VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data) { UINT8 ComprType; UINT8 BitDec; FUINT8 BitCmp; UINT8 CmpSubType; UINT16 AddVal; UINT32 InPos; UINT32 OutPos; FUINT16 InVal; FUINT16 OutVal; FUINT8 ValSize; FUINT8 InShift; FUINT8 OutShift; UINT8* Ent1B; UINT16* Ent2B; //UINT32 Time; //Time = GetTickCount(); ComprType = Data[0x00]; Bank->DataSize = ReadLE32(&Data[0x01]); BitDec = Data[0x05]; BitCmp = Data[0x06]; CmpSubType = Data[0x07]; AddVal = ReadLE16(&Data[0x08]); switch(ComprType) { case 0x00: // n-Bit compression if (CmpSubType == 0x02) { Ent1B = (UINT8*)PCMTbl.Entries; Ent2B = (UINT16*)PCMTbl.Entries; if (! PCMTbl.EntryCount) { fprintf(stderr, "Error loading table-compressed data block! No table loaded!\n"); return; } else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) { fprintf(stderr, "Warning! Data block and loaded value table incompatible!\n"); return; } } ValSize = (BitDec + 7) / 8; InPos = 0x0A; InShift = 0; OutShift = BitDec - BitCmp; for (OutPos = 0x00; OutPos < Bank->DataSize; OutPos += ValSize) { if (InPos >= DataSize) break; InVal = ReadBits(Data, &InPos, &InShift, BitCmp); switch(CmpSubType) { case 0x00: // Copy OutVal = InVal + AddVal; break; case 0x01: // Shift Left OutVal = (InVal << OutShift) + AddVal; break; case 0x02: // Table switch(ValSize) { case 0x01: OutVal = Ent1B[InVal]; break; case 0x02: OutVal = Ent2B[InVal]; break; } break; } memcpy(&Bank->Data[OutPos], &OutVal, ValSize); } break; } //Time = GetTickCount() - Time; //printf("Decompression Time: %u\n", Time); return; }*/ static bool DecompressDataBlk(VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data) { UINT8 ComprType; UINT8 BitDec; FUINT8 BitCmp; UINT8 CmpSubType; UINT16 AddVal; const UINT8* InPos; const UINT8* InDataEnd; UINT8* OutPos; const UINT8* OutDataEnd; FUINT16 InVal; FUINT16 OutVal; FUINT8 ValSize; FUINT8 InShift; FUINT8 OutShift; UINT8* Ent1B; UINT16* Ent2B; #if defined(_DEBUG) && defined(WIN32) UINT32 Time; #endif // ReadBits Variables FUINT8 BitsToRead; FUINT8 BitReadVal; FUINT8 InValB; FUINT8 BitMask; FUINT8 OutBit; // Variables for DPCM UINT16 OutMask; #if defined(_DEBUG) && defined(WIN32) Time = GetTickCount(); #endif ComprType = Data[0x00]; Bank->DataSize = ReadLE32(&Data[0x01]); switch(ComprType) { case 0x00: // n-Bit compression BitDec = Data[0x05]; BitCmp = Data[0x06]; CmpSubType = Data[0x07]; AddVal = ReadLE16(&Data[0x08]); Ent1B = NULL; Ent2B = NULL; if (CmpSubType == 0x02) { Ent1B = (UINT8*)PCMTbl.Entries; // Big Endian note: Those are stored in LE and converted when reading. Ent2B = (UINT16*)PCMTbl.Entries; if (! PCMTbl.EntryCount) { Bank->DataSize = 0x00; fprintf(stderr, "Error loading table-compressed data block! No table loaded!\n"); return false; } else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) { Bank->DataSize = 0x00; fprintf(stderr, "Warning! Data block and loaded value table incompatible!\n"); return false; } } ValSize = (BitDec + 7) / 8; InPos = Data + 0x0A; InDataEnd = Data + DataSize; InShift = 0; OutShift = BitDec - BitCmp; OutDataEnd = Bank->Data + Bank->DataSize; OutVal = 0x0000; for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) { //InVal = ReadBits(Data, InPos, &InShift, BitCmp); // inlined - is 30% faster OutBit = 0x00; InVal = 0x0000; BitsToRead = BitCmp; while(BitsToRead) { BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; BitsToRead -= BitReadVal; BitMask = (1 << BitReadVal) - 1; InShift += BitReadVal; InValB = (*InPos << InShift >> 8) & BitMask; if (InShift >= 8) { InShift -= 8; InPos ++; if (InShift) InValB |= (*InPos << InShift >> 8) & BitMask; } InVal |= InValB << OutBit; OutBit += BitReadVal; } switch(CmpSubType) { case 0x00: // Copy OutVal = InVal + AddVal; break; case 0x01: // Shift Left OutVal = (InVal << OutShift) + AddVal; break; case 0x02: // Table switch(ValSize) { case 0x01: OutVal = Ent1B[InVal]; break; case 0x02: #ifdef VGM_LITTLE_ENDIAN OutVal = Ent2B[InVal]; #else OutVal = ReadLE16((UINT8*)&Ent2B[InVal]); #endif break; } break; } #ifdef VGM_LITTLE_ENDIAN //memcpy(OutPos, &OutVal, ValSize); if (ValSize == 0x01) *((UINT8*)OutPos) = (UINT8)OutVal; else //if (ValSize == 0x02) *((UINT16*)OutPos) = (UINT16)OutVal; #else if (ValSize == 0x01) { *OutPos = (UINT8)OutVal; } else //if (ValSize == 0x02) { OutPos[0x00] = (UINT8)((OutVal & 0x00FF) >> 0); OutPos[0x01] = (UINT8)((OutVal & 0xFF00) >> 8); } #endif } break; case 0x01: // Delta-PCM BitDec = Data[0x05]; BitCmp = Data[0x06]; OutVal = ReadLE16(&Data[0x08]); Ent1B = (UINT8*)PCMTbl.Entries; Ent2B = (UINT16*)PCMTbl.Entries; if (! PCMTbl.EntryCount) { Bank->DataSize = 0x00; fprintf(stderr, "Error loading table-compressed data block! No table loaded!\n"); return false; } else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) { Bank->DataSize = 0x00; fprintf(stderr, "Warning! Data block and loaded value table incompatible!\n"); return false; } ValSize = (BitDec + 7) / 8; OutMask = (1 << BitDec) - 1; InPos = Data + 0x0A; InDataEnd = Data + DataSize; InShift = 0; OutShift = BitDec - BitCmp; OutDataEnd = Bank->Data + Bank->DataSize; AddVal = 0x0000; for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) { //InVal = ReadBits(Data, InPos, &InShift, BitCmp); // inlined - is 30% faster OutBit = 0x00; InVal = 0x0000; BitsToRead = BitCmp; while(BitsToRead) { BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; BitsToRead -= BitReadVal; BitMask = (1 << BitReadVal) - 1; InShift += BitReadVal; InValB = (*InPos << InShift >> 8) & BitMask; if (InShift >= 8) { InShift -= 8; InPos ++; if (InShift) InValB |= (*InPos << InShift >> 8) & BitMask; } InVal |= InValB << OutBit; OutBit += BitReadVal; } switch(ValSize) { case 0x01: AddVal = Ent1B[InVal]; OutVal += AddVal; OutVal &= OutMask; *((UINT8*)OutPos) = (UINT8)OutVal; break; case 0x02: #ifdef VGM_LITTLE_ENDIAN AddVal = Ent2B[InVal]; OutVal += AddVal; OutVal &= OutMask; *((UINT16*)OutPos) = (UINT16)OutVal; #else AddVal = ReadLE16((UINT8*)&Ent2B[InVal]); OutVal += AddVal; OutVal &= OutMask; OutPos[0x00] = (UINT8)((OutVal & 0x00FF) >> 0); OutPos[0x01] = (UINT8)((OutVal & 0xFF00) >> 8); #endif break; } } break; default: fprintf(stderr, "Error: Unknown data block compression!\n"); return false; } #if defined(_DEBUG) && defined(WIN32) Time = GetTickCount() - Time; fprintf(stderr, "Decompression Time: %u\n", Time); #endif return true; } static UINT8 GetDACFromPCMBank(void) { // for YM2612 DAC data only /*VGM_PCM_BANK* TempPCM; UINT32 CurBnk;*/ UINT32 DataPos; /*TempPCM = &PCMBank[0x00]; DataPos = TempPCM->DataPos; for (CurBnk = 0x00; CurBnk < TempPCM->BankCount; CurBnk ++) { if (DataPos < TempPCM->Bank[CurBnk].DataSize) { if (TempPCM->DataPos < TempPCM->DataSize) TempPCM->DataPos ++; return TempPCM->Bank[CurBnk].Data[DataPos]; } DataPos -= TempPCM->Bank[CurBnk].DataSize; } return 0x80;*/ DataPos = PCMBank[0x00].DataPos; if (DataPos >= PCMBank[0x00].DataSize) return 0x80; PCMBank[0x00].DataPos ++; return PCMBank[0x00].Data[DataPos]; } static UINT8* GetPointerFromPCMBank(UINT8 Type, UINT32 DataPos) { if (Type >= PCM_BANK_COUNT) return NULL; if (DataPos >= PCMBank[Type].DataSize) return NULL; return &PCMBank[Type].Data[DataPos]; } static void ReadPCMTable(UINT32 DataSize, const UINT8* Data) { UINT8 ValSize; UINT32 TblSize; PCMTbl.ComprType = Data[0x00]; PCMTbl.CmpSubType = Data[0x01]; PCMTbl.BitDec = Data[0x02]; PCMTbl.BitCmp = Data[0x03]; PCMTbl.EntryCount = ReadLE16(&Data[0x04]); ValSize = (PCMTbl.BitDec + 7) / 8; TblSize = PCMTbl.EntryCount * ValSize; PCMTbl.Entries = realloc(PCMTbl.Entries, TblSize); memcpy(PCMTbl.Entries, &Data[0x06], TblSize); if (DataSize < 0x06 + TblSize) fprintf(stderr, "Warning! Bad PCM Table Length!\n"); return; } #define CHIP_CHECK(name) (ChipAudio[CurChip].name.ChipType != 0xFF) static void InterpretVGM(UINT32 SampleCount) { INT32 SmplPlayed; UINT8 Command; UINT8 TempByt; UINT16 TempSht; UINT32 TempLng; VGM_PCM_BANK* TempPCM; VGM_PCM_DATA* TempBnk; UINT32 ROMSize; UINT32 DataStart; UINT32 DataLen; const UINT8* ROMData; UINT8 CurChip; const UINT8* VGMPnt; if (VGMEnd) return; if (PausePlay && ! ForceVGMExec) return; SmplPlayed = SamplePbk2VGM_I(VGMSmplPlayed + SampleCount); while(VGMSmplPos <= SmplPlayed) { Command = VGMData[VGMPos + 0x00]; if (Command >= 0x70 && Command <= 0x8F) { switch(Command & 0xF0) { case 0x70: VGMSmplPos += (Command & 0x0F) + 0x01; break; case 0x80: TempByt = GetDACFromPCMBank(); if (VGMHead.lngHzYM2612) { chip_reg_write(0x02, 0x00, 0x00, 0x2A, TempByt); } VGMSmplPos += (Command & 0x0F); break; } VGMPos += 0x01; } else { VGMPnt = &VGMData[VGMPos]; // Cheat Mode (to use 2 instances of 1 chip) CurChip = 0x00; switch(Command) { case 0x30: if (VGMHead.lngHzPSG & 0x40000000) { Command += 0x20; CurChip = 0x01; } break; case 0x3F: if (VGMHead.lngHzPSG & 0x40000000) { Command += 0x10; CurChip = 0x01; } break; case 0xA1: if (VGMHead.lngHzYM2413 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xA2: case 0xA3: if (VGMHead.lngHzYM2612 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xA4: if (VGMHead.lngHzYM2151 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xA5: if (VGMHead.lngHzYM2203 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xA6: case 0xA7: if (VGMHead.lngHzYM2608 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xA8: case 0xA9: if (VGMHead.lngHzYM2610 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xAA: if (VGMHead.lngHzYM3812 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xAB: if (VGMHead.lngHzYM3526 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xAC: if (VGMHead.lngHzY8950 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xAE: case 0xAF: if (VGMHead.lngHzYMF262 & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; case 0xAD: if (VGMHead.lngHzYMZ280B & 0x40000000) { Command -= 0x50; CurChip = 0x01; } break; } switch(Command) { case 0x66: // End Of File if (VGMHead.lngLoopOffset) { VGMPos = VGMHead.lngLoopOffset; VGMSmplPos -= VGMHead.lngLoopSamples; VGMSmplPlayed -= SampleVGM2Pbk_I(VGMHead.lngLoopSamples); SmplPlayed = SamplePbk2VGM_I(VGMSmplPlayed + SampleCount); VGMCurLoop ++; if (VGMMaxLoopM && VGMCurLoop >= VGMMaxLoopM) { #ifndef CONSOLE_MODE if (! FadePlay) { FadeStart = SampleVGM2Pbk_I(VGMHead.lngTotalSamples + (VGMCurLoop - 1) * VGMHead.lngLoopSamples); } #endif FadePlay = true; } if (FadePlay && ! FadeTime) VGMEnd = true; } else { if (VGMHead.lngTotalSamples != (UINT32)VGMSmplPos) { #ifdef CONSOLE_MODE fprintf(stderr, "Warning! Header Samples: %u\t Counted Samples: %u\n", VGMHead.lngTotalSamples, VGMSmplPos); ErrorHappened = true; #endif VGMHead.lngTotalSamples = VGMSmplPos; } if (HardStopOldVGMs) { if (VGMHead.lngVersion < 0x150 || (VGMHead.lngVersion == 0x150 && HardStopOldVGMs == 0x02)) Chips_GeneralActions(0x01); // reset all chips, for instant silence } VGMEnd = true; break; } break; case 0x62: // 1/60s delay VGMSmplPos += 735; VGMPos += 0x01; break; case 0x63: // 1/50s delay VGMSmplPos += 882; VGMPos += 0x01; break; case 0x61: // xx Sample Delay TempSht = ReadLE16(&VGMPnt[0x01]); VGMSmplPos += TempSht; VGMPos += 0x03; break; case 0x50: // SN76496 write if (CHIP_CHECK(SN76496)) { chip_reg_write(0x00, CurChip, 0x00, 0x00, VGMPnt[0x01]); } VGMPos += 0x02; break; case 0x51: // YM2413 write if (CHIP_CHECK(YM2413)) { chip_reg_write(0x01, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x52: // YM2612 write port 0 case 0x53: // YM2612 write port 1 if (CHIP_CHECK(YM2612)) { chip_reg_write(0x02, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x67: // PCM Data Stream TempByt = VGMPnt[0x02]; TempLng = ReadLE32(&VGMPnt[0x03]); if (TempLng & 0x80000000) { TempLng &= 0x7FFFFFFF; CurChip = 0x01; } switch(TempByt & 0xC0) { case 0x00: // Database Block case 0x40: AddPCMData(TempByt, TempLng, &VGMPnt[0x07]); /*switch(TempByt) { case 0x00: // YM2612 PCM Database break; case 0x01: // RF5C68 PCM Database break; case 0x02: // RF5C164 PCM Database break; }*/ break; case 0x80: // ROM/RAM Dump if (VGMCurLoop) break; ROMSize = ReadLE32(&VGMPnt[0x07]); DataStart = ReadLE32(&VGMPnt[0x0B]); DataLen = TempLng - 0x08; ROMData = &VGMPnt[0x0F]; switch(TempByt) { case 0x80: // SegaPCM ROM if (! CHIP_CHECK(SegaPCM)) break; sega_pcm_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x81: // YM2608 DELTA-T ROM Image if (! CHIP_CHECK(YM2608)) break; ym2608_write_data_pcmrom(CurChip, 0x02, ROMSize, DataStart, DataLen, ROMData); break; case 0x82: // YM2610 ADPCM ROM Image case 0x83: // YM2610 DELTA-T ROM Image if (! CHIP_CHECK(YM2610)) break; TempByt = 0x01 + (TempByt - 0x82); ym2610_write_data_pcmrom(CurChip, TempByt, ROMSize, DataStart, DataLen, ROMData); break; case 0x84: // YMF278B ROM Image if (! CHIP_CHECK(YMF278B)) break; ymf278b_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x85: // YMF271 ROM Image if (! CHIP_CHECK(YMF271)) break; ymf271_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x86: // YMZ280B ROM Image if (! CHIP_CHECK(YMZ280B)) break; ymz280b_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x87: // YMF278B RAM Image if (! CHIP_CHECK(YMF278B)) break; ymf278b_write_ram(CurChip, DataStart, DataLen, ROMData); break; case 0x88: // Y8950 DELTA-T ROM Image if (! CHIP_CHECK(Y8950) || PlayingMode == 0x01) break; y8950_write_data_pcmrom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x89: // MultiPCM ROM Image if (! CHIP_CHECK(MultiPCM)) break; multipcm_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8A: // UPD7759 ROM Image if (! CHIP_CHECK(UPD7759)) break; upd7759_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8B: // OKIM6295 ROM Image if (! CHIP_CHECK(OKIM6295)) break; okim6295_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8C: // K054539 ROM Image if (! CHIP_CHECK(K054539)) break; k054539_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8D: // C140 ROM Image if (! CHIP_CHECK(C140)) break; c140_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8E: // K053260 ROM Image if (! CHIP_CHECK(K053260)) break; k053260_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x8F: // QSound ROM Image if (! CHIP_CHECK(QSound)) break; qsound_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x90: // ES5506 ROM Image if (! CHIP_CHECK(ES5506)) break; es5506_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x91: // X1-010 ROM Image if (! CHIP_CHECK(X1_010)) break; x1_010_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x92: // C352 ROM Image if (! CHIP_CHECK(C352)) break; c352_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; case 0x93: // GA20 ROM Image if (! CHIP_CHECK(GA20)) break; iremga20_write_rom(CurChip, ROMSize, DataStart, DataLen, ROMData); break; // case 0x8C: // OKIM6376 ROM Image // if (! CHIP_CHECK(OKIM6376)) // break; // break; } break; case 0xC0: // RAM Write if (! (TempByt & 0x20)) { DataStart = ReadLE16(&VGMPnt[0x07]); DataLen = TempLng - 0x02; ROMData = &VGMPnt[0x09]; } else { DataStart = ReadLE32(&VGMPnt[0x07]); DataLen = TempLng - 0x04; ROMData = &VGMPnt[0x0B]; } switch(TempByt) { case 0xC0: // RF5C68 RAM Database if (! CHIP_CHECK(RF5C68)) break; rf5c68_write_ram(CurChip, DataStart, DataLen, ROMData); break; case 0xC1: // RF5C164 RAM Database if (! CHIP_CHECK(RF5C164)) break; rf5c164_write_ram(CurChip, DataStart, DataLen, ROMData); break; case 0xC2: // NES APU RAM if (! CHIP_CHECK(NES)) break; nes_write_ram(CurChip, DataStart, DataLen, ROMData); break; case 0xE0: // SCSP RAM if (! CHIP_CHECK(SCSP)) break; scsp_write_ram(CurChip, DataStart, DataLen, ROMData); break; case 0xE1: // ES5503 RAM if (! CHIP_CHECK(ES5503)) break; es5503_write_ram(CurChip, DataStart, DataLen, ROMData); break; } break; } VGMPos += 0x07 + TempLng; break; case 0xE0: // Seek to PCM Data Bank Pos PCMBank[0x00].DataPos = ReadLE32(&VGMPnt[0x01]); VGMPos += 0x05; break; case 0x31: // Set AY8910 stereo mask TempByt = VGMPnt[0x01]; TempLng = TempByt & 0x3F; CurChip = (TempByt & 0x80)? 1: 0; if (TempByt & 0x40) ym2203_set_stereo_mask_ay(CurChip, TempLng); else ayxx_set_stereo_mask(CurChip, TempLng); VGMPos += 0x02; break; case 0x4F: // GG Stereo if (CHIP_CHECK(SN76496)) { chip_reg_write(0x00, CurChip, 0x01, 0x00, VGMPnt[0x01]); } VGMPos += 0x02; break; case 0x54: // YM2151 write if (CHIP_CHECK(YM2151)) { chip_reg_write(0x03, CurChip, 0x01, VGMPnt[0x01], VGMPnt[0x02]); } //VGMSmplPos += 80; VGMPos += 0x03; break; case 0xC0: // Sega PCM memory write TempSht = ReadLE16(&VGMPnt[0x01]); CurChip = (TempSht & 0x8000) >> 15; if (CHIP_CHECK(SegaPCM)) { sega_pcm_w(CurChip, TempSht & 0x7FFF, VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xB0: // RF5C68 register write if (CHIP_CHECK(RF5C68)) { chip_reg_write(0x05, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xC1: // RF5C164 memory write if (CHIP_CHECK(RF5C68)) { TempSht = ReadLE16(&VGMPnt[0x01]); rf5c68_mem_w(CurChip, TempSht, VGMPnt[0x03]); } VGMPos += 0x04; break; case 0x55: // YM2203 if (CHIP_CHECK(YM2203)) { chip_reg_write(0x06, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x56: // YM2608 write port 0 case 0x57: // YM2608 write port 1 if (CHIP_CHECK(YM2608)) { chip_reg_write(0x07, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x58: // YM2610 write port 0 case 0x59: // YM2610 write port 1 if (CHIP_CHECK(YM2610)) { chip_reg_write(0x08, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x5A: // YM3812 write if (CHIP_CHECK(YM3812)) { chip_reg_write(0x09, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x5B: // YM3526 write if (CHIP_CHECK(YM3526)) { chip_reg_write(0x0A, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x5C: // Y8950 write if (CHIP_CHECK(Y8950)) { chip_reg_write(0x0B, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x5E: // YMF262 write port 0 case 0x5F: // YMF262 write port 1 if (CHIP_CHECK(YMF262)) { chip_reg_write(0x0C, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x5D: // YMZ280B write if (CHIP_CHECK(YMZ280B)) { chip_reg_write(0x0F, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xD0: // YMF278B write if (CHIP_CHECK(YMF278B)) { CurChip = (VGMPnt[0x01] & 0x80) >> 7; chip_reg_write(0x0D, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xD1: // YMF271 write if (CHIP_CHECK(YMF271)) { CurChip = (VGMPnt[0x01] & 0x80) >> 7; chip_reg_write(0x0E, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xB1: // RF5C164 register write if (CHIP_CHECK(RF5C164)) { chip_reg_write(0x10, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xC2: // RF5C164 memory write if (CHIP_CHECK(RF5C164)) { TempSht = ReadLE16(&VGMPnt[0x01]); rf5c164_mem_w(CurChip, TempSht, VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xB2: // PWM channel write if (CHIP_CHECK(PWM)) { chip_reg_write(0x11, CurChip, (VGMPnt[0x01] & 0xF0) >> 4, VGMPnt[0x01] & 0x0F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x68: // PCM RAM write CurChip = (VGMPnt[0x02] & 0x80) >> 7; TempByt = VGMPnt[0x02] & 0x7F; DataStart = ReadLE24(&VGMPnt[0x03]); TempLng = ReadLE24(&VGMPnt[0x06]); DataLen = ReadLE24(&VGMPnt[0x09]); if (! DataLen) DataLen += 0x01000000; ROMData = GetPointerFromPCMBank(TempByt, DataStart); if (ROMData == NULL) { VGMPos += 0x0C; break; } switch(TempByt) { case 0x01: if (! CHIP_CHECK(RF5C68)) break; rf5c68_write_ram(CurChip, TempLng, DataLen, ROMData); break; case 0x02: if (! CHIP_CHECK(RF5C164)) break; rf5c164_write_ram(CurChip, TempLng, DataLen, ROMData); break; case 0x06: if (! CHIP_CHECK(SCSP)) break; scsp_write_ram(CurChip, TempLng, DataLen, ROMData); break; case 0x07: if (! CHIP_CHECK(NES)) break; Last95Drum = DataStart / DataLen - 1; Last95Max = PCMBank[TempByt].DataSize / DataLen; nes_write_ram(CurChip, TempLng, DataLen, ROMData); break; } VGMPos += 0x0C; break; case 0xA0: // AY8910 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(AY8910)) { chip_reg_write(0x12, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xB3: // GameBoy DMG write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(GameBoy)) { chip_reg_write(0x13, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xB4: // NES APU write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(NES)) { chip_reg_write(0x14, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xB5: // MultiPCM write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(MultiPCM)) { chip_reg_write(0x15, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xC3: // MultiPCM memory write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(MultiPCM)) { TempSht = ReadLE16(&VGMPnt[0x02]); multipcm_bank_write(CurChip, VGMPnt[0x01] & 0x7F, TempSht); } VGMPos += 0x04; break; case 0xB6: // UPD7759 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(UPD7759)) { chip_reg_write(0x16, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xB7: // OKIM6258 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(OKIM6258)) { chip_reg_write(0x17, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xB8: // OKIM6295 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(OKIM6295)) { chip_reg_write(0x18, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xD2: // SCC1 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(K051649)) { chip_reg_write(0x19, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xD3: // K054539 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(K054539)) { chip_reg_write(0x1A, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xB9: // HuC6280 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(HuC6280)) { chip_reg_write(0x1B, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xD4: // C140 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(C140)) { chip_reg_write(0x1C, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xBA: // K053260 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(K053260)) { chip_reg_write(0x1D, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xBB: // Pokey write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(Pokey)) { chip_reg_write(0x1E, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xC4: // QSound write if (CHIP_CHECK(QSound)) { chip_reg_write(0x1F, CurChip, VGMPnt[0x01], VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xC5: // YMF292/SCSP write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(SCSP)) { chip_reg_write(0x20, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xBC: // WonderSwan write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(WSwan)) { chip_reg_write(0x21, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xC6: // WonderSwan memory write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(WSwan)) { TempSht = ReadBE16(&VGMPnt[0x01]) & 0x7FFF; ws_write_ram(CurChip, TempSht, VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xC7: // VSU write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(VSU)) { chip_reg_write(0x22, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xBD: // SAA1099 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(SAA1099)) { chip_reg_write(0x23, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xD5: // ES5503 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(ES5503)) { chip_reg_write(0x24, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xBE: // ES5506 write (8-bit data) CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(ES5506)) { chip_reg_write(0x25, CurChip, VGMPnt[0x01] & 0x7F, 0x00, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0xD6: // ES5506 write (16-bit data) CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(ES5506)) { chip_reg_write(0x25, CurChip, 0x80 | (VGMPnt[0x01] & 0x7F), VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; case 0xC8: // X1-010 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(X1_010)) { chip_reg_write(0x26, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; #if 0 // for ctr's WIP rips case 0xC9: // C352 write CurChip = 0x00; if (CHIP_CHECK(C352)) { if (VGMPnt[0x01] == 0x03 && VGMPnt[0x02] == 0xFF && VGMPnt[0x03] == 0xFF) c352_w(CurChip, 0x202, 0x0020); else chip_reg_write(0x27, CurChip, VGMPnt[0x01], VGMPnt[0x02], VGMPnt[0x03]); } VGMPos += 0x04; break; #endif case 0xE1: // C352 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(C352)) { TempSht = ((VGMPnt[0x01] & 0x7F) << 8) | (VGMPnt[0x02] << 0); c352_w(CurChip, TempSht, (VGMPnt[0x03] << 8) | VGMPnt[0x04]); } VGMPos += 0x05; break; case 0xBF: // GA20 write CurChip = (VGMPnt[0x01] & 0x80) >> 7; if (CHIP_CHECK(GA20)) { chip_reg_write(0x28, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); } VGMPos += 0x03; break; case 0x90: // DAC Ctrl: Setup Chip CurChip = VGMPnt[0x01]; if (CurChip == 0xFF) { VGMPos += 0x05; break; } if (! DacCtrl[CurChip].Enable) { device_start_daccontrol(CurChip); device_reset_daccontrol(CurChip); DacCtrl[CurChip].Enable = true; DacCtrlUsg[DacCtrlUsed] = CurChip; DacCtrlUsed ++; } TempByt = VGMPnt[0x02]; // Chip Type TempSht = ReadBE16(&VGMPnt[0x03]); daccontrol_setup_chip(CurChip, TempByt & 0x7F, (TempByt & 0x80) >> 7, TempSht); VGMPos += 0x05; break; case 0x91: // DAC Ctrl: Set Data CurChip = VGMPnt[0x01]; if (CurChip == 0xFF || ! DacCtrl[CurChip].Enable) { VGMPos += 0x05; break; } DacCtrl[CurChip].Bank = VGMPnt[0x02]; if (DacCtrl[CurChip].Bank >= PCM_BANK_COUNT) DacCtrl[CurChip].Bank = 0x00; TempPCM = &PCMBank[DacCtrl[CurChip].Bank]; Last95Max = TempPCM->BankCount; daccontrol_set_data(CurChip, TempPCM->Data, TempPCM->DataSize, VGMPnt[0x03], VGMPnt[0x04]); VGMPos += 0x05; break; case 0x92: // DAC Ctrl: Set Freq CurChip = VGMPnt[0x01]; if (CurChip == 0xFF || ! DacCtrl[CurChip].Enable) { VGMPos += 0x06; break; } TempLng = ReadLE32(&VGMPnt[0x02]); Last95Freq = TempLng; daccontrol_set_frequency(CurChip, TempLng); VGMPos += 0x06; break; case 0x93: // DAC Ctrl: Play from Start Pos CurChip = VGMPnt[0x01]; if (CurChip == 0xFF || ! DacCtrl[CurChip].Enable || ! PCMBank[DacCtrl[CurChip].Bank].BankCount) { VGMPos += 0x0B; break; } DataStart = ReadLE32(&VGMPnt[0x02]); Last95Drum = 0xFFFF; TempByt = VGMPnt[0x06]; DataLen = ReadLE32(&VGMPnt[0x07]); daccontrol_start(CurChip, DataStart, TempByt, DataLen); VGMPos += 0x0B; break; case 0x94: // DAC Ctrl: Stop immediately CurChip = VGMPnt[0x01]; if (! DacCtrl[CurChip].Enable) { VGMPos += 0x02; break; } Last95Drum = 0xFFFF; if (CurChip < 0xFF) { daccontrol_stop(CurChip); } else { for (CurChip = 0x00; CurChip < 0xFF; CurChip ++) daccontrol_stop(CurChip); } VGMPos += 0x02; break; case 0x95: // DAC Ctrl: Play Block (small) CurChip = VGMPnt[0x01]; if (CurChip == 0xFF || ! DacCtrl[CurChip].Enable || ! PCMBank[DacCtrl[CurChip].Bank].BankCount) { VGMPos += 0x05; break; } TempPCM = &PCMBank[DacCtrl[CurChip].Bank]; TempSht = ReadLE16(&VGMPnt[0x02]); Last95Drum = TempSht; Last95Max = TempPCM->BankCount; if (TempSht >= TempPCM->BankCount) TempSht = 0x00; TempBnk = &TempPCM->Bank[TempSht]; TempByt = DCTRL_LMODE_BYTES | (VGMPnt[0x04] & 0x10) | // Reverse Mode ((VGMPnt[0x04] & 0x01) << 7); // Looping daccontrol_start(CurChip, TempBnk->DataStart, TempByt, TempBnk->DataSize); VGMPos += 0x05; break; default: #ifdef CONSOLE_MODE if (! CmdList[Command]) { fprintf(stderr, "Unknown command: %02hhX\n", Command); CmdList[Command] = true; } #endif switch(Command & 0xF0) { case 0x00: case 0x10: case 0x20: VGMPos += 0x01; break; case 0x30: VGMPos += 0x02; break; case 0x40: case 0x50: case 0xA0: case 0xB0: VGMPos += 0x03; break; case 0xC0: case 0xD0: VGMPos += 0x04; break; case 0xE0: case 0xF0: VGMPos += 0x05; break; default: VGMEnd = true; EndPlay = true; break; } break; } } if (VGMPos >= VGMHead.lngEOFOffset) VGMEnd = true; if (VGMEnd) break; } return; } static void GeneralChipLists(void) { // Generate Chip List for playback loop UINT16 CurBufIdx; CA_LIST* CLstOld; CA_LIST* CLst; CA_LIST* CurLst; UINT8 CurChip; UINT8 CurCSet; CAUD_ATTR* CAA; ChipListAll = NULL; ChipListPause = NULL; //ChipListOpt = NULL; // generate list of all chips that are used in the current VGM CurBufIdx = 0x00; CLstOld = NULL; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { CAA = (CAUD_ATTR*)&ChipAudio[CurCSet] + CurChip; if (CAA->ChipType != 0xFF) { CLst = &ChipListBuffer[CurBufIdx]; CurBufIdx ++; if (CLstOld == NULL) ChipListAll = CLst; else CLstOld->next = CLst; CLst->CAud = CAA; CLst->COpts = (CHIP_OPTS*)&ChipOpts[CurCSet] + CurChip; CLstOld = CLst; } } } if (CLstOld != NULL) CLstOld->next = NULL; // Go through the chip list and copy all chips to a new list, except for a few // selected ones. CLstOld = NULL; CurLst = ChipListAll; while(CurLst != NULL) { // don't emulate the RF5Cxx chips when paused+emulated if (CurLst->CAud->ChipType != 0x05 && CurLst->CAud->ChipType != 0x10) { CLst = &ChipListBuffer[CurBufIdx]; CurBufIdx ++; if (CLstOld == NULL) ChipListPause = CLst; else CLstOld->next = CLst; *CLst = *CurLst; CLstOld = CLst; } CurLst = CurLst->next; } if (CLstOld != NULL) CLstOld->next = NULL; return; } static void SetupResampler(CAUD_ATTR* CAA) { if (! CAA->SmpRate) { CAA->Resampler = 0xFF; return; } if (CAA->SmpRate < SampleRate) CAA->Resampler = 0x01; else if (CAA->SmpRate == SampleRate) CAA->Resampler = 0x02; else if (CAA->SmpRate > SampleRate) CAA->Resampler = 0x03; if (CAA->Resampler == 0x01 || CAA->Resampler == 0x03) { if (ResampleMode == 0x02 || (ResampleMode == 0x01 && CAA->Resampler == 0x03)) CAA->Resampler = 0x00; } CAA->SmpP = 0x00; CAA->SmpLast = 0x00; CAA->SmpNext = 0x00; CAA->LSmpl.Left = 0x00; CAA->LSmpl.Right = 0x00; if (CAA->Resampler == 0x01) { // Pregenerate first Sample (the upsampler is always one too late) CAA->StreamUpdate(CAA->ChipID, StreamBufs, 1); CAA->NSmpl.Left = StreamBufs[0x00][0x00]; CAA->NSmpl.Right = StreamBufs[0x01][0x00]; } else { CAA->NSmpl.Left = 0x00; CAA->NSmpl.Right = 0x00; } return; } static void ChangeChipSampleRate(void* DataPtr, UINT32 NewSmplRate) { CAUD_ATTR* CAA = (CAUD_ATTR*)DataPtr; if (CAA->SmpRate == NewSmplRate) return; // quick and dirty hack to make sample rate changes work CAA->SmpRate = NewSmplRate; if (CAA->SmpRate < SampleRate) CAA->Resampler = 0x01; else if (CAA->SmpRate == SampleRate) CAA->Resampler = 0x02; else if (CAA->SmpRate > SampleRate) CAA->Resampler = 0x03; CAA->SmpP = 1; CAA->SmpNext -= CAA->SmpLast; CAA->SmpLast = 0x00; return; } INLINE INT16 Limit2Short(INT32 Value) { INT32 NewValue; NewValue = Value; if (NewValue < -0x8000) NewValue = -0x8000; if (NewValue > 0x7FFF) NewValue = 0x7FFF; return (INT16)NewValue; } static void null_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { memset(outputs[0x00], 0x00, sizeof(stream_sample_t) * samples); memset(outputs[0x01], 0x00, sizeof(stream_sample_t) * samples); return; } static void dual_opl2_stereo(UINT8 ChipID, stream_sample_t **outputs, int samples) { ym3812_stream_update(ChipID, outputs, samples); // Dual-OPL with Stereo if (ChipID & 0x01) memset(outputs[0x00], 0x00, sizeof(stream_sample_t) * samples); // Mute Left Chanel else memset(outputs[0x01], 0x00, sizeof(stream_sample_t) * samples); // Mute Right Chanel return; } // I recommend 11 bits as it's fast and accurate #define FIXPNT_BITS 11 #define FIXPNT_FACT (1 << FIXPNT_BITS) #if (FIXPNT_BITS <= 11) typedef UINT32 SLINT; // 32-bit is a lot faster #else typedef UINT64 SLINT; #endif #define FIXPNT_MASK (FIXPNT_FACT - 1) #define getfriction(x) ((x) & FIXPNT_MASK) #define getnfriction(x) ((FIXPNT_FACT - (x)) & FIXPNT_MASK) #define fpi_floor(x) ((x) & ~FIXPNT_MASK) #define fpi_ceil(x) ((x + FIXPNT_MASK) & ~FIXPNT_MASK) #define fp2i_floor(x) ((x) / FIXPNT_FACT) #define fp2i_ceil(x) ((x + FIXPNT_MASK) / FIXPNT_FACT) static void ResampleChipStream(CA_LIST* CLst, WAVE_32BS* RetSample, UINT32 Length) { CAUD_ATTR* CAA; INT32* CurBufL; INT32* CurBufR; INT32* StreamPnt[0x02]; UINT32 InBase; UINT32 InPos; UINT32 InPosNext; UINT32 OutPos; UINT32 SmpFrc; // Sample Friction UINT32 InPre; UINT32 InNow; SLINT InPosL; INT64 TempSmpL; INT64 TempSmpR; INT32 TempS32L; INT32 TempS32R; INT32 SmpCnt; // must be signed, else I'm getting calculation errors INT32 CurSmpl; UINT64 ChipSmpRate; CAA = CLst->CAud; CurBufL = StreamBufs[0x00]; CurBufR = StreamBufs[0x01]; // This Do-While-Loop gets and resamples the chip output of one or more chips. // It's a loop to support the AY8910 paired with the YM2203/YM2608/YM2610. do { switch(CAA->Resampler) { case 0x00: // old, but very fast resampler CAA->SmpLast = CAA->SmpNext; CAA->SmpP += Length; CAA->SmpNext = (UINT32)((UINT64)CAA->SmpP * CAA->SmpRate / SampleRate); if (CAA->SmpLast >= CAA->SmpNext) { RetSample->Left += CAA->LSmpl.Left * CAA->Volume; RetSample->Right += CAA->LSmpl.Right * CAA->Volume; } else { SmpCnt = CAA->SmpNext - CAA->SmpLast; CAA->StreamUpdate(CAA->ChipID, StreamBufs, SmpCnt); if (SmpCnt == 1) { RetSample->Left += CurBufL[0x00] * CAA->Volume; RetSample->Right += CurBufR[0x00] * CAA->Volume; CAA->LSmpl.Left = CurBufL[0x00]; CAA->LSmpl.Right = CurBufR[0x00]; } else if (SmpCnt == 2) { RetSample->Left += (CurBufL[0x00] + CurBufL[0x01]) * CAA->Volume >> 1; RetSample->Right += (CurBufR[0x00] + CurBufR[0x01]) * CAA->Volume >> 1; CAA->LSmpl.Left = CurBufL[0x01]; CAA->LSmpl.Right = CurBufR[0x01]; } else { TempS32L = CurBufL[0x00]; TempS32R = CurBufR[0x00]; for (CurSmpl = 0x01; CurSmpl < SmpCnt; CurSmpl ++) { TempS32L += CurBufL[CurSmpl]; TempS32R += CurBufR[CurSmpl]; } RetSample->Left += TempS32L * CAA->Volume / SmpCnt; RetSample->Right += TempS32R * CAA->Volume / SmpCnt; CAA->LSmpl.Left = CurBufL[SmpCnt - 1]; CAA->LSmpl.Right = CurBufR[SmpCnt - 1]; } } break; case 0x01: // Upsampling ChipSmpRate = CAA->SmpRate; InPosL = (SLINT)(FIXPNT_FACT * CAA->SmpP * ChipSmpRate / SampleRate); InPre = (UINT32)fp2i_floor(InPosL); InNow = (UINT32)fp2i_ceil(InPosL); /*if (InNow - CAA->SmpNext >= SMPL_BUFSIZE) { fprintf(stderr, "Sample Buffer Overflow!\n"); #ifdef _DEBUG *(char*)NULL = 0; #endif CAA->SmpLast = 0; CAA->SmpNext = 0; CAA->SmpP = 0; break; }*/ CurBufL[0x00] = CAA->LSmpl.Left; CurBufR[0x00] = CAA->LSmpl.Right; CurBufL[0x01] = CAA->NSmpl.Left; CurBufR[0x01] = CAA->NSmpl.Right; StreamPnt[0x00] = &CurBufL[0x02]; StreamPnt[0x01] = &CurBufR[0x02]; CAA->StreamUpdate(CAA->ChipID, StreamPnt, InNow - CAA->SmpNext); InBase = FIXPNT_FACT + (UINT32)(InPosL - (SLINT)CAA->SmpNext * FIXPNT_FACT); /*if (fp2i_floor(InBase) >= SMPL_BUFSIZE) { fprintf(stderr, "Sample Buffer Overflow!\n"); #ifdef _DEBUG *(char*)NULL = 0; #endif CAA->SmpLast = 0; CAA->SmpP = 0; break; }*/ SmpCnt = FIXPNT_FACT; CAA->SmpLast = InPre; CAA->SmpNext = InNow; for (OutPos = 0x00; OutPos < Length; OutPos ++) { InPos = InBase + (UINT32)(FIXPNT_FACT * OutPos * ChipSmpRate / SampleRate); InPre = fp2i_floor(InPos); InNow = fp2i_ceil(InPos); SmpFrc = getfriction(InPos); // Linear interpolation TempSmpL = ((INT64)CurBufL[InPre] * (FIXPNT_FACT - SmpFrc)) + ((INT64)CurBufL[InNow] * SmpFrc); TempSmpR = ((INT64)CurBufR[InPre] * (FIXPNT_FACT - SmpFrc)) + ((INT64)CurBufR[InNow] * SmpFrc); RetSample[OutPos].Left += (INT32)(TempSmpL * CAA->Volume / SmpCnt); RetSample[OutPos].Right += (INT32)(TempSmpR * CAA->Volume / SmpCnt); } CAA->LSmpl.Left = CurBufL[InPre]; CAA->LSmpl.Right = CurBufR[InPre]; CAA->NSmpl.Left = CurBufL[InNow]; CAA->NSmpl.Right = CurBufR[InNow]; CAA->SmpP += Length; break; case 0x02: // Copying CAA->SmpNext = CAA->SmpP * CAA->SmpRate / SampleRate; CAA->StreamUpdate(CAA->ChipID, StreamBufs, Length); for (OutPos = 0x00; OutPos < Length; OutPos ++) { RetSample[OutPos].Left += CurBufL[OutPos] * CAA->Volume; RetSample[OutPos].Right += CurBufR[OutPos] * CAA->Volume; } CAA->SmpP += Length; CAA->SmpLast = CAA->SmpNext; break; case 0x03: // Downsampling ChipSmpRate = CAA->SmpRate; InPosL = (SLINT)(FIXPNT_FACT * (CAA->SmpP + Length) * ChipSmpRate / SampleRate); CAA->SmpNext = (UINT32)fp2i_ceil(InPosL); CurBufL[0x00] = CAA->LSmpl.Left; CurBufR[0x00] = CAA->LSmpl.Right; StreamPnt[0x00] = &CurBufL[0x01]; StreamPnt[0x01] = &CurBufR[0x01]; CAA->StreamUpdate(CAA->ChipID, StreamPnt, CAA->SmpNext - CAA->SmpLast); InPosL = (SLINT)(FIXPNT_FACT * CAA->SmpP * ChipSmpRate / SampleRate); // I'm adding 1.0 to avoid negative indexes InBase = FIXPNT_FACT + (UINT32)(InPosL - (SLINT)CAA->SmpLast * FIXPNT_FACT); InPosNext = InBase; for (OutPos = 0x00; OutPos < Length; OutPos ++) { //InPos = InBase + (UINT32)(FIXPNT_FACT * OutPos * ChipSmpRate / SampleRate); InPos = InPosNext; InPosNext = InBase + (UINT32)(FIXPNT_FACT * (OutPos+1) * ChipSmpRate / SampleRate); // first frictional Sample SmpFrc = getnfriction(InPos); if (SmpFrc) { InPre = fp2i_floor(InPos); TempSmpL = (INT64)CurBufL[InPre] * SmpFrc; TempSmpR = (INT64)CurBufR[InPre] * SmpFrc; } else { TempSmpL = TempSmpR = 0x00; } SmpCnt = SmpFrc; // last frictional Sample SmpFrc = getfriction(InPosNext); InPre = fp2i_floor(InPosNext); if (SmpFrc) { TempSmpL += (INT64)CurBufL[InPre] * SmpFrc; TempSmpR += (INT64)CurBufR[InPre] * SmpFrc; SmpCnt += SmpFrc; } // whole Samples in between //InPre = fp2i_floor(InPosNext); InNow = fp2i_ceil(InPos); SmpCnt += (InPre - InNow) * FIXPNT_FACT; // this is faster while(InNow < InPre) { TempSmpL += (INT64)CurBufL[InNow] * FIXPNT_FACT; TempSmpR += (INT64)CurBufR[InNow] * FIXPNT_FACT; //SmpCnt ++; InNow ++; } RetSample[OutPos].Left += (INT32)(TempSmpL * CAA->Volume / SmpCnt); RetSample[OutPos].Right += (INT32)(TempSmpR * CAA->Volume / SmpCnt); } CAA->LSmpl.Left = CurBufL[InPre]; CAA->LSmpl.Right = CurBufR[InPre]; CAA->SmpP += Length; CAA->SmpLast = CAA->SmpNext; break; default: CAA->SmpP += SampleRate; break; // do absolutely nothing } if (CAA->SmpLast >= CAA->SmpRate) { CAA->SmpLast -= CAA->SmpRate; CAA->SmpNext -= CAA->SmpRate; CAA->SmpP -= SampleRate; } CAA = CAA->Paired; } while(CAA != NULL); return; } static INT32 RecalcFadeVolume(void) { float TempSng; if (FadePlay) { if (! FadeStart) FadeStart = PlayingTime; TempSng = (PlayingTime - FadeStart) / (float)SampleRate; MasterVol = 1.0f - TempSng / (FadeTime * 0.001f); if (MasterVol < 0.0f) { MasterVol = 0.0f; //EndPlay = true; VGMEnd = true; } FinalVol = VolumeLevelM * MasterVol * MasterVol; } return (INT32)(0x100 * FinalVol + 0.5f); } UINT32 FillBuffer(WAVE_16BS* Buffer, UINT32 BufferSize) { UINT32 CurSmpl; WAVE_32BS TempBuf; INT32 CurMstVol; UINT32 RecalcStep; CA_LIST* CurCLst; //memset(Buffer, 0x00, sizeof(WAVE_16BS) * BufferSize); RecalcStep = FadePlay ? SampleRate / 44100 : 0; CurMstVol = RecalcFadeVolume(); if (Buffer == NULL) { //for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++) // InterpretFile(1); InterpretFile(BufferSize); if (FadePlay && ! FadeStart) { FadeStart = PlayingTime; RecalcStep = FadePlay ? SampleRate / 100 : 0; } //if (RecalcStep && ! (CurSmpl % RecalcStep)) if (RecalcStep) CurMstVol = RecalcFadeVolume(); if (VGMEnd) { if (PauseSmpls <= BufferSize) { PauseSmpls = 0; EndPlay = true; } else { PauseSmpls -= BufferSize; } } return BufferSize; } CurChipList = (VGMEnd || PausePlay) ? ChipListPause : ChipListAll; for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++) { InterpretFile(1); // Sample Structures // 00 - SN76496 // 01 - YM2413 // 02 - YM2612 // 03 - YM2151 // 04 - SegaPCM // 05 - RF5C68 // 06 - YM2203 // 07 - YM2608 // 08 - YM2610/YM2610B // 09 - YM3812 // 0A - YM3526 // 0B - Y8950 // 0C - YMF262 // 0D - YMF278B // 0E - YMF271 // 0F - YMZ280B // 10 - RF5C164 // 11 - PWM // 12 - AY8910 // 13 - GameBoy // 14 - NES APU // 15 - MultiPCM // 16 - UPD7759 // 17 - OKIM6258 // 18 - OKIM6295 // 19 - K051649 // 1A - K054539 // 1B - HuC6280 // 1C - C140 // 1D - K053260 // 1E - Pokey // 1F - QSound // 20 - YMF292/SCSP // 21 - WonderSwan // 22 - VSU // 23 - SAA1099 // 24 - ES5503 // 25 - ES5506 // 26 - X1-010 // 27 - C352 // 28 - GA20 TempBuf.Left = 0x00; TempBuf.Right = 0x00; CurCLst = CurChipList; while(CurCLst != NULL) { if (! CurCLst->COpts->Disabled) { ResampleChipStream(CurCLst, &TempBuf, 1); } CurCLst = CurCLst->next; } // ChipData << 9 [ChipVol] >> 5 << 8 [MstVol] >> 11 -> 9-5+8-11 = <<1 TempBuf.Left = ((TempBuf.Left >> 5) * CurMstVol) >> 11; TempBuf.Right = ((TempBuf.Right >> 5) * CurMstVol) >> 11; if (SurroundSound) TempBuf.Right *= -1; Buffer[CurSmpl].Left = Limit2Short(TempBuf.Left); Buffer[CurSmpl].Right = Limit2Short(TempBuf.Right); if (FadePlay && ! FadeStart) { FadeStart = PlayingTime; RecalcStep = FadePlay ? SampleRate / 100 : 0; } if (RecalcStep && ! (CurSmpl % RecalcStep)) CurMstVol = RecalcFadeVolume(); if (VGMEnd) { if (! PauseSmpls) { //if (! FullBufFill) if (! EndPlay) { EndPlay = true; break; } } else //if (PauseSmpls) { PauseSmpls --; } } } return CurSmpl; } #ifdef WIN32 DWORD WINAPI PlayingThread(void* Arg) { LARGE_INTEGER CPUFreq; LARGE_INTEGER TimeNow; LARGE_INTEGER TimeLast; UINT64 TimeDiff; UINT64 SampleTick; UINT64 Ticks; BOOL RetVal; hPlayThread = GetCurrentThread(); #ifndef _DEBUG RetVal = SetThreadPriority(hPlayThread, THREAD_PRIORITY_TIME_CRITICAL); #else RetVal = SetThreadPriority(hPlayThread, THREAD_PRIORITY_ABOVE_NORMAL); #endif if (! RetVal) { // Error by setting priority } QueryPerformanceFrequency(&CPUFreq); QueryPerformanceCounter(&TimeNow); TimeLast = TimeNow; SampleTick = CPUFreq.QuadPart / SampleRate; while (! CloseThread) { while(PlayingMode != 0x01 && ! CloseThread) Sleep(1); if (! PauseThread) { TimeDiff = TimeNow.QuadPart - TimeLast.QuadPart; if (TimeDiff >= SampleTick) { Ticks = TimeDiff * SampleRate / CPUFreq.QuadPart; if (Ticks > SampleRate / 2) Ticks = SampleRate / 50; FillBuffer(NULL, (UINT32)Ticks); if (! ResetPBTimer) { TimeLast = TimeNow; } else { QueryPerformanceCounter(&TimeLast); TimeLast.QuadPart -= TimeDiff; ResetPBTimer = false; } } // I tried to make sample-accurate replaying through Hardware FM // to make PSG-PCM clear, but it didn't work //if (! FMAccurate) Sleep(1); //else // Sleep(0); } else { ThreadPauseConfrm = true; if (! ThreadNoWait) TimeLast = TimeNow; Sleep(1); } QueryPerformanceCounter(&TimeNow); } hPlayThread = NULL; return 0x00000000; } #else UINT64 TimeSpec2Int64(const struct timespec* ts) { return (UINT64)ts->tv_sec * 1000000000 + ts->tv_nsec; } void* PlayingThread(void* Arg) { UINT64 CPUFreq; UINT64 TimeNow; UINT64 TimeLast; UINT64 TimeDiff; UINT64 SampleTick; UINT64 Ticks; struct timespec TempTS; int RetVal; //RetVal = clock_getres(CLOCK_MONOTONIC, &TempTS); //CPUFreq = TimeSpec2Int64(&TempTS); CPUFreq = 1000000000; RetVal = clock_gettime(CLOCK_MONOTONIC, &TempTS); TimeNow = TimeSpec2Int64(&TempTS); TimeLast = TimeNow; SampleTick = CPUFreq / SampleRate; while (! CloseThread) { while(PlayingMode != 0x01 && ! CloseThread) Sleep(1); if (! PauseThread) { TimeDiff = TimeNow - TimeLast; if (TimeDiff >= SampleTick) { Ticks = TimeDiff * SampleRate / CPUFreq; if (Ticks > SampleRate / 2) Ticks = SampleRate / 50; FillBuffer(NULL, (UINT32)Ticks); if (! ResetPBTimer) { TimeLast = TimeNow; } else { RetVal = clock_gettime(CLOCK_MONOTONIC, &TempTS); TimeLast = TimeSpec2Int64(&TempTS) - TimeDiff; ResetPBTimer = false; } } //if (! FMAccurate) Sleep(1); //else // Sleep(0); } else { ThreadPauseConfrm = true; if (ThreadNoWait) TimeLast = TimeNow; Sleep(1); } RetVal = clock_gettime(CLOCK_MONOTONIC, &TempTS); TimeNow = TimeSpec2Int64(&TempTS); } return NULL; } #endif ================================================ FILE: VGMPlay/VGMPlay.dsp ================================================ # Microsoft Developer Studio Project File - Name="VGMPlay" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** NICHT BEARBEITEN ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=VGMPlay - Win32 Debug !MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE !MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl !MESSAGE !MESSAGE NMAKE /f "VGMPlay.mak". !MESSAGE !MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben !MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: !MESSAGE !MESSAGE NMAKE /f "VGMPlay.mak" CFG="VGMPlay - Win32 Debug" !MESSAGE !MESSAGE Fr die Konfiguration stehen zur Auswahl: !MESSAGE !MESSAGE "VGMPlay - Win32 Release" (basierend auf "Win32 (x86) Console Application") !MESSAGE "VGMPlay - Win32 Debug" (basierend auf "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "VGMPlay - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c # ADD CPP /nologo /MD /W3 /GX /Ox /Ot /Og /Oi /Ob2 /I "zlib" /D "NDEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FD /c # SUBTRACT CPP /Oa /Ow /YX /Yc /Yu # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 msvcrt.lib oldnames.lib kernel32.lib user32.lib advapi32.lib winmm.lib zdll.lib /nologo /subsystem:console /machine:I386 /nodefaultlib /libpath:"zlib" # Begin Special Build Tool SOURCE="$(InputPath)" PostBuild_Cmds=..\vgm2txt\HiddenMsg.exe Release\VGMPlay.exe # End Special Build Tool !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "zlib" /D "_DEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FR /FD /GZ /c # SUBTRACT CPP /YX /Yc /Yu # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 msvcrtd.lib oldnames.lib kernel32.lib user32.lib advapi32.lib winmm.lib zlib.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib /libpath:"zlib" # SUBTRACT LINK32 /profile /map !ENDIF # Begin Target # Name "VGMPlay - Win32 Release" # Name "VGMPlay - Win32 Debug" # Begin Group "Quellcodedateien" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\dbus_stub.c # End Source File # Begin Source File SOURCE=.\mmkeys_Win.c # End Source File # Begin Source File SOURCE=.\pt_ioctl.c # End Source File # Begin Source File SOURCE=.\Stream.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\VGMPlay.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\VGMPlay_AddFmts.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\VGMPlayUI.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # End Group # Begin Group "Header-Dateien" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\dbus.h # End Source File # Begin Source File SOURCE=.\mmkeys.h # End Source File # Begin Source File SOURCE=.\PortTalk_IOCTL.h # End Source File # Begin Source File SOURCE=.\Stream.h # End Source File # Begin Source File SOURCE=".\XMasFiles\SWJ-SQRC01_1C.h" # End Source File # Begin Source File SOURCE=.\VGMFile.h # End Source File # Begin Source File SOURCE=.\VGMPlay.h # End Source File # Begin Source File SOURCE=.\VGMPlay_Intf.h # End Source File # Begin Source File SOURCE=.\XMasFiles\XMasBonus.h # End Source File # End Group # Begin Group "Ressourcendateien" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # Begin Group "SoundCore" # PROP Default_Filter "c;h;cpp" # Begin Group "FM OPL Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2413intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2413intf.h # End Source File # Begin Source File SOURCE=.\chips\262intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\262intf.h # End Source File # Begin Source File SOURCE=.\chips\3526intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\3526intf.h # End Source File # Begin Source File SOURCE=.\chips\3812intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\3812intf.h # End Source File # Begin Source File SOURCE=.\chips\8950intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\8950intf.h # End Source File # Begin Source File SOURCE=.\chips\adlibemu.h # End Source File # Begin Source File SOURCE=.\chips\adlibemu_opl2.c # End Source File # Begin Source File SOURCE=.\chips\adlibemu_opl3.c # End Source File # Begin Source File SOURCE=.\chips\emu2413.c # End Source File # Begin Source File SOURCE=.\chips\emu2413.h # End Source File # Begin Source File SOURCE=.\chips\emutypes.h # End Source File # Begin Source File SOURCE=.\chips\fmopl.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\fmopl.h # End Source File # Begin Source File SOURCE=.\chips\opl.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=.\chips\opl.h # End Source File # Begin Source File SOURCE=.\chips\opll.c # End Source File # Begin Source File SOURCE=.\chips\opll.h # End Source File # Begin Source File SOURCE=.\chips\vrc7tone.h # End Source File # Begin Source File SOURCE=.\chips\ym2413.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ym2413.h # End Source File # Begin Source File SOURCE=.\chips\ymf262.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf262.h # End Source File # Begin Source File SOURCE=.\chips\ymf278b.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf278b.h # End Source File # End Group # Begin Group "FM OPN Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2203intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2203intf.h # End Source File # Begin Source File SOURCE=.\chips\2608intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2608intf.h # End Source File # Begin Source File SOURCE=.\chips\2610intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2610intf.h # End Source File # Begin Source File SOURCE=.\chips\2612intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2612intf.h # End Source File # Begin Source File SOURCE=.\chips\fm.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\fm.h # End Source File # Begin Source File SOURCE=.\chips\fm2612.c # End Source File # Begin Source File SOURCE=.\chips\ym2612.c # End Source File # Begin Source File SOURCE=.\chips\ym2612.h # End Source File # Begin Source File SOURCE=.\chips\ym3438.c # End Source File # Begin Source File SOURCE=.\chips\ym3438.h # End Source File # End Group # Begin Group "FM OPx Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2151intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2151intf.h # End Source File # Begin Source File SOURCE=.\chips\opm.c # End Source File # Begin Source File SOURCE=.\chips\opm.h # End Source File # Begin Source File SOURCE=.\chips\scsp.c # End Source File # Begin Source File SOURCE=.\chips\scsp.h # End Source File # Begin Source File SOURCE=.\chips\scspdsp.c # End Source File # Begin Source File SOURCE=.\chips\scspdsp.h # End Source File # Begin Source File SOURCE=.\chips\scsplfo.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=.\chips\ym2151.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ym2151.h # End Source File # Begin Source File SOURCE=.\chips\ymf271.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf271.h # End Source File # End Group # Begin Group "PCM Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\c140.c # End Source File # Begin Source File SOURCE=.\chips\c140.h # End Source File # Begin Source File SOURCE=.\chips\c352.c # End Source File # Begin Source File SOURCE=.\chips\c352.h # End Source File # Begin Source File SOURCE=.\chips\es5503.c # End Source File # Begin Source File SOURCE=.\chips\es5503.h # End Source File # Begin Source File SOURCE=.\chips\es5506.c # End Source File # Begin Source File SOURCE=.\chips\es5506.h # End Source File # Begin Source File SOURCE=.\chips\iremga20.c # End Source File # Begin Source File SOURCE=.\chips\iremga20.h # End Source File # Begin Source File SOURCE=.\chips\k053260.c # End Source File # Begin Source File SOURCE=.\chips\k053260.h # End Source File # Begin Source File SOURCE=.\chips\k054539.c # End Source File # Begin Source File SOURCE=.\chips\k054539.h # End Source File # Begin Source File SOURCE=.\chips\multipcm.c # End Source File # Begin Source File SOURCE=.\chips\multipcm.h # End Source File # Begin Source File SOURCE=.\chips\okim6258.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\okim6258.h # End Source File # Begin Source File SOURCE=.\chips\okim6295.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\okim6295.h # End Source File # Begin Source File SOURCE=.\chips\pwm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\pwm.h # End Source File # Begin Source File SOURCE=.\chips\qsound_ctr.c # End Source File # Begin Source File SOURCE=.\chips\qsound_ctr.h # End Source File # Begin Source File SOURCE=.\chips\qsound_intf.c # End Source File # Begin Source File SOURCE=.\chips\qsound_intf.h # End Source File # Begin Source File SOURCE=.\chips\qsound_mame.c # End Source File # Begin Source File SOURCE=.\chips\qsound_mame.h # End Source File # Begin Source File SOURCE=.\chips\rf5c68.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\rf5c68.h # End Source File # Begin Source File SOURCE=.\chips\scd_pcm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\scd_pcm.h # End Source File # Begin Source File SOURCE=.\chips\segapcm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\segapcm.h # End Source File # Begin Source File SOURCE=.\chips\upd7759.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\upd7759.h # End Source File # Begin Source File SOURCE=.\chips\x1_010.c # End Source File # Begin Source File SOURCE=.\chips\x1_010.h # End Source File # Begin Source File SOURCE=.\chips\ymdeltat.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ymdeltat.h # End Source File # Begin Source File SOURCE=.\chips\ymz280b.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ymz280b.h # End Source File # End Group # Begin Group "OPL Mapper" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\ay8910_opl.c # End Source File # Begin Source File SOURCE=.\chips\sn76496_opl.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413_opl.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413hd.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413hd.h # End Source File # End Group # Begin Group "PSG Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\ay8910.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ay8910.h # End Source File # Begin Source File SOURCE=.\chips\ay_intf.c # End Source File # Begin Source File SOURCE=.\chips\ay_intf.h # End Source File # Begin Source File SOURCE=.\chips\c6280.c # End Source File # Begin Source File SOURCE=.\chips\c6280.h # End Source File # Begin Source File SOURCE=.\chips\c6280intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\c6280intf.h # End Source File # Begin Source File SOURCE=.\chips\emu2149.c # End Source File # Begin Source File SOURCE=.\chips\emu2149.h # End Source File # Begin Source File SOURCE=.\chips\gb.c # End Source File # Begin Source File SOURCE=.\chips\gb.h # End Source File # Begin Source File SOURCE=.\chips\k051649.c # End Source File # Begin Source File SOURCE=.\chips\k051649.h # End Source File # Begin Source File SOURCE=.\chips\Ootake_PSG.c # End Source File # Begin Source File SOURCE=.\chips\Ootake_PSG.h # End Source File # Begin Source File SOURCE=.\chips\pokey.c # End Source File # Begin Source File SOURCE=.\chips\pokey.h # End Source File # Begin Source File SOURCE=.\chips\saa1099.c # End Source File # Begin Source File SOURCE=.\chips\saa1099.h # End Source File # Begin Source File SOURCE=.\chips\sn76489.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn76489.h # End Source File # Begin Source File SOURCE=.\chips\sn76496.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn76496.h # End Source File # Begin Source File SOURCE=.\chips\sn764intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn764intf.h # End Source File # Begin Source File SOURCE=.\chips\vsu.c # End Source File # Begin Source File SOURCE=.\chips\vsu.h # End Source File # Begin Source File SOURCE=.\chips\ws_audio.c # End Source File # Begin Source File SOURCE=.\chips\ws_audio.h # End Source File # Begin Source File SOURCE=.\chips\ws_initialIo.h # End Source File # End Group # Begin Group "NES Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\nes_apu.c # End Source File # Begin Source File SOURCE=.\chips\nes_apu.h # End Source File # Begin Source File SOURCE=.\chips\nes_defs.h # End Source File # Begin Source File SOURCE=.\chips\nes_intf.c # End Source File # Begin Source File SOURCE=.\chips\nes_intf.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_apu.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_apu.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_dmc.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_dmc.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_fds.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_fds.h # End Source File # End Group # Begin Source File SOURCE=.\chips\ChipIncl.h # End Source File # Begin Source File SOURCE=.\ChipMapper.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\ChipMapper.h # End Source File # Begin Source File SOURCE=.\chips\dac_control.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\dac_control.h # End Source File # Begin Source File SOURCE=.\chips\mamedef.h # End Source File # Begin Source File SOURCE=.\chips\panning.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\panning.h # End Source File # End Group # Begin Source File SOURCE=.\ReadMe.txt # End Source File # End Target # End Project ================================================ FILE: VGMPlay/VGMPlay.dsw ================================================ Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELSCHT WERDEN! ############################################################################### Project: "VGMPlay"=".\VGMPlay.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### ================================================ FILE: VGMPlay/VGMPlay.h ================================================ // Header File for structures and constants used within VGMPlay.c #include "VGMFile.h" #define VGMPLAY_VER_STR "0.40.9.1" //#define APLHA //#define BETA #define VGM_VER_STR "1.71b" #define VGM_VER_NUM 0x170 #define CHIP_COUNT 0x29 typedef struct chip_options { bool Disabled; UINT8 EmuCore; UINT8 ChnCnt; // Special Flags: // YM2612: Bit 0 - DAC Highpass Enable, Bit 1 - SSG-EG Enable // YM-OPN: Bit 0 - Disable AY8910-Part UINT16 SpecialFlags; // Channel Mute Mask - 1 Channel is represented by 1 bit UINT32 ChnMute1; // Mask 2 - used by YMF287B for OPL4 Wavetable Synth and by YM2608/YM2610 for PCM UINT32 ChnMute2; // Mask 3 - used for the AY-part of some OPN-chips UINT32 ChnMute3; INT16* Panning; } CHIP_OPTS; typedef struct chips_options { CHIP_OPTS SN76496; CHIP_OPTS YM2413; CHIP_OPTS YM2612; CHIP_OPTS YM2151; CHIP_OPTS SegaPCM; CHIP_OPTS RF5C68; CHIP_OPTS YM2203; CHIP_OPTS YM2608; CHIP_OPTS YM2610; CHIP_OPTS YM3812; CHIP_OPTS YM3526; CHIP_OPTS Y8950; CHIP_OPTS YMF262; CHIP_OPTS YMF278B; CHIP_OPTS YMF271; CHIP_OPTS YMZ280B; CHIP_OPTS RF5C164; CHIP_OPTS PWM; CHIP_OPTS AY8910; CHIP_OPTS GameBoy; CHIP_OPTS NES; CHIP_OPTS MultiPCM; CHIP_OPTS UPD7759; CHIP_OPTS OKIM6258; CHIP_OPTS OKIM6295; CHIP_OPTS K051649; CHIP_OPTS K054539; CHIP_OPTS HuC6280; CHIP_OPTS C140; CHIP_OPTS K053260; CHIP_OPTS Pokey; CHIP_OPTS QSound; CHIP_OPTS SCSP; CHIP_OPTS WSwan; CHIP_OPTS VSU; CHIP_OPTS SAA1099; CHIP_OPTS ES5503; CHIP_OPTS ES5506; CHIP_OPTS X1_010; CHIP_OPTS C352; CHIP_OPTS GA20; // CHIP_OPTS OKIM6376; } CHIPS_OPTION; ================================================ FILE: VGMPlay/VGMPlay.ini ================================================ ; VGMPlay Configuration File ; -------------------------- ; ; Default Values are usually 0 (False for boolean ones) ; Boolean Values are: ; False / 0 ; True / 1 [General] ; Default Sample Rate: 44100 SampleRate = 48000 ; If you set PlaybackRate to 50, some songs will play slower, like on a PAL console. ; If you set it to 60, some songs may play faster, like PAL games on a NTSC console. PlaybackRate = 0 ; double the volume of the YM2xxx SSG, if it gets overridden by a VGM's header. ; This option will be removed once all VGMs on vgmrips are fixed. DoubleSSGVol = True ; Display Japanese GD3 Tag if available ; Most western Windows systems won't be able to display Japanese characters in the normal console, ; so use at own risk. (Linux doesn't have this problem.) PreferJapTag = False ; Default Fade Time: 5000 FadeTime = 8000 ; In-Playlist Fade Time: for all looping tracks in a playlist (except the last one) ; Setting this to 0 simulate a Song-Medley without gaps. ; Default Fade Time in Playlist: 2000 FadeTimePL = 0 JinglePause = 1000 ; enforce silence at the end of old VGMs (version <1.50), enable to fix hanging notes in playlists HardStopOld = False ; Fade RAW logs from emulators (VGMs without Creator-Tag) so that they don't ; end abruptly at the full volume level but at 33% FadeRAWLogs = False ; Default Volume: 1.0 (of course) Volume = 1.0 ; Log Sound to Wave: 0 - no logging, 1 - log only, 2 - play and log LogSound = 0 ; Maximum Loops before fading ; Default: 0x02 (0x01 for CMF) MaxLoops = 0x02 MaxLoopsCMF = 0x01 ; Resampling Mode: ; 0 - always high quality resampler (default) ; 1 - HQ resampler for upsampling, LQ resampler for downsampling (recommend for slow machines) ; 2 - always low quality resampler (very fast) ResamplingMode = 0 ; Chip Sample Mode: ; 0 - Native (default) ; 1 - use highest sampling rate (native or the one below) ; 2 - use always the sampling rate below (CPU friendly) ; 3 - use native sample rate for FM chips and highest sampling rate for all others ChipSmplMode = 3 ; Default Chip Sample Rate: 0 (results in value of Playback SampleRate) ChipSmplRate = 0 ; [Windows only] waveOut device ID to use (0 = default/Wave Mapper) OutputDevice = 0 ; Force Audio Buffer Number (1 Buffer = 10 ms, Minimum is 4, Maximum is 200) ; higher values result in greater delays while seeking (and pausing with EmulatePause On) ; set this to 50 or 100 if the sound is choppy ; 0 results in 10 for Windows 98/ME/2000/XP/7, ; 50 for Windows 95 and 20 for Windows Vista AudioBuffers = 0 ; "Surround" Sound - inverts the waveform of the right channel to create a pseudo surround effect ; use only with headphones!! SurroundSound = False ; Emulate during Pause: continue to generate sound while playback is paused EmulatePause = False ; Shows the last data block played with DAC Stream Command 95. Useful for debugging. ; 0 - don't show ; 1 - show data block ID only ; 2 - show data block ID + frequency ; 3 - show data block ID + frequency in KHz ShowStreamCmds = 3 ; --- FM Hardware Section Start --- ; Hardware FM Port (in hex, usually 220 or 388) FMPort = 0 ; Force FM Mode, even if only the SN76496 is used, also enables Mixed Mode (Hardware FM + Software Emulator) FMForce = False ; Makes some FM-Voices fading on track-end instead of instant silencing them FMSoftStop = True ; Overrides Volume setting, if FM hardware is used ; Possible values: ; = 0 - don't override (default) ; > 0 - override Volume setting and VGM Volume Modifier ; < 0 - multiply with volume FMVolume = 1.0 ; allow OPL3 panning commands in OPL 1/2 data (default: False) FMOPL2Pan = True ; --- FM Hardware Section End --- ; Chip Options ; ------------ ; - Disabled = False/True ; disable the emulation of the current chip ; - EmulatorType = 0 / 1 / ... ; 0 is recommend/default, 1+ are alternative emulation cores ; - MuteMask = 0 ; mute channels by setting the muting bitmask ; - MuteCh? = False/True ; mute channel ? ; - Mutexxx = False/True ; mute channel with the name xxx (e.g. DAC, DT, BD, ...) [SN76496] Disabled = False ; EmulatorType: 0 - MAME, 1 - Maxim EmulatorType = 0x00 ; Channels: 4 (0-3) [YM2413] Disabled = False ; FMPort = 0: ; EmulatorType: 0 - EMU2413, 1 - MAME, 2 - Nuked OPLL ; FMPort != 0: ; EmulatorType: 0 - Valley Bell Custom, 1 - Meka EmulatorType = 0x00 ; Channels: 14 (0-8, BD, SD, TOM, TC, HH) [YM2612] Disabled = False ; EmulatorType: 0 - MAME (Genesis Plus GX), 1 - Nuked OPN2, 2 - Gens EmulatorType = 0x00 ; MAME: if on, the chip updates its left/right channel alternatively, creating a nice pseudo-stereo effect ; Note: If you emulate at a set sample rate, this option halves it. PseudoStereo = False ; Gens: DAC Highpass-Filter (sometimes sounds good, but sometimes it generates a terrible noise) DACHighpass = False ; Gens: SSG-EG Enable (very buggy) SSG-EG = False ; Nuked OPN2 chip type: 0 - YM2612 with Mega Drive model 1 filter simulation ; 1 - ASIC YM3438, 2 - Discrete YM3438, 3 - YM2612 NukedType = 0x00 ; Channels: 7 (0-5, DAC) [YM2151] Disabled = False ; EmulatorType: 0 - MAME, 1 - Nuked OPM EmulatorType = 0x00 ; Channels: 8 (0-7) [SegaPCM] Disabled = False ; Channels: 16 (0-15) [RF5C68] Disabled = False ; Channels: 8 (0-7) [YM2203] Disabled = False ; AY/YM2149 EmulatorType: 0 - EMU2149, 1 - MAME EmulatorType = 0x00 ; disable the AY8910-part to speed up loading DisableAY = False ; Channels: 3 (0-2) [YM2608] Disabled = False EmulatorType = 0x00 DisableAY = False ; Channels: 6 FM (0-5) + 6 ADPCM (0-5) + 1 Delta-T ; Use MuteMask_FM, MuteMask_PCM (Delta-T is Ch6), MuteFMCh, MutePCMCh and MuteDT [YM2610] Disabled = False EmulatorType = 0x00 DisableAY = False ; Channels: 6 FM (0-5) + 6 ADPCM (0-5) + 1 Delta-T ; Use MuteMask_FM, MuteMask_PCM (Delta-T is Ch6), MuteFMCh, MutePCMCh and MuteDT [YM3812] Disabled = False ; EmulatorType: 0 - DOSBox (AdLibEmu), 1 - MAME EmulatorType = 0x00 ; Channels: 14 (0-8, BD, SD, TOM, TC, HH) [YM3526] Disabled = False ; Channels: 14 (0-8, BD, SD, TOM, TC, HH) [Y8950] Disabled = False ; Channels: 15 (0-8, BD, SD, TOM, TC, HH, DT) [YMF262] Disabled = False ; EmulatorType: 0 - DOSBox (AdLibEmu), 1 - MAME EmulatorType = 0x00 ; Channels: 23 (0-17, BD, SD, TOM, TC, HH) [YMF278B] Disabled = False ; Channels: 23 FM (0-17, BD, SD, TOM, TC, HH) + 24 WaveTable (0-23) ; Use MuteMask_FM, MuteMask_WT, MuteFMCh and MuteWTCh [YMF271] Disabled = False ; Channels: 12 (0-11) [YMZ280B] Disabled = False ; Channels: 8 (0-7) [RF5C164] Disabled = False ; Channels: 8 (0-7) [PWM] Disabled = False ; Channels: none (it just has left and right) [AY8910] Disabled = False ; EmulatorType: 0 - EMU2149, 1 - MAME EmulatorType = 0x00 ; Channels: 3 (0-2) [GameBoy] Disabled = False ; double the volume of the Wave Channel (sounds better, but may be less accurate and seems to sound distorted sometimes, like nezplay++) BoostWaveChn = True ; Channels: 4 (0-3) [NES APU] Disabled = False ; EmulatorType: 0 - NSFPlay, 1 - MAME EmulatorType = 0x00 ; Channels: 6 (0-5 = Square 1, Square 2, Triangle, Noise, DPCM, FDS) ; Options (NSFPlay cores only) ; ------- ; APU/DMC Options (2 bits, default: 0x03) ; 0x01 - OPT_UNMUTE_ON_RESET (enable all channels by default after reset) ; 0x02 - OPT_NONLINEAR_MIXER SharedOpts = 0x03 ; APU Options (2 bits, default: 0x01) ; 0x01 - OPT_PHASE_REFRESH ; 0x02 - OPT_DUTY_SWAP APUOpts = 0x01 ; DMC Options (6 bits, default: 0x3B) ; 0x01 - OPT_ENABLE_4011 ; 0x02 - OPT_ENABLE_PNOISE ; 0x04 - OPT_DPCM_ANTI_CLICK (nullify register 4011 writes, keeps DPCM limits correctly) ; 0x08 - OPT_RANDOMIZE_NOISE ; 0x10 - OPT_TRI_MUTE (stops Triangle wave if set to freq = 0, processes it at a very high rate else) ; 0x20 - OPT_TRI_NULL (VB custom, always makes Triangle return to null-level when stopping) DMCOpts = 0x3B ; FDS Options (1 bit, default: 0x00) ; 0x01 - OPT_4085_RESET (reset modulation phase on 4085 writes) FDSOpts = 0x00 [YMW258] Disabled = False ; Channels: 28 (0-27) [uPD7759] Disabled = False ; Channels: none (actually 1) [OKIM6258] Disabled = False ; enables internal 10-bit processing (original MESS behaviour) ; The comments in the code say something about 10-bit and 12-bit DAC, but that's not what the code does. Enable10Bit = False ; Channels: none (actually 1) [OKIM6295] Disabled = False ; Channels: 4 (0-3) [K051649] ; also known as SCC1 Disabled = False ; Channels: 5 (0-4) [K054539] Disabled = False ; Channels: 8 (0-7) [HuC6280] Disabled = False ; EmulatorType: 0 - Ootake, 1 - MAME (sounds brighter, lacks LFO) EmulatorType = 0x00 ; Channels: 6 (0-5) [C140] Disabled = False ; Channels: 24 (0-23) [K053260] Disabled = False ; Channels: 4 (0-3) [Pokey] Disabled = False ; Channels: 4 (0-3) [QSound] Disabled = False ; EmulatorType: 0 - superctr custom (Based on real DSP code, featuring QSound effects), ; 1 - MAME (old HLE, higher sample rate) EmulatorType = 0x00 ; Channels: 16 (0-15) [SCSP] Disabled = False ; Skip all DSP calculations, huge speedup (the DSP doesn't work correctly right now anyway) (default: True) BypassDSP = True ; Channels: 32 (0-31) [WSwan] Disabled = False ; Channels: 4 (0-3) [VSU] Disabled = False ; Channels: 6 (0-5) [SAA1099] Disabled = False ; Channels: 6 (0-5) [ES5503] Disabled = False ; Channels: 32 (0-31) [ES5506] Disabled = False ; Channels: 32 (0-31) [X1-010] Disabled = False ; Channels: 16 (0-15) [C352] Disabled = False ; disable rear channels (disregarding the rear enable/disable setting in VGMs) DisableRear = False ; Channels: 32 (0-31) [GA20] Disabled = False ; Channels: 4 (0-3) ================================================ FILE: VGMPlay/VGMPlay.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VGMPlay", "VGMPlay.vcxproj", "{8519237F-1B46-4C8A-994C-58D218182F2C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8519237F-1B46-4C8A-994C-58D218182F2C}.Debug|Win32.ActiveCfg = Debug|Win32 {8519237F-1B46-4C8A-994C-58D218182F2C}.Debug|Win32.Build.0 = Debug|Win32 {8519237F-1B46-4C8A-994C-58D218182F2C}.Debug|x64.ActiveCfg = Debug|x64 {8519237F-1B46-4C8A-994C-58D218182F2C}.Debug|x64.Build.0 = Debug|x64 {8519237F-1B46-4C8A-994C-58D218182F2C}.Release|Win32.ActiveCfg = Release|Win32 {8519237F-1B46-4C8A-994C-58D218182F2C}.Release|Win32.Build.0 = Release|Win32 {8519237F-1B46-4C8A-994C-58D218182F2C}.Release|x64.ActiveCfg = Release|x64 {8519237F-1B46-4C8A-994C-58D218182F2C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: VGMPlay/VGMPlay.txt ================================================ VGM Player - Readme ========== General ======= Usage: Drop a file on the executable or open the program and type the filename. Supported files types are: - Video Game Music Files (*.vgm, *.vgz) - Creative Music Files (*.cmf) - DosBox RAW OPL Log Files (*.dro) - Playlist files (*.m3u) Supported chips --------------- - SN76496** (Sega PSG) and T6W28** (NeoGeo Pocket custom) - YM2413* (OPLL) - YM2612 (OPN2) - YM2151 (OPM) - SegaPCM - RF5C68 - YM2203 (OPN) - YM2608 (OPNA) - YM2610/B (OPNB) - YM3812* (OPL2) - YM3526* (OPL) - Y8950* (MSX AUDIO) - YMF262* (OP3) - YMF278B (OPL4) ! - YMF271 (OPLX) - YMZ280B - RF5C164 (Sega MegaCD PCM) - PWM (from Sega 32x) - AY8910 (MSX PSG) - GameBoy DMG - NES APU (incl. FDS) - YMW258 (MultiPCM) - UPD7759 - OKI6258 (Sharp X68000 ADPCM) - OKI6295 - K051649 - K054539 - HuC6280 (PC Engine) - Namco C140 - K053260 - Pokey (Atari) - QSound - SCSP (Saturn Custom Sound Processor, YMF292-F) - WonderSwan - Virtual Boy VSU - SAA1099 - ESS5503 - ESS5505/6 - Seta X1-010 - Namco C352 - Irem GA20 * This chip can be emulated via OPL Hardware (like Soundblaster sound cards). ** OPL hardware emulation is available, but software emulation is prefered. Hardware emulation is used if another chip activates HW emulation or FMForce is True. ! For some songs you need a sample ROM, called yrw801.rom, to make playback work. Place it in the directory of VGMPlay.exe. OPL hardware emulation can be enabled by setting the "FMPort"-entry in the ini-file. If you want to use FM Hardware under Windows NT/2000/XP/... you have to install PortTalk. PortTalk Website: http://www.beyondlogic.org/porttalk/porttalk.htm Under Linux the program must have root rights to use Hardware FM. It's possible to write Wave-Files by editing the "LogSound"-line in the ini-file. Batch conversions are possible by opening a playlist. FM Hardware can't be logged to Wave files. Keys ---- You can use the following keys during playback: Space - Pause Cursor Left/Right - Seek 5 seconds backward/forward Ctrl + Cursor Left/Right - Seek 1 minute backward/forward ESC/Q - Quit the program F - Fade out R - Restart current Track PageUp/B - Previous Track PageDown/N - Next Track Note: All keys also work during silent sound logging. System Reqiurements =================== It depends on the files you want to play. Minimum Reqiurements -------------------- Software Emulation: 166 MHz should be enough for SN76496 + YM2612. For chips with 12+ FM channels you need probably need at least 700 MHz. 16 MB RAM (plus a size of the VGM you want to play, so for a 100 MB vgm, you need ~110 MB RAM) That allows you to play VGMs with up to 2x FM, 2x PSG or FM + PSG + PCM. It's NOT enough for YMF271 emulation. The YMF262 is equal to 2x FM. The YMF278B is Wavetable + YMF262 (with the latter one disabled if unused). Hardware FM Playback: 486 with 33 MHz? - I dunno On my PII 233 MHz FM Playback takes 3.5% CPU at a maximum. Bugs ==== PauseEmulation is disabled under Linux unless FM Hardware is used. Under Linux you have to double-tap ESC to quit the program (or just press Q). I haven't yet found a way around it. Sometimes MAME's sound cores tend to sound strange. I already fixed some, but it's not my fault. Ubuntu might refuse to run (or maybe compile) VGMPlay correctly. If so, it either crashes upon opening a vgm file or doesn't open the sound device. (and the ini-file, too. VGMs are opened for some reason.) Using Wine with the Windows-version of VGMPlay should work. It runs fine on openSUSE. (I compile and test it regularly with 12.3 64-bit. Older version were successfully tested with 11.1 32-bit and 11.4 64-bit) It also ran fine on the Debian system at university. Comments ======== If you want to set some options, open the ini-file. It's well commented. The T6W28 doesn't use MAME's T6W28 core. Instead I modified the SN76496 core to emulate the T6W28 with 2 SN76496 chips. The SN76496 OPL emulation is okay, but it's impossible to get the noise sound right. EMU2413 Emulator was added, because sometimes the one of MAME sounds strange. I added the Gens YM2612 core for the same reason (before I fixed MAME's YM2612 core). I haven't yet found a player that supports all three version of dro files. P.S.: AdPlug now seems to support them. Some may be wondering, how someone can have the idea to implement OPL Hardware support. Here a more or less small story: I like CMF MIDI files, but I was unable to listen to them while doing other things on my computer in Windows 95. It was impossible to listen to them unter Windows 2000. When I found AdPlug, the CMF support was bad and I started to make my VGM Player play CMF files. Debugging wasn't nice - a Pentium2 233MHz isn't stong enough for a Software OPL Emulator in Debug Mode. The OPL2-Port option in AdPlug was interesting (I have a Soundblaster 16) and showed me the power of PortTalk. My MIDI-FM Player that got OPL-Hardware-Support earlier than my VGM Player, because playing usual MIDI files requires a lot more debugging than playing simple VGM files. When it was working in my MIDI player, I implemented OPL-Hardware-Support into VGMPlay. Of course the first working chips were OPL/OPL2/OPL3. I remembered that Meka had OPL-Support, too and wrote my OPLL->OPL-Mapper. It sounded quite good, but I was unable to get the PSG and the YM2413 of Space Harrier Theme in sync. So a PSG->OPL-Mapper followed. Later I ported Meka's OPL Mapper, because I liked the bass of some Phantasy Star tunes. Credits ======= This program was written by Valley Bell. - Special thanks to ctr for major contributions in general. - almost all software emulators are from MAME (http://mamedev.org) - EMU2413 and Gens YM2612 were ported from Maxim's in_vgm - the YMF278B core was ported from openMSX - zlib compression by Jean-loup Gailly and Mark Adler is used - all custom OPL Mappers were written using MAME software emulators and the OPL2/3 programming guides by Jeffrey S. Lee and Vladimir Arnost - one YM2413 OPL Mapper was ported from MEKA. - the RF5C164 and PWM cores were ported from Gens/GS - the MAME YM2612 core was fixed with the help of Blargg's MAME YM2612 fix and Genesis Plus GX' YM2612 core - AdLibEmu (OPL2 and OPL3 core) was ported from DOSBox - The default HuC6280 core is from Ootake. - EMU2149, the alternative NES APU core and the NES FDS core were ported from rainwarrior's NSFPlay. - the WonderSwan core was ported from in_wsr - Virtual Boy VSU core was ported from vbjin and originates from mednafen - Thanks to grauw for patches that make it work properly on Mac OS X. - ctr for writing the new C352 sound core. ================================================ FILE: VGMPlay/VGMPlay.vcxproj ================================================  Debug Win32 Release Win32 Debug x64 Release x64 true true true true true true true true 15.0 {8519237F-1B46-4C8A-994C-58D218182F2C} Win32Proj VGMPlay 7.0 Application true v100 MultiByte Application false v100 true MultiByte Application true v100 MultiByte Application false v100 true MultiByte true $(ProjectDir)zlib;$(IncludePath) $(ProjectDir)zlib;$(LibraryPath) $(SolutionDir)$(Configuration)_Win32\ $(Configuration)_Win32\ true $(ProjectDir)zlib;$(IncludePath) $(ProjectDir)zlib;$(LibraryPath) $(SolutionDir)$(Configuration)_Win64\ $(Configuration)_Win64\ false $(ProjectDir)zlib;$(IncludePath) $(ProjectDir)zlib;$(LibraryPath) $(SolutionDir)$(Configuration)_Win32\ $(Configuration)_Win32\ false $(ProjectDir)zlib;$(IncludePath) $(ProjectDir)zlib;$(LibraryPath) $(SolutionDir)$(Configuration)_Win64\ $(Configuration)_Win64\ NotUsing Level3 Disabled VGM_LITTLE_ENDIAN;ENABLE_ALL_CORES;CONSOLE_MODE;ADDITIONAL_FORMATS;SET_CONSOLE_TITLE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console kernel32.lib;user32.lib;advapi32.lib;winmm.lib;zdll.lib;%(AdditionalDependencies) true NotUsing Level3 Disabled VGM_LITTLE_ENDIAN;ENABLE_ALL_CORES;CONSOLE_MODE;ADDITIONAL_FORMATS;SET_CONSOLE_TITLE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) EditAndContinue true Console kernel32.lib;user32.lib;advapi32.lib;winmm.lib;zlibstat64d.lib;%(AdditionalDependencies) true Level3 NotUsing MaxSpeed true true VGM_LITTLE_ENDIAN;ENABLE_ALL_CORES;CONSOLE_MODE;ADDITIONAL_FORMATS;SET_CONSOLE_TITLE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true kernel32.lib;user32.lib;advapi32.lib;winmm.lib;zdll.lib;%(AdditionalDependencies) Level3 NotUsing MaxSpeed true true VGM_LITTLE_ENDIAN;ENABLE_ALL_CORES;CONSOLE_MODE;ADDITIONAL_FORMATS;SET_CONSOLE_TITLE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true kernel32.lib;user32.lib;advapi32.lib;winmm.lib;zlibstat64.lib;%(AdditionalDependencies) ================================================ FILE: VGMPlay/VGMPlay.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {d92e176a-b6da-4926-ac77-9285e175b450} c;h;cpp {7b8c14e4-98d2-494e-8eeb-102684e2b0d8} c;h {b7d46055-4043-4069-8dfd-cd0614f24cb8} c;h {5f79bd0e-fae5-4188-8c92-06c81891c82c} c;h {a8502cef-6a14-45a9-b505-818f39601a20} c;h {b0a32a39-96e4-4b81-92ca-fff506eb2b1e} c;h {c2ae641c-5a5a-41d2-8a54-e5caa03cd919} c;h {8e35abaa-f8c4-43f7-8fe0-e17fefb8c132} c;h Headerdateien Headerdateien Headerdateien Headerdateien Headerdateien Headerdateien Headerdateien SoundCore SoundCore SoundCore SoundCore SoundCore SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\OPL Mapper SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips Headerdateien Headerdateien SoundCore\FM OPL Chips SoundCore\FM OPx Chips Quelldateien Quelldateien Quelldateien Quelldateien Quelldateien SoundCore SoundCore SoundCore SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPL Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPN Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\FM OPx Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\PCM Chips SoundCore\OPL Mapper SoundCore\OPL Mapper SoundCore\OPL Mapper SoundCore\OPL Mapper SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\PSG Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips SoundCore\NES Chips Quelldateien Quelldateien SoundCore\FM OPL Chips SoundCore\FM OPx Chips ================================================ FILE: VGMPlay/VGMPlayUI.c ================================================ // VGMPlayUI.c: C Source File for the Console User Interface // Note: In order to make MS VC6 NOT crash when using fprintf with stdout, stderr, etc. // if linked to msvcrt.lib, the following project setting is important: // C/C++ -> Code Generation -> Runtime libraries: Multithreaded DLL #define _GNU_SOURCE #include #include #include #include #include #include // for toupper #include // for setlocale #include "stdbool.h" #include #ifdef WIN32 #include #include #else #include // for PATH_MAX #include #include // for STDIN_FILENO and usleep() #include // for struct timeval in _kbhit() #include // for signal() #include // for select() #define Sleep(msec) usleep(msec * 1000) #define _vsnwprintf vswprintf #endif #define printerr(x) fprintf(stderr, x) #include "chips/mamedef.h" #include "Stream.h" #include "VGMPlay.h" #include "VGMPlay_Intf.h" #include "mmkeys.h" #include "dbus.h" #ifdef XMAS_EXTRA #include "XMasFiles/XMasBonus.h" #endif #ifdef WS_DEMO #include "XMasFiles/SWJ-SQRC01_1C.h" #endif #ifndef WIN32 void WaveOutLinuxCallBack(void); #endif #ifdef WIN32 #define DIR_CHR '\\' #define DIR_STR "\\" #define QMARK_CHR '\"' #else #define DIR_CHR '/' #define DIR_STR "/" #define QMARK_CHR '\'' #ifndef SHARE_PREFIX #define SHARE_PREFIX "/usr/local" #endif #endif #define APP_NAME "VGM Player" #define APP_NAME_L L"VGM Player" int main(int argc, char* argv[]); static void RemoveNewLines(char* String); static void RemoveQuotationMarks(char* String); char* GetLastDirSeparator(const char* FilePath); static bool IsAbsolutePath(const char* FilePath); static char* GetFileExtension(const char* FilePath); static void StandardizeDirSeparators(char* FilePath); #ifdef WIN32 static void WinNT_Check(void); #endif static char* GetAppFileName(void); static void cls(void); #ifndef WIN32 static void changemode(bool); static int _kbhit(void); static int _getch(void); #endif static INT8 stricmp_u(const char *string1, const char *string2); static INT8 strnicmp_u(const char *string1, const char *string2, size_t count); static void ReadOptions(const char* AppName); static bool GetBoolFromStr(const char* TextStr); #if defined(XMAS_EXTRA) || defined(WS_DEMO) static bool XMas_Extra(char* FileName, bool Mode); #endif #ifndef WIN32 static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr); #endif static bool OpenPlayListFile(const char* FileName); static bool OpenMusicFile(const char* FileName); extern bool OpenVGMFile(const char* FileName); extern bool OpenOtherFile(const char* FileName); static void wprintc(const wchar_t* format, ...); static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock); const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag); static void ShowVGMTag(void); static void MMKey_Event(UINT8 event); static void PlayVGM_UI(void); INLINE INT8 sign(double Value); INLINE long int Round(double Value); INLINE double RoundSpecial(double Value, double RoundTo); static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate); // Options Variables extern UINT32 SampleRate; // Note: also used by some sound cores to // determinate the chip sample rate extern UINT32 VGMPbRate; extern UINT32 VGMMaxLoop; extern UINT32 CMFMaxLoop; UINT32 FadeTimeN; // normal fade time UINT32 FadeTimePL; // in-playlist fade time extern UINT32 FadeTime; UINT32 PauseTimeJ; // Pause Time for Jingles UINT32 PauseTimeL; // Pause Time for Looping Songs extern UINT32 PauseTime; static UINT8 Show95Cmds; extern float VolumeLevel; extern bool SurroundSound; extern UINT8 HardStopOldVGMs; extern bool FadeRAWLog; static UINT8 LogToWave; //extern bool FullBufFill; extern bool PauseEmulate; extern bool DoubleSSGVol; static UINT16 ForceAudioBuf; static UINT8 OutputDevID; extern UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; extern UINT16 FMPort; extern bool UseFM; extern bool FMForce; //extern bool FMAccurate; extern bool FMBreakFade; extern float FMVol; extern bool FMOPL2Pan; extern CHIPS_OPTION ChipOpts[0x02]; extern bool ThreadPauseEnable; extern volatile bool ThreadPauseConfrm; extern bool ThreadNoWait; // don't reset the timer extern UINT16 AUDIOBUFFERU; extern UINT32 SMPL_P_BUFFER; extern char SoundLogFile[MAX_PATH]; extern UINT8 OPL_MODE; extern UINT8 OPL_CHIPS; //extern bool WINNT_MODE; UINT8 NEED_LARGE_AUDIOBUFS; extern char* AppPaths[8]; static char AppPathBuffer[MAX_PATH * 2]; static char PLFileBase[MAX_PATH]; char PLFileName[MAX_PATH]; UINT32 PLFileCount; static char** PlayListFile; UINT32 CurPLFile; static UINT8 NextPLCmd; UINT8 PLMode; // set to 1 to show Playlist text static bool FirstInit; extern bool AutoStopSkip; static UINT8 lastMMEvent = 0x00; char VgmFileName[MAX_PATH]; static UINT8 FileMode; extern VGM_HEADER VGMHead; extern UINT32 VGMDataLen; extern UINT8* VGMData; extern GD3_TAG VGMTag; static bool PreferJapTag; extern volatile bool PauseThread; static bool StreamStarted; extern float MasterVol; extern UINT32 VGMPos; extern INT32 VGMSmplPos; extern INT32 VGMSmplPlayed; extern INT32 VGMSampleRate; extern UINT32 BlocksSent; extern UINT32 BlocksPlayed; static bool IsRAWLog; extern bool EndPlay; extern bool PausePlay; extern bool FadePlay; extern bool ForceVGMExec; extern UINT8 PlayingMode; extern UINT32 PlayingTime; extern UINT32 FadeStart; extern UINT32 VGMMaxLoopM; extern UINT32 VGMCurLoop; extern float VolumeLevelM; bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c extern float FinalVol; extern bool ResetPBTimer; #ifndef WIN32 static struct termios oldterm; static bool termmode; #endif static volatile bool sigint = false; UINT8 CmdList[0x100]; //extern UINT8 DISABLE_YMZ_FIX; extern UINT8 IsVGMInit; extern UINT16 Last95Drum; // for optvgm debugging extern UINT16 Last95Max; // for optvgm debugging extern UINT32 Last95Freq; // for optvgm debugging static bool PrintMSHours; #ifdef WIN32 static BOOL WINAPI signal_handler(DWORD dwCtrlType) { switch(dwCtrlType) { case CTRL_C_EVENT: // Ctrl + C case CTRL_CLOSE_EVENT: // close console window via X button case CTRL_BREAK_EVENT: // Ctrl + Break sigint = true; return TRUE; case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: return FALSE; } return FALSE; } #else static void signal_handler(int signal) { if(signal == SIGINT || signal == SIGTERM || signal == SIGHUP) sigint = true; } #endif int main(int argc, char* argv[]) { int argbase; int ErrRet; char* AppName; #if defined(XMAS_EXTRA) || defined(WS_DEMO) bool XMasEnable; #endif char* AppPathPtr; const char* StrPtr; const char* FileExt; UINT8 CurPath; UINT32 ChrPos; char* DispFileName; // set locale to "current system locale" // (makes Unicode characters (like umlauts) work under Linux and fixes some // Unicode -> ANSI conversions) setlocale(LC_CTYPE, ""); #ifndef WIN32 tcgetattr(STDIN_FILENO, &oldterm); termmode = false; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); #else SetConsoleCtrlHandler(signal_handler, TRUE); #endif if (argc > 1) { if (! stricmp_u(argv[1], "-v") || ! stricmp_u(argv[1], "--version")) { printf("VGMPlay %s" #if defined(APLHA) " alpha" #elif defined(BETA) " beta" #endif ", supports VGM %s\n", VGMPLAY_VER_STR, VGM_VER_STR); return 0; } } #ifdef SET_CONSOLE_TITLE #ifdef WIN32 SetConsoleTitle(APP_NAME); // Set Windows Console Title #else printf("\x1B]0;%s\x07", APP_NAME); // Set xterm/rxvt Terminal Title #endif #endif printf(APP_NAME); #ifdef XMAS_EXTRA printf(" - XMas Release"); #endif printf("\n----------\n"); //if (argv[0x00] == NULL) // printf("Argument \"Application-Name\" is NULL!\n"); // Warning! It's dangerous to use Argument 0! // AppName may be "vgmplay" instead of "vgmplay.exe" VGMPlay_Init(); // Note: Paths are checked from last to first. CurPath = 0x00; AppPathPtr = AppPathBuffer; #ifndef WIN32 // Path 1: global share directory AppPaths[CurPath] = SHARE_PREFIX "/share/vgmplay/"; CurPath ++; #endif // Path 2: exe's directory AppName = GetAppFileName(); // "C:\VGMPlay\VGMPlay.exe" // Note: GetAppFileName always returns native directory separators. StrPtr = strrchr(AppName, DIR_CHR); if (StrPtr != NULL) { ChrPos = StrPtr + 1 - AppName; strncpy(AppPathPtr, AppName, ChrPos); AppPathPtr[ChrPos] = 0x00; // "C:\VGMPlay\" AppPaths[CurPath] = AppPathPtr; CurPath ++; AppPathPtr += ChrPos + 1; } #ifndef WIN32 // Path 3: home directory StrPtr = getenv("XDG_CONFIG_HOME"); if (StrPtr != NULL && StrPtr[0] == '\0') { strcpy(AppPathPtr, StrPtr); } else { StrPtr = getenv("HOME"); if (StrPtr != NULL) strcpy(AppPathPtr, StrPtr); else strcpy(AppPathPtr, ""); strcat(AppPathPtr, "/.config"); } strcat(AppPathPtr, "/vgmplay/"); AppPaths[CurPath] = AppPathPtr; CurPath ++; AppPathPtr += strlen(AppPathPtr) + 1; #endif // Path 4: working directory ("\0") AppPathPtr[0] = '\0'; AppPaths[CurPath] = AppPathPtr; CurPath ++; #if 0 // set to 1 to print all selected search paths CurPath = 0; while(AppPaths[CurPath] != NULL) { printf("Path %u: %s\n", CurPath + 1, AppPaths[CurPath]); CurPath ++; } #endif ReadOptions(AppName); VGMPlay_Init2(); MultimediaKeyHook_Init(); MultimediaKeyHook_SetCallback(&MMKey_Event); ErrRet = 0; argbase = 0x01; while(argbase < argc) { if (! strnicmp_u(argv[argbase], "-LogSound:", 10)) { LogToWave = (UINT8)strtoul(argv[argbase] + 10, NULL, 0); argbase ++; } else if (! strnicmp_u(argv[argbase], "-DeviceId:", 10)) { OutputDevID = (UINT8)strtoul(argv[argbase] + 10, NULL, 0); argbase ++; } else { break; } } printf("\nFile Name:\t"); if (argc <= argbase) { #ifdef WIN32 INT32 OldCP; OldCP = GetConsoleCP(); // Set the Console Input Codepage to ANSI. // The Output Codepage must be left at OEM, else the displayed characters are wrong. ChrPos = GetACP(); ErrRet = SetConsoleCP(ChrPos); // set input codepage //ErrRet = SetConsoleOutputCP(ChrPos); // set output codepage (would be a bad idea) StrPtr = fgets(VgmFileName, MAX_PATH, stdin); if (StrPtr == NULL) VgmFileName[0] = '\0'; // Playing with the console font resets the Console Codepage to OEM, so I have to // convert the file name in this case. if (GetConsoleCP() == GetOEMCP()) OemToChar(VgmFileName, VgmFileName); // OEM -> ANSI conversion // This fixes the display of non-ANSI characters. ErrRet = SetConsoleCP(OldCP); // This codepage stuff drives me insane. // Debug and Release build behave differently - WHAT?? // // There a list of behaviours. // Debug and Release were tested by dropping a file on it and via Visual Studio. // // Input CP 850, Output CP 850 // Debug build: Dynamite D³x // Release build: Dynamite Düx // Input CP 1252, Output CP 850 // Debug build: Dynamite D³x // Release build: Dynamite D³x // Input CP 850, Output CP 1252 // Debug build: Dynamite D³x [tag display wrong] // Release build: Dynamite Düx [tag display wrong] // Input CP 1252, Output CP 1252 // Debug build: Dynamite D³x [tag display wrong] // Release build: Dynamite D³x [tag display wrong] #else fflush(stdout); while(1) { fd_set fds; FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); struct timeval tv = {0, 10}; int sel_ret = select(1, &fds, NULL, NULL, &tv); if(sel_ret == -1) break; else if(!sel_ret) { DBus_ReadWriteDispatch(); // If ^C has been pressed, quit immediately if(sigint) { printf("\n"); break; } } else { StrPtr = fgets(VgmFileName, MAX_PATH, stdin); break; } } if (StrPtr == NULL) VgmFileName[0] = '\0'; #endif RemoveNewLines(VgmFileName); RemoveQuotationMarks(VgmFileName); } else { // The argument should already use the ANSI codepage. strcpy(VgmFileName, argv[argbase]); DispFileName = GetLastDirSeparator(VgmFileName); if(DispFileName && strlen(DispFileName) > 2) DispFileName++; else DispFileName = VgmFileName; printf("%s\n", DispFileName); } if (! strlen(VgmFileName)) goto ExitProgram; StandardizeDirSeparators(VgmFileName); #if defined(XMAS_EXTRA) || defined(WS_DEMO) XMasEnable = XMas_Extra(VgmFileName, 0x00); #endif #if 0 { // Print hex characters of file name (for vgm-player script debugging) const char* CurChr; #ifdef WIN32 printf("Input CP: %d, Output CP: %d\n", GetConsoleCP(), GetConsoleOutputCP()); #endif printf("VgmFileName: "); CurChr = VgmFileName; while(*CurChr != '\0') { printf("%02X ", (UINT8)*CurChr); CurChr ++; } printf("%02X\n", (UINT8)*CurChr); _getch(); } #endif #if 0 { // strip spaces and \n (fixed bugs with vgm-player script with un-7z) char* CurChr; // trim \n and spaces off CurChr = strchr(VgmFileName, '\n'); if (CurChr != NULL) *CurChr = '\0'; CurChr = VgmFileName + strlen(VgmFileName) - 1; while(CurChr > VgmFileName && *CurChr == ' ') *(CurChr --) = '\0'; } #endif FirstInit = true; StreamStarted = false; FileExt = GetFileExtension(VgmFileName); if (FileExt == NULL || stricmp_u(FileExt, "m3u")) PLMode = 0x00; else PLMode = 0x01; lastMMEvent = 0x00; if (! PLMode) { PLFileCount = 0x00; CurPLFile = 0x00; // no Play List File if (! OpenMusicFile(VgmFileName)) { printerr("Error opening the file!\n"); if (argv[0][1] == ':') _getch(); ErrRet = 1; goto ExitProgram; } printf("\n"); ErrorHappened = false; FadeTime = FadeTimeN; PauseTime = PauseTimeL; PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); // 44100 smpl * 60 sec * 60 min ShowVGMTag(); NextPLCmd = 0x80; PlayVGM_UI(); CloseVGMFile(); } else { strcpy(PLFileName, VgmFileName); if (! OpenPlayListFile(PLFileName)) { printerr("Error opening the playlist!\n"); if (argv[0][1] == ':') _getch(); ErrRet = 1; goto ExitProgram; } for (CurPLFile = 0x00; CurPLFile < PLFileCount; CurPLFile ++) { cls(); printf(APP_NAME); printf("\n----------\n"); printf("\nPlaylist File:\t%s\n", PLFileName); printf("Playlist Entry:\t%u / %u\n", CurPLFile + 1, PLFileCount); printf("File Name:\t%s\n", PlayListFile[CurPLFile]); if (IsAbsolutePath(PlayListFile[CurPLFile])) { strcpy(VgmFileName, PlayListFile[CurPLFile]); } else { strcpy(VgmFileName, PLFileBase); strcat(VgmFileName, PlayListFile[CurPLFile]); } if (! OpenMusicFile(VgmFileName)) { printf("Error opening the file!\n"); _getch(); while(_kbhit()) _getch(); continue; } printf("\n"); ErrorHappened = false; if (CurPLFile < PLFileCount - 1) FadeTime = FadeTimePL; else FadeTime = FadeTimeN; PauseTime = VGMHead.lngLoopOffset ? PauseTimeL : PauseTimeJ; PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); ShowVGMTag(); NextPLCmd = 0x00; PlayVGM_UI(); CloseVGMFile(); if (ErrorHappened) { if (_kbhit()) _getch(); _getch(); ErrorHappened = false; } if (NextPLCmd == 0xFF) break; else if (NextPLCmd == 0x01) CurPLFile -= 0x02; // Jump to last File (-2 + 1 = -1) } } if (ErrorHappened && argv[0][1] == ':') { if (_kbhit()) _getch(); _getch(); } #ifdef _DEBUG printf("Press any key ..."); _getch(); #endif ExitProgram: #if defined(XMAS_EXTRA) || defined(WS_DEMO) if (XMasEnable) XMas_Extra(VgmFileName, 0x01); #endif #ifndef WIN32 changemode(false); #ifdef SET_CONSOLE_TITLE // printf("\x1B]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\x07", APP_NAME); // Reset xterm/rxvt Terminal Title #endif #endif MultimediaKeyHook_Deinit(); VGMPlay_Deinit(); free(AppName); return ErrRet; } static void RemoveNewLines(char* String) { char* StrPtr; StrPtr = String + strlen(String) - 1; while(StrPtr >= String && (UINT8)*StrPtr < 0x20) { *StrPtr = '\0'; StrPtr --; } return; } static void RemoveQuotationMarks(char* String) { UINT32 StrLen; char* EndQMark; if (String[0x00] != QMARK_CHR) return; StrLen = strlen(String); memmove(String, String + 0x01, StrLen); // Remove first char EndQMark = strrchr(String, QMARK_CHR); if (EndQMark != NULL) *EndQMark = 0x00; // Remove last Quot.-Mark return; } char* GetLastDirSeparator(const char* FilePath) { char* SepPos1; char* SepPos2; SepPos1 = strrchr(FilePath, '/'); SepPos2 = strrchr(FilePath, '\\'); if (SepPos1 < SepPos2) return SepPos2; else return SepPos1; } static bool IsAbsolutePath(const char* FilePath) { #ifdef WIN32 if (FilePath[0] == '\0') return false; // empty string if (FilePath[1] == ':') return true; // Device Path: C:\path if (! strncmp(FilePath, "\\\\", 2)) return true; // Network Path: \\computername\path #else if (FilePath[0] == '/') return true; // absolute UNIX path #endif return false; } static char* GetFileExtension(const char* FilePath) { char* DirSepPos; char* ExtDotPos; DirSepPos = GetLastDirSeparator(FilePath); if (DirSepPos == NULL) DirSepPos = (char*)FilePath; ExtDotPos = strrchr(DirSepPos, '.'); if (ExtDotPos == NULL) return NULL; else return ExtDotPos + 1; } static void StandardizeDirSeparators(char* FilePath) { char* CurChr; CurChr = FilePath; while(*CurChr != '\0') { if (*CurChr == '\\' || *CurChr == '/') *CurChr = DIR_CHR; CurChr ++; } return; } #ifdef WIN32 static void WinNT_Check(void) { OSVERSIONINFO VerInf; VerInf.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&VerInf); //WINNT_MODE = (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT); /* Following Systems need larger Audio Buffers: - Windows 95 (500+ ms) - Windows Vista (200+ ms) Tested Systems: - Windows 95B - Windows 98 SE - Windows 2000 - Windows XP (32-bit) - Windows Vista (32-bit) - Windows 7 (64-bit) */ NEED_LARGE_AUDIOBUFS = 0; if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { if (VerInf.dwMajorVersion == 4 && VerInf.dwMinorVersion == 0) NEED_LARGE_AUDIOBUFS = 50; // Windows 95 } else if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT) { if (VerInf.dwMajorVersion == 6 && VerInf.dwMinorVersion == 0) NEED_LARGE_AUDIOBUFS = 20; // Windows Vista } return; } #endif static char* GetAppFileName(void) { char* AppPath; AppPath = calloc(MAX_PATH, sizeof(char)); #ifdef WIN32 GetModuleFileName(NULL, AppPath, MAX_PATH - 1); #else readlink("/proc/self/exe", AppPath, MAX_PATH - 1); #endif return AppPath; } static void cls(void) { #ifdef WIN32 // CLS-Function from the MSDN Help HANDLE hConsole; COORD coordScreen = {0, 0}; BOOL bSuccess; DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD dwConSize; hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // get the number of character cells in the current buffer bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi); // fill the entire screen with blanks dwConSize = csbi.dwSize.X * csbi.dwSize.Y; bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, &cCharsWritten); // get the current text attribute //bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi); // now set the buffer's attributes accordingly //bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, // &cCharsWritten); // put the cursor at (0, 0) bSuccess = SetConsoleCursorPosition(hConsole, coordScreen); #else int retVal; retVal = system("clear"); #endif return; } #ifndef WIN32 static void changemode(bool dir) { static struct termios newterm; if (termmode == dir) return; if (dir) { newterm = oldterm; newterm.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newterm); } else { tcsetattr(STDIN_FILENO, TCSANOW, &oldterm); } termmode = dir; return; } static int _kbhit(void) { struct timeval tv; fd_set rdfs; int kbret; bool needchg; needchg = (! termmode); if (needchg) changemode(true); tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&rdfs); FD_SET(STDIN_FILENO, &rdfs); select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv); kbret = FD_ISSET(STDIN_FILENO, &rdfs); if (needchg) changemode(false); return kbret; } static int _getch(void) { int ch; bool needchg; needchg = (! termmode); if (needchg) changemode(true); ch = getchar(); if (needchg) changemode(false); return ch; } #endif static INT8 stricmp_u(const char *string1, const char *string2) { // my own stricmp, because VC++6 doesn't find _stricmp when compiling without // standard libraries const char* StrPnt1; const char* StrPnt2; char StrChr1; char StrChr2; StrPnt1 = string1; StrPnt2 = string2; while(true) { StrChr1 = toupper(*StrPnt1); StrChr2 = toupper(*StrPnt2); if (StrChr1 < StrChr2) return -1; else if (StrChr1 > StrChr2) return +1; if (StrChr1 == 0x00) return 0; StrPnt1 ++; StrPnt2 ++; } return 0; } static INT8 strnicmp_u(const char *string1, const char *string2, size_t count) { // my own strnicmp, because GCC doesn't seem to have _strnicmp const char* StrPnt1; const char* StrPnt2; char StrChr1; char StrChr2; size_t CurChr; StrPnt1 = string1; StrPnt2 = string2; CurChr = 0x00; while(CurChr < count) { StrChr1 = toupper(*StrPnt1); StrChr2 = toupper(*StrPnt2); if (StrChr1 < StrChr2) return -1; else if (StrChr1 > StrChr2) return +1; if (StrChr1 == 0x00) return 0; StrPnt1 ++; StrPnt2 ++; CurChr ++; } return 0; } static void ReadOptions(const char* AppName) { const UINT8 CHN_COUNT[CHIP_COUNT] = { 0x04, 0x09, 0x06, 0x08, 0x10, 0x08, 0x03, 0x00, 0x00, 0x09, 0x09, 0x09, 0x12, 0x00, 0x0C, 0x08, 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00, 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10, 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20, 0x04 }; const UINT8 CHN_MASK_CNT[CHIP_COUNT] = { 0x04, 0x0E, 0x07, 0x08, 0x10, 0x08, 0x03, 0x06, 0x06, 0x0E, 0x0E, 0x0E, 0x17, 0x18, 0x0C, 0x08, 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00, 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10, 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20, 0x04 }; const char* FNList[3]; char* FileName; FILE* hFile; char TempStr[0x40]; UINT32 StrLen; UINT32 TempLng; char* LStr; char* RStr; UINT8 IniSection; UINT8 CurChip; CHIP_OPTS* TempCOpt; CHIP_OPTS* TempCOpt2; UINT8 CurChn; char* TempPnt; bool TempFlag; // most defaults are set by VGMPlay_Init() FadeTimeN = FadeTime; FadeTimePL = 2000; PauseTimeJ = PauseTime; PauseTimeL = 0; Show95Cmds = 0x00; LogToWave = 0x00; OutputDevID = 0; ForceAudioBuf = 0x00; PreferJapTag = false; if (AppName == NULL) { printerr("Argument \"Application-Path\" is NULL!\nSkip loading INI.\n"); return; } // AppName: "C:\VGMPlay\VGMPlay.exe" RStr = strrchr(AppName, DIR_CHR); if (RStr != NULL) RStr ++; else RStr = (char*)AppName; FileName = (char*)malloc(strlen(RStr) + 0x05); // ".ini" + 00 strcpy(FileName, RStr); // FileName: "VGMPlay.exe" RStr = GetFileExtension(FileName); if (RStr == NULL) { RStr = FileName + strlen(FileName); *RStr = '.'; RStr ++; } strcpy(RStr, "ini"); // FileName: "VGMPlay.ini" or "vgmplay.ini" // on Linux platforms, it searches for "vgmplay.ini" first and // file names are case sensitive FNList[0] = FileName; FNList[1] = "VGMPlay.ini"; FNList[2] = NULL; LStr = FileName; FileName = FindFile_List(FNList); free(LStr); if (FileName == NULL) { printerr("Failed to load INI.\n"); return; } hFile = fopen(FileName, "rt"); free(FileName); if (hFile == NULL) { printerr("Failed to load INI.\n"); return; } IniSection = 0x00; while(! feof(hFile)) { LStr = fgets(TempStr, 0x40, hFile); if (LStr == NULL) break; if (TempStr[0x00] == ';') // Comment line continue; StrLen = strlen(TempStr) - 0x01; //if (TempStr[StrLen] == '\n') // TempStr[StrLen] = '\0'; while(TempStr[StrLen] < 0x20) { TempStr[StrLen] = '\0'; if (! StrLen) break; StrLen --; } if (! StrLen) continue; StrLen ++; LStr = &TempStr[0x00]; while(*LStr == ' ') LStr ++; if (LStr[0x00] == ';') // Comment line continue; if (LStr[0x00] == '[') RStr = strchr(TempStr, ']'); else RStr = strchr(TempStr, '='); if (RStr == NULL) continue; if (LStr[0x00] == '[') { // Line pattern: [Group] LStr ++; RStr = strchr(TempStr, ']'); if (RStr != NULL) RStr[0x00] = '\0'; if (! stricmp_u(LStr, "General")) { IniSection = 0x00; } else { IniSection = 0xFF; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { if (! stricmp_u(LStr, GetChipName(CurChip))) { IniSection = 0x80 | CurChip; break; } } if (IniSection == 0xFF) continue; } } else { // Line pattern: Option = Value TempLng = RStr - TempStr; TempStr[TempLng] = '\0'; // Prepare Strings (trim the spaces) RStr = &TempStr[TempLng - 0x01]; while(*RStr == ' ') *(RStr --) = '\0'; RStr = &TempStr[StrLen - 0x01]; while(*RStr == ' ') *(RStr --) = '\0'; RStr = &TempStr[TempLng + 0x01]; while(*RStr == ' ') RStr ++; switch(IniSection) { case 0x00: // General Sction if (! stricmp_u(LStr, "SampleRate")) { SampleRate = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "PlaybackRate")) { VGMPbRate = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "DoubleSSGVol")) { DoubleSSGVol = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "PreferJapTag")) { PreferJapTag = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "FadeTime")) { FadeTimeN = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "FadeTimePL")) { FadeTimePL = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "JinglePause")) { PauseTimeJ = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "HardStopOld")) { HardStopOldVGMs = (UINT8)strtoul(RStr, &TempPnt, 0); if (TempPnt == RStr) HardStopOldVGMs = GetBoolFromStr(RStr) ? 0x01 : 0x00; } else if (! stricmp_u(LStr, "FadeRAWLogs")) { FadeRAWLog = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "Volume")) { VolumeLevel = (float)strtod(RStr, NULL); } else if (! stricmp_u(LStr, "LogSound")) { //LogToWave = GetBoolFromStr(RStr); LogToWave = (UINT8)strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "MaxLoops")) { VGMMaxLoop = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "MaxLoopsCMF")) { CMFMaxLoop = strtoul(RStr, NULL, 0); } else if (! stricmp_u(LStr, "ResamplingMode")) { ResampleMode = (UINT8)strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "ChipSmplMode")) { CHIP_SAMPLING_MODE = (UINT8)strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "ChipSmplRate")) { CHIP_SAMPLE_RATE = strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "OutputDevice")) { OutputDevID = (UINT8)strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "AudioBuffers")) { ForceAudioBuf = (UINT16)strtol(RStr, NULL, 0); if (ForceAudioBuf < 0x04) ForceAudioBuf = 0x00; } else if (! stricmp_u(LStr, "SurroundSound")) { SurroundSound = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "EmulatePause")) { PauseEmulate = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "ShowStreamCmds")) { Show95Cmds = (UINT8)strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "FMPort")) { FMPort = (UINT16)strtoul(RStr, NULL, 16); } else if (! stricmp_u(LStr, "FMForce")) { FMForce = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "FMVolume")) { FMVol = (float)strtod(RStr, NULL); } else if (! stricmp_u(LStr, "FMOPL2Pan")) { FMOPL2Pan = GetBoolFromStr(RStr); } /*else if (! stricmp_u(LStr, "AccurateFM")) { FMAccurate = GetBoolFromStr(RStr); }*/ else if (! stricmp_u(LStr, "FMSoftStop")) { FMBreakFade = GetBoolFromStr(RStr); } break; case 0x80: // SN76496 case 0x81: // YM2413 case 0x82: // YM2612 case 0x83: // YM2151 case 0x84: // SegaPCM case 0x85: // RF5C68 case 0x86: // YM2203 case 0x87: // YM2608 case 0x88: // YM2610 case 0x89: // YM3812 case 0x8A: // YM3526 case 0x8B: // Y8950 case 0x8C: // YMF262 case 0x8D: // YMF278B case 0x8E: // YMF271 case 0x8F: // YMZ280B case 0x90: // RF5C164 case 0x91: // PWM case 0x92: // AY8910 case 0x93: // GameBoy case 0x94: // NES case 0x95: // MultiPCM case 0x96: // UPD7759 case 0x97: // OKIM6258 case 0x98: // OKIM6295 case 0x99: // K051649 case 0x9A: // K054539 case 0x9B: // HuC6280 case 0x9C: // C140 case 0x9D: // K053260 case 0x9E: // Pokey case 0x9F: // QSound case 0xA0: // SCSP case 0xA1: // WonderSwan case 0xA2: // VSU case 0xA3: // SAA1099 case 0xA4: // ES5503 case 0xA5: // ES5506 case 0xA6: // X1_010 case 0xA7: // C352 case 0xA8: // GA20 CurChip = IniSection & 0x7F; TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00] + CurChip; if (! stricmp_u(LStr, "Disabled")) { TempCOpt->Disabled = GetBoolFromStr(RStr); } else if (! stricmp_u(LStr, "EmulatorType")) { TempCOpt->EmuCore = (UINT8)strtol(RStr, NULL, 0); } else if (! stricmp_u(LStr, "MuteMask")) { if (! CHN_COUNT[CurChip]) break; // must use MuteMaskFM and MuteMask??? TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); if (CHN_MASK_CNT[CurChip] < 0x20) TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1; } else if (! strnicmp_u(LStr, "MuteCh", 0x06)) { if (! CHN_COUNT[CurChip]) break; // must use MuteFM and Mute??? CurChn = (UINT8)strtol(LStr + 0x06, &TempPnt, 0); if (TempPnt == NULL || *TempPnt) break; if (CurChn >= CHN_COUNT[CurChip]) break; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } else { switch(CurChip) { //case 0x00: // SN76496 case 0x02: // YM2612 if (! stricmp_u(LStr, "MuteDAC")) { CurChn = 0x06; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } else if (! stricmp_u(LStr, "DACHighpass")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } else if (! stricmp_u(LStr, "SSG-EG")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 1); TempCOpt->SpecialFlags |= TempFlag << 1; } else if (! stricmp_u(LStr, "PseudoStereo")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 2); TempCOpt->SpecialFlags |= TempFlag << 2; } else if (! stricmp_u(LStr, "NukedType")) { TempLng = (UINT32)strtoul(RStr, NULL, 0) & 0x03; TempCOpt->SpecialFlags &= ~(0x03 << 3); TempCOpt->SpecialFlags |= TempLng << 3; } break; //case 0x03: // YM2151 //case 0x04: // SegaPCM //case 0x05: // RF5C68 case 0x06: // YM2203 if (! stricmp_u(LStr, "DisableAY")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } break; case 0x07: // YM2608 case 0x08: // YM2610 if (! stricmp_u(LStr, "DisableAY")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } else if (! stricmp_u(LStr, "MuteMask_FM")) { TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1; } else if (! stricmp_u(LStr, "MuteMask_PCM")) { TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0); TempCOpt->ChnMute2 &= (1 << (CHN_MASK_CNT[CurChip] + 1)) - 1; } else if (! strnicmp_u(LStr, "MuteFMCh", 0x08)) { CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); if (TempPnt == NULL || *TempPnt) break; if (CurChn >= CHN_MASK_CNT[CurChip]) break; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } else if (! strnicmp_u(LStr, "MutePCMCh", 0x09)) { CurChn = (UINT8)strtol(LStr + 0x09, &TempPnt, 0); if (TempPnt == NULL || *TempPnt) break; if (CurChn >= CHN_MASK_CNT[CurChip]) break; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute2 &= ~(0x01 << CurChn); TempCOpt->ChnMute2 |= TempFlag << CurChn; } else if (! stricmp_u(LStr, "MuteDT")) { CurChn = 0x06; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute2 &= ~(0x01 << CurChn); TempCOpt->ChnMute2 |= TempFlag << CurChn; } break; case 0x01: // YM2413 case 0x09: // YM3812 case 0x0A: // YM3526 case 0x0B: // Y8950 case 0x0C: // YMF262 CurChn = 0xFF; if (! stricmp_u(LStr, "MuteBD")) CurChn = 0x00; else if (! stricmp_u(LStr, "MuteSD")) CurChn = 0x01; else if (! stricmp_u(LStr, "MuteTOM")) CurChn = 0x02; else if (! stricmp_u(LStr, "MuteTC")) CurChn = 0x03; else if (! stricmp_u(LStr, "MuteHH")) CurChn = 0x04; else if (CurChip == 0x0B && ! stricmp_u(LStr, "MuteDT")) CurChn = 0x05; if (CurChn != 0xFF) { if (CurChip < 0x0C) CurChn += 9; else CurChn += 18; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } break; case 0x0D: // YMF278B if (! stricmp_u(LStr, "MuteMask_FM")) { TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip - 0x01]) - 1; } else if (! stricmp_u(LStr, "MuteMask_WT")) { TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0); TempCOpt->ChnMute2 &= (1 << CHN_MASK_CNT[CurChip]) - 1; } else if (! strnicmp_u(LStr, "MuteFMCh", 0x08)) { CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); if (TempPnt == NULL || *TempPnt) break; if (CurChn >= CHN_COUNT[CurChip - 0x01]) break; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } else if (! strnicmp_u(LStr, "MuteFM", 0x06)) { CurChn = 0xFF; if (! stricmp_u(LStr + 6, "BD")) CurChn = 0x00; else if (! stricmp_u(LStr + 6, "SD")) CurChn = 0x01; else if (! stricmp_u(LStr + 6, "TOM")) CurChn = 0x02; else if (! stricmp_u(LStr + 6, "TC")) CurChn = 0x03; else if (! stricmp_u(LStr + 6, "HH")) CurChn = 0x04; if (CurChn != 0xFF) { CurChn += 18; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute1 &= ~(0x01 << CurChn); TempCOpt->ChnMute1 |= TempFlag << CurChn; } } else if (! strnicmp_u(LStr, "MuteWTCh", 0x08)) { CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); if (TempPnt == NULL || *TempPnt) break; if (CurChn >= CHN_MASK_CNT[CurChip]) break; TempFlag = GetBoolFromStr(RStr); TempCOpt->ChnMute2 &= ~(0x01 << CurChn); TempCOpt->ChnMute2 |= TempFlag << CurChn; } break; //case 0x0E: // YMF271 //case 0x0F: // YMZ280B /*if (! stricmp_u(LStr, "DisableFix")) { DISABLE_YMZ_FIX = GetBoolFromStr(RStr); } break;*/ //case 0x10: // RF5C164 //case 0x11: // PWM //case 0x12: // AY8910 case 0x13: // GameBoy if (! stricmp_u(LStr, "BoostWaveChn")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } break; case 0x14: // NES if (! stricmp_u(LStr, "SharedOpts")) { // 2 bits TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03; TempCOpt->SpecialFlags &= ~(0x03 << 0) & 0x7FFF; TempCOpt->SpecialFlags |= TempLng << 0; } else if (! stricmp_u(LStr, "APUOpts")) { // 2 bits TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03; TempCOpt->SpecialFlags &= ~(0x03 << 2) & 0x7FFF; TempCOpt->SpecialFlags |= TempLng << 2; } else if (! stricmp_u(LStr, "DMCOpts")) { // 8 bits (6 bits used) TempLng = (UINT32)strtol(RStr, NULL, 0) & 0xFF; TempCOpt->SpecialFlags &= ~(0xFF << 4) & 0x7FFF; TempCOpt->SpecialFlags |= TempLng << 4; } else if (! stricmp_u(LStr, "FDSOpts")) { // 1 bit TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x01; TempCOpt->SpecialFlags &= ~(0x01 << 12) & 0x7FFF; TempCOpt->SpecialFlags |= TempLng << 12; } break; case 0x17: // OKIM6258 if (! stricmp_u(LStr, "Enable10Bit")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } break; case 0x20: // SCSP if (! stricmp_u(LStr, "BypassDSP")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } break; case 0x27: // C352 if (! stricmp_u(LStr, "DisableRear")) { TempFlag = GetBoolFromStr(RStr); TempCOpt->SpecialFlags &= ~(0x01 << 0); TempCOpt->SpecialFlags |= TempFlag << 0; } break; } } break; case 0xFF: // Dummy Section break; } } } TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00]; TempCOpt2 = (CHIP_OPTS*)&ChipOpts[0x01]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++, TempCOpt2 ++) { TempCOpt2->Disabled = TempCOpt->Disabled; TempCOpt2->EmuCore = TempCOpt->EmuCore; TempCOpt2->SpecialFlags = TempCOpt->SpecialFlags; TempCOpt2->ChnMute1 = TempCOpt->ChnMute1; TempCOpt2->ChnMute2 = TempCOpt->ChnMute2; TempCOpt2->ChnMute3 = TempCOpt->ChnMute3; } fclose(hFile); #ifdef WIN32 WinNT_Check(); #endif if (CHIP_SAMPLE_RATE <= 0) CHIP_SAMPLE_RATE = SampleRate; return; } static bool GetBoolFromStr(const char* TextStr) { if (! stricmp_u(TextStr, "True")) return true; else if (! stricmp_u(TextStr, "False")) return false; else return strtol(TextStr, NULL, 0) ? true : false; } #if defined(XMAS_EXTRA) || defined(WS_DEMO) static bool XMas_Extra(char* FileName, bool Mode) { char* FileTitle; const UINT8* XMasData; UINT32 XMasSize; FILE* hFile; if (! Mode) { // Prepare Mode FileTitle = NULL; XMasData = NULL; #ifdef XMAS_EXTRA if (! stricmp_u(FileName, "WEWISH") { FileTitle = "WEWISH.CMF"; XMasSize = sizeof(WEWISH_CMF); XMasData = WEWISH_CMF; } else if (! stricmp_u(FileName, "tim7") { FileTitle = "lem_tim7.vgz"; XMasSize = sizeof(TIM7_VGZ); XMasData = TIM7_VGZ; } else if (! stricmp_u(FileName, "jingleb") { FileTitle = "lxmas_jb.dro"; XMasSize = sizeof(JB_DRO); XMasData = JB_DRO; } else if (! stricmp_u(FileName, "rudolph") { FileTitle = "rudolph.dro"; XMasSize = sizeof(RODOLPH_DRO); XMasData = RODOLPH_DRO; } else if (! stricmp_u(FileName, "clyde")) { FileTitle = "clyde1_1.dro"; XMasSize = sizeof(clyde1_1_dro); XMasData = clyde1_1_dro; } #elif defined(WS_DEMO) if (! stricmp_u(FileName, "wswan")) { FileTitle = "SWJ-SQRC01_1C.vgz"; XMasSize = sizeof(FF1ws_1C); XMasData = FF1ws_1C; } #endif if (XMasData) { #ifdef WIN32 GetEnvironmentVariable("Temp", FileName, MAX_PATH); #else strcpy(FileName, "/tmp"); #endif strcat(FileName, DIR_STR); if (FileTitle == NULL) FileTitle = "XMas.dat"; strcat(FileName, FileTitle); hFile = fopen(FileName, "wb"); if (hFile == NULL) { FileName[0x00] = '\0'; printerr("Critical XMas-Error!\n"); return false; } fwrite(XMasData, 0x01, XMasSize, hFile); fclose(hFile); } else { FileName = NULL; return false; } } else { // Unprepare Mode if (! remove(FileName)) return false; // btw: it's intentional that the user can grab the file from the temp-folder } return true; } #endif #ifndef WIN32 static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr) { const UINT16 CONV_TBL[0x20] = { 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, // 88-8F 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178}; // 98-9F UINT32 StrLen; UINT16 UnicodeChr; char* DstPtr; const unsigned char* SrcPtr; SrcPtr = (const unsigned char*)SrcStr; StrLen = 0x00; while(*SrcPtr != '\0') { if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0) UnicodeChr = *SrcPtr; else UnicodeChr = CONV_TBL[*SrcPtr - 0x80]; if (UnicodeChr < 0x0080) StrLen ++; else if (UnicodeChr < 0x0800) StrLen += 2; else StrLen += 3; SrcPtr ++; } *DstStr = (char*)malloc((StrLen + 0x01) * sizeof(char)); SrcPtr = (const unsigned char*)SrcStr; DstPtr = *DstStr; while(*SrcPtr != '\0') { if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0) UnicodeChr = (unsigned char)*SrcPtr; else UnicodeChr = CONV_TBL[*SrcPtr - 0x80]; if (UnicodeChr < 0x0080) { *DstPtr = UnicodeChr & 0xFF; DstPtr ++; } else if (UnicodeChr < 0x0800) { DstPtr[0x00] = 0xC0 | ((UnicodeChr >> 6) & 0x1F); DstPtr[0x01] = 0x80 | ((UnicodeChr >> 0) & 0x3F); DstPtr += 0x02; } else { DstPtr[0x00] = 0xE0 | ((UnicodeChr >> 12) & 0x0F); DstPtr[0x01] = 0x80 | ((UnicodeChr >> 6) & 0x3F); DstPtr[0x02] = 0x80 | ((UnicodeChr >> 0) & 0x3F); DstPtr += 0x03; } SrcPtr ++; } *DstPtr = '\0'; return; } #endif static bool OpenPlayListFile(const char* FileName) { const char M3UV2_HEAD[] = "#EXTM3U"; const char M3UV2_META[] = "#EXTINF:"; const UINT8 UTF8_SIG[] = {0xEF, 0xBB, 0xBF}; UINT32 METASTR_LEN; size_t RetVal; FILE* hFile; UINT32 LineNo; bool IsV2Fmt; UINT32 PLAlloc; char TempStr[0x1000]; // 4096 chars should be enough char* RetStr; bool IsUTF8; hFile = fopen(FileName, "rt"); if (hFile == NULL) return false; RetVal = fread(TempStr, 0x01, 0x03, hFile); if (RetVal >= 0x03) IsUTF8 = ! memcmp(TempStr, UTF8_SIG, 0x03); else IsUTF8 = false; rewind(hFile); PLAlloc = 0x0100; PLFileCount = 0x00; LineNo = 0x00; IsV2Fmt = false; METASTR_LEN = strlen(M3UV2_META); PlayListFile = (char**)malloc(PLAlloc * sizeof(char*)); while(! feof(hFile)) { RetStr = fgets(TempStr, 0x1000, hFile); if (RetStr == NULL) break; //RetStr = strchr(TempStr, 0x0D); //if (RetStr) // *RetStr = 0x00; // remove NewLine-Character RetStr = TempStr + strlen(TempStr) - 0x01; while(RetStr >= TempStr && *RetStr < 0x20) { *RetStr = '\0'; // remove NewLine-Characters RetStr --; } if (! strlen(TempStr)) continue; if (! LineNo) { if (! strcmp(TempStr, M3UV2_HEAD)) { IsV2Fmt = true; LineNo ++; continue; } } if (IsV2Fmt) { if (! strncmp(TempStr, M3UV2_META, METASTR_LEN)) { // Ignore Metadata of m3u Version 2 LineNo ++; continue; } } if (PLFileCount >= PLAlloc) { PLAlloc += 0x0100; PlayListFile = (char**)realloc(PlayListFile, PLAlloc * sizeof(char*)); } // TODO: // - supprt UTF-8 m3us under Windows // - force IsUTF8 via Commandline #ifdef WIN32 // Windows uses the 1252 Codepage by default PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char)); strcpy(PlayListFile[PLFileCount], TempStr); #else if (! IsUTF8) { // Most recent Linux versions use UTF-8, so I need to convert all strings. ConvertCP1252toUTF8(&PlayListFile[PLFileCount], TempStr); } else { PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char)); strcpy(PlayListFile[PLFileCount], TempStr); } #endif StandardizeDirSeparators(PlayListFile[PLFileCount]); PLFileCount ++; LineNo ++; } fclose(hFile); RetStr = GetLastDirSeparator(FileName); if (RetStr != NULL) { RetStr ++; strncpy(PLFileBase, FileName, RetStr - FileName); PLFileBase[RetStr - FileName] = '\0'; StandardizeDirSeparators(PLFileBase); } else { strcpy(PLFileBase, ""); } return true; } static bool OpenMusicFile(const char* FileName) { if (OpenVGMFile(FileName)) return true; else if (OpenOtherFile(FileName)) return true; return false; } static void wprintc(const wchar_t* format, ...) { va_list arg_list; int RetVal; UINT32 BufSize; wchar_t* printbuf; #ifdef WIN32 UINT32 StrLen; char* oembuf; DWORD CPMode; #endif BufSize = 0x00; printbuf = NULL; do { BufSize += 0x100; printbuf = (wchar_t*)realloc(printbuf, BufSize * sizeof(wchar_t)); // Note: On Linux every vprintf call needs its own set of va_start/va_end commands. // Under Windows (with VC6) one only one block for all calls works, too. va_start(arg_list, format); RetVal = _vsnwprintf(printbuf, BufSize - 0x01, format, arg_list); va_end(arg_list); } while(RetVal == -1 && BufSize < 0x1000); #ifdef WIN32 StrLen = wcslen(printbuf); // This is the only way to print Unicode stuff to the Windows console. // No, wprintf doesn't work. RetVal = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), printbuf, StrLen, &CPMode, NULL); if (! RetVal) // call failed (e.g. with ERROR_CALL_NOT_IMPLEMENTED on Win95) { // fallback to printf with OEM codepage oembuf = (char*)malloc(BufSize); /*if (GetConsoleOutputCP() == GetOEMCP()) CPMode = CP_OEMCP; else CPMode = CP_ACP;*/ CPMode = GetConsoleOutputCP(); WideCharToMultiByte(CPMode, 0x00, printbuf, StrLen + 1, oembuf, BufSize, NULL, NULL); printf("%s", oembuf); free(oembuf); } #else // on Linux, it's easy printf("%ls", printbuf); #endif free(printbuf); return; } static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock) { if (! Clock) return; if (ChipID == 0x00 && (Clock & 0x80000000)) Clock &= ~0x40000000; if (Clock & 0x80000000) { Clock &= ~0x80000000; ChipID |= 0x80; } if (Clock & 0x40000000) printf("2x"); printf("%s, ", GetAccurateChipName(ChipID, SubType)); return; } const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag) { const wchar_t* RetTag; if (EngTag == NULL || ! wcslen(EngTag)) { RetTag = JapTag; } else if (JapTag == NULL || ! wcslen(JapTag)) { RetTag = EngTag; } else { if (! PreferJapTag) RetTag = EngTag; else RetTag = JapTag; } if (RetTag == NULL) return L""; else return RetTag; } static void ShowVGMTag(void) { const wchar_t* TitleTag; const wchar_t* GameTag; const wchar_t* AuthorTag; const wchar_t* SystemTag; UINT8 CurChip; UINT32 ChpClk; UINT8 ChpType; INT16 VolMod; #ifdef SET_CONSOLE_TITLE wchar_t TitleStr[0x80]; UINT32 StrLen; #endif TitleTag = GetTagStrEJ(VGMTag.strTrackNameE, VGMTag.strTrackNameJ); GameTag = GetTagStrEJ(VGMTag.strGameNameE, VGMTag.strGameNameJ); AuthorTag = GetTagStrEJ(VGMTag.strAuthorNameE, VGMTag.strAuthorNameJ); SystemTag = GetTagStrEJ(VGMTag.strSystemNameE, VGMTag.strSystemNameJ); #ifdef SET_CONSOLE_TITLE // --- Show "Song (Game) - VGM Player" as Console Title --- if (! wcslen(TitleTag)) { char* TempPtr1; char* TempPtr2; TempPtr1 = strrchr(VgmFileName, '\\'); TempPtr2 = strrchr(VgmFileName, '/'); if (TempPtr1 < TempPtr2) TempPtr1 = TempPtr2; if (TempPtr1 == NULL) TempPtr1 = VgmFileName; else TempPtr1 ++; //strncpy(TitleStr, TempPtr1, 0x70); mbstowcs(TitleStr, TempPtr1, 0x7F); TitleStr[0x70] = '\0'; } else { #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(OLD_SWPRINTF) swprintf(TitleStr, L"%.*ls", 0x70, TitleTag); #else swprintf(TitleStr, 0x80, L"%.*ls", 0x70, TitleTag); #endif } StrLen = wcslen(TitleStr); if (wcslen(GameTag) && StrLen < 0x6C) { #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(OLD_SWPRINTF) swprintf(TitleStr + StrLen, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag); #else swprintf(TitleStr + StrLen, 0x80 - StrLen, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag); #endif StrLen = wcslen(TitleStr); } wcscat(TitleStr, L" - " APP_NAME_L); #ifdef WIN32 SetConsoleTitleW(TitleStr); // Set Windows Console Title #else printf("\x1B]0;%ls\x07", TitleStr); // Set xterm/rxvt Terminal Title #endif #endif // --- Display Tag Data --- if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) VolMod = VGMHead.bytVolumeModifier; else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) VolMod = VOLUME_MODIF_WRAP - 0x100; else VolMod = VGMHead.bytVolumeModifier - 0x100; wprintc(L"Track Title:\t%ls\n", TitleTag); wprintc(L"Game Name:\t%ls\n", GameTag); wprintc(L"System:\t\t%ls\n", SystemTag); wprintc(L"Composer:\t%ls\n", AuthorTag); wprintc(L"Release:\t%ls\n", VGMTag.strReleaseDate); printf("Version:\t%X.%02X\t", VGMHead.lngVersion >> 8, VGMHead.lngVersion & 0xFF); printf(" Gain:%5.2f\t", pow(2.0, VolMod / (double)0x20)); printf("Loop: "); if (VGMHead.lngLoopOffset) { UINT32 PbRateMul; UINT32 PbRateDiv; UINT32 PbSamples; // calculate samples for correct display with changed playback rate if (! VGMPbRate || ! VGMHead.lngRate) { PbRateMul = 1; PbRateDiv = 1; } else { PbRateMul = VGMHead.lngRate; PbRateDiv = VGMPbRate; } PbSamples = (UINT32)((UINT64)VGMHead.lngLoopSamples * PbRateMul / PbRateDiv); printf("Yes ("); PrintMinSec(PbSamples, VGMSampleRate); printf(")\n"); } else { printf("No\n"); } wprintc(L"VGM by:\t\t%ls\n", VGMTag.strCreator); wprintc(L"Notes:\t\t%ls\n", VGMTag.strNotes); printf("\n"); printf("Used chips:\t"); for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { ChpClk = GetChipClock(&VGMHead, CurChip, &ChpType); if (ChpClk && GetChipClock(&VGMHead, 0x80 | CurChip, NULL)) ChpClk |= 0x40000000; PrintChipStr(CurChip, ChpType, ChpClk); } printf("\b\b \n"); printf("\n"); return; } static void MMKey_Event(UINT8 event) { lastMMEvent = event; return; } #define LOG_SAMPLES (SampleRate / 5) static void PlayVGM_UI(void) { INT32 VGMPbSmplCount; INT32 PlaySmpl; UINT8 KeyCode; UINT32 VGMPlaySt; UINT32 VGMPlayEnd; char WavFileName[MAX_PATH]; char* TempStr; WAVE_16BS* TempBuf; UINT8 RetVal; UINT32 TempLng; bool PosPrint; bool LastUninit; bool QuitPlay; UINT32 PlayTimeEnd; printf("Initializing ...\r"); PlayVGM(); DBus_EmitSignal(SIGNAL_SEEK | SIGNAL_METADATA | SIGNAL_PLAYSTATUS | SIGNAL_CONTROLS); /*switch(LogToWave) { case 0x00: break; case 0x01: // Currently there's no record for Hardware FM PlayingMode = 0x00; // Impossible to log at full speed AND use FMPort break; case 0x02: if (PlayingMode == 0x01) LogToWave = 0x00; // Output and log sound (FM isn't logged) break; }*/ switch(PlayingMode) { case 0x00: AUDIOBUFFERU = 10; break; case 0x01: AUDIOBUFFERU = 0; // no AudioBuffers needed break; case 0x02: AUDIOBUFFERU = 5; // try to sync Hardware/Software Emulator as well as possible break; } if (AUDIOBUFFERU < NEED_LARGE_AUDIOBUFS) AUDIOBUFFERU = NEED_LARGE_AUDIOBUFS; if (ForceAudioBuf && AUDIOBUFFERU) AUDIOBUFFERU = ForceAudioBuf; switch(FileMode) { case 0x00: // VGM // RAW Log: no loop, no Creator, System Name set IsRAWLog = (! VGMHead.lngLoopOffset && ! wcslen(VGMTag.strCreator) && (wcslen(VGMTag.strSystemNameE) || wcslen(VGMTag.strSystemNameJ))); break; case 0x01: // CMF IsRAWLog = false; break; case 0x02: // DRO IsRAWLog = true; break; } if (! VGMHead.lngTotalSamples) IsRAWLog = false; #ifndef WIN32 changemode(true); #endif switch(PlayingMode) { case 0x00: case 0x02: if (LogToWave) { strcpy(WavFileName, VgmFileName); TempStr = GetFileExtension(WavFileName); if (TempStr == NULL) TempStr = WavFileName + strlen(WavFileName); else TempStr --; strcpy(TempStr, ".wav"); strcpy(SoundLogFile, WavFileName); } //FullBufFill = ! LogToWave; switch(LogToWave) { case 0x00: case 0x02: SoundLogging(LogToWave ? true : false); if (FirstInit || ! StreamStarted) { // support smooth transistions between songs RetVal = StartStream(OutputDevID); if (RetVal) { printf("Error openning Sound Device!\n"); return; } StreamStarted = true; } PauseStream(PausePlay); break; case 0x01: TempBuf = (WAVE_16BS*)malloc(SAMPLESIZE * LOG_SAMPLES); if (TempBuf == NULL) { printf("Allocation Error!\n"); return; } StartStream(0xFF); RetVal = SaveFile(0x00000000, NULL); if (RetVal) { printf("Can't open %s!\n", SoundLogFile); return; } break; } break; case 0x01: // PlayVGM() does it all //FullBufFill = true; break; } FirstInit = false; VGMPlaySt = VGMPos; if (VGMHead.lngGD3Offset) VGMPlayEnd = VGMHead.lngGD3Offset; else VGMPlayEnd = VGMHead.lngEOFOffset; VGMPlayEnd -= VGMPlaySt; if (! FileMode) VGMPlayEnd --; // EOF Command doesn't count PosPrint = true; PlayTimeEnd = 0; QuitPlay = false; while(! QuitPlay) { DBus_ReadWriteDispatch(); if(sigint) { QuitPlay = true; NextPLCmd = 0xFF; } if (! PausePlay || PosPrint) { PosPrint = false; VGMPbSmplCount = SampleVGM2Playback(VGMHead.lngTotalSamples); PlaySmpl = VGMPos - VGMPlaySt; #ifdef WIN32 printf("Playing %01.2f%%\t", 100.0 * PlaySmpl / VGMPlayEnd); #else // \t doesn't display correctly under Linux // but \b causes flickering under Windows printf("Playing %01.2f%% \b\b\b\t", 100.0 * PlaySmpl / VGMPlayEnd); #endif if (LogToWave != 0x01) { PlaySmpl = (BlocksSent - BlocksPlayed) * SMPL_P_BUFFER; PlaySmpl = VGMSmplPlayed - PlaySmpl; } else { PlaySmpl = VGMSmplPlayed; } if (! VGMCurLoop) { if (PlaySmpl < 0) PlaySmpl = 0; } else { //printf("%i", VGMSmplPos); while(PlaySmpl < SampleVGM2Playback(VGMHead.lngTotalSamples - VGMHead.lngLoopSamples)) PlaySmpl += SampleVGM2Playback(VGMHead.lngLoopSamples); } //if (PlaySmpl > VGMPbSmplCount) // PlaySmpl = VGMPbSmplCount; PrintMinSec(PlaySmpl, SampleRate); printf(" / "); PrintMinSec(VGMPbSmplCount, SampleRate); printf(" seconds"); if (Show95Cmds && Last95Max != 0xFFFF) { UINT16 drumID = 1 + Last95Drum; // 0-based -> 1-based, 0xFFFF = 0 if (Show95Cmds == 0x01) printf(" %02hX / %02hX", drumID , Last95Max); else if (Show95Cmds == 0x02) printf(" %02hX / %02hX at %5u Hz", drumID, Last95Max, Last95Freq); else if (Show95Cmds == 0x03) printf(" %02hX / %02hX at %4.1f KHz", drumID, Last95Max, Last95Freq / 1000.0); } //printf(" %u / %u", multipcm_get_channels(0, NULL), 28); printf("\r"); #ifndef WIN32 fflush(stdout); #endif if (LogToWave == 0x01 && ! PausePlay) { TempLng = FillBuffer(TempBuf, LOG_SAMPLES); if (TempLng) SaveFile(TempLng, TempBuf); if (EndPlay) break; } else { #ifdef WIN32 Sleep(50); #endif } } else { #ifdef WIN32 Sleep(1); #endif } #ifndef WIN32 if (! PausePlay && PlayingMode != 0x01) WaveOutLinuxCallBack(); else Sleep(50); #endif if (EndPlay) { if (! PlayTimeEnd) { PlayTimeEnd = PlayingTime; // quitting now terminates the program, so I need some special // checks to make sure that the rest of the audio buffer is played if (! PLFileCount || CurPLFile >= PLFileCount - 0x01) { if (FileMode == 0x01) PlayTimeEnd += SampleRate << 1; // Add 2 secs PlayTimeEnd += AUDIOBUFFERU * SMPL_P_BUFFER; } } if (PlayingTime >= PlayTimeEnd) QuitPlay = true; } if (_kbhit() || lastMMEvent) { if (lastMMEvent) { if (lastMMEvent == MMKEY_PLAY) KeyCode = ' '; else if (lastMMEvent == MMKEY_PREV) KeyCode = 'B'; else if (lastMMEvent == MMKEY_NEXT) KeyCode = 'N'; lastMMEvent = 0x00; } else { KeyCode = _getch(); } if (KeyCode < 0x80) KeyCode = toupper(KeyCode); switch(KeyCode) { #ifndef WIN32 case 0x1B: // Special Key KeyCode = _getch(); if (KeyCode == 0x1B || KeyCode == 0x00) { // ESC Key pressed QuitPlay = true; NextPLCmd = 0xFF; break; } switch(KeyCode) { case 0x5B: // Cursor-Key Table // Key KeyCode // Up 41 // Down 42 // Left 44 // Right 43 // Cursor only: CursorKey // Ctrl: 0x31 + 0x3B + 0x35 + CursorKey // Alt: 0x31 + 0x3B + 0x33 + CursorKey // Page-Keys: PageKey + 0x7E // PageUp 35 // PageDown 36 KeyCode = _getch(); // Get 2nd Key // Convert Cursor Key Code from Linux to Windows switch(KeyCode) { case 0x31: // Ctrl or Alt key KeyCode = _getch(); if (KeyCode == 0x3B) { KeyCode = _getch(); if (KeyCode == 0x35) { KeyCode = _getch(); switch(KeyCode) { case 0x41: KeyCode = 0x8D; break; case 0x42: KeyCode = 0x91; break; case 0x43: KeyCode = 0x74; break; case 0x44: KeyCode = 0x73; break; default: KeyCode = 0x00; break; } } } if ((KeyCode & 0xF0) == 0x30) KeyCode = 0x00; break; case 0x35: KeyCode = 0x49; _getch(); break; case 0x36: KeyCode = 0x51; _getch(); break; case 0x41: KeyCode = 0x48; break; case 0x42: KeyCode = 0x50; break; case 0x43: KeyCode = 0x4D; break; case 0x44: KeyCode = 0x4B; break; default: KeyCode = 0x00; break; } } // At this point I have Windows-style keys. #else //#ifdef WIN32 case 0xE0: // Special Key // Cursor-Key Table // Shift + Cursor results in the usual value for the Cursor Key // Alt + Cursor results in 0x00 + (0x50 + CursorKey) (0x00 instead of 0xE0) // Key None Ctrl // Up 48 8D // Down 50 91 // Left 4B 73 // Right 4D 74 KeyCode = _getch(); // Get 2nd Key #endif switch(KeyCode) { case 0x4B: // Cursor Left PlaySmpl = -5; break; case 0x4D: // Cursor Right PlaySmpl = 5; break; case 0x73: // Ctrl + Cursor Left PlaySmpl = -60; break; case 0x74: // Ctrl + Cursor Right PlaySmpl = 60; break; case 0x49: // Page Up if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile) { NextPLCmd = 0x01; QuitPlay = true; } PlaySmpl = 0; break; case 0x51: // Page Down if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01) { NextPLCmd = 0x00; QuitPlay = true; } PlaySmpl = 0; break; default: PlaySmpl = 0; break; } if (PlaySmpl) { SeekVGM(true, PlaySmpl * SampleRate); PosPrint = true; DBus_EmitSignal(SIGNAL_SEEK); } break; #ifdef WIN32 case 0x1B: // ESC #endif case 'Q': QuitPlay = true; NextPLCmd = 0xFF; break; case ' ': PauseVGM(! PausePlay); PosPrint = true; DBus_EmitSignal(SIGNAL_PLAYSTATUS); // Emit status change signal break; case 'F': // Fading FadeTime = FadeTimeN; FadePlay = true; break; case 'R': // Restart DBus_EmitSignal(SIGNAL_SEEK); RestartVGM(); PosPrint = true; break; case 'B': // Previous file (Back) if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile) { NextPLCmd = 0x01; QuitPlay = true; } break; case 'N': // Next file if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01) { NextPLCmd = 0x00; QuitPlay = true; } break; } } /*if (! PauseThread && FadePlay && (! FadeTime || MasterVol == 0.0f)) { QuitPlay = true; }*/ if (FadeRAWLog && IsRAWLog && ! PausePlay && ! FadePlay && FadeTimeN) { PlaySmpl = (INT32)VGMHead.lngTotalSamples - FadeTimeN * VGMSampleRate / 1500; if (VGMSmplPos >= PlaySmpl) { FadeTime = FadeTimeN; FadePlay = true; // (FadeTime / 1500) ends at 33% } } } ThreadNoWait = false; // Last Uninit: ESC pressed, no playlist, last file in playlist LastUninit = (NextPLCmd & 0x80) || ! PLFileCount || (NextPLCmd == 0x00 && CurPLFile >= PLFileCount - 0x01); switch(PlayingMode) { case 0x00: switch(LogToWave) { case 0x00: case 0x02: if (LastUninit) { StopStream(); StreamStarted = false; } else { if (ThreadPauseEnable) { ThreadPauseConfrm = false; PauseThread = true; while(! ThreadPauseConfrm) Sleep(1); // Wait until the Thread is finished } else { PauseThread = true; } if (LogToWave) SaveFile(0xFFFFFFFF, NULL); } break; case 0x01: SaveFile(0xFFFFFFFF, NULL); break; } break; case 0x01: if (StreamStarted) { StopStream(); StreamStarted = false; } break; case 0x02: if (LastUninit) { StopStream(); StreamStarted = false; #ifdef MIXER_MUTING #ifdef WIN32 mixerClose(hmixer); #else close(hmixer); #endif #endif } else { if (ThreadPauseEnable) { ThreadPauseConfrm = false; PauseThread = true; while(! ThreadPauseConfrm) Sleep(1); // Wait until the Thread is finished PauseStream(true); } else { PauseThread = true; } } break; } #ifndef WIN32 changemode(false); #endif StopVGM(); printf("\nPlaying finished.\n"); return; } INLINE INT8 sign(double Value) { if (Value > 0.0) return 1; else if (Value < 0.0) return -1; else return 0; } INLINE long int Round(double Value) { // Alternative: (fabs(Value) + 0.5) * sign(Value); return (long int)(Value + 0.5 * sign(Value)); } INLINE double RoundSpecial(double Value, double RoundTo) { return (long int)(Value / RoundTo + 0.5 * sign(Value)) * RoundTo; } static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate) { float TimeSec; UINT16 TimeMin; UINT16 TimeHours; TimeSec = (float)RoundSpecial(SamplePos / (double)SmplRate, 0.01); //TimeSec = SamplePos / (float)SmplRate; TimeMin = (UINT16)TimeSec / 60; TimeSec -= TimeMin * 60; if (! PrintMSHours) { printf("%02hu:%05.2f", TimeMin, TimeSec); } else { TimeHours = TimeMin / 60; TimeMin %= 60; printf("%hu:%02hu:%05.2f", TimeHours, TimeMin, TimeSec); } return; } ================================================ FILE: VGMPlay/VGMPlay_AddFmts.c ================================================ // VGMPlay_AddFmts.c: C Source File for playback of additional non-VGM formats #define _GNU_SOURCE #include #include #include #include #include "stdbool.h" #include #ifdef WIN32 //#include void __stdcall Sleep(unsigned int dwMilliseconds); #else #include #define Sleep(msec) usleep(msec * 1000) #endif #include #include "chips/mamedef.h" #include "VGMPlay.h" #include "ChipMapper.h" // Structures for DRO and CMF files typedef struct _cmf_file_header { UINT32 fccCMF; UINT16 shtVersion; UINT16 shtOffsetInsData; UINT16 shtOffsetMusData; UINT16 shtTickspQuarter; UINT16 shtTickspSecond; UINT16 shtOffsetTitle; UINT16 shtOffsetAuthor; UINT16 shtOffsetComments; UINT8 bytChnUsed[0x10]; UINT16 shtInstrumentCount; UINT16 shtTempo; } CMF_HEADER; typedef struct _cmf_instrument_table { UINT8 Character[0x02]; UINT8 ScaleLevel[0x02]; UINT8 AttackDelay[0x02]; UINT8 SustnRelease[0x02]; UINT8 WaveSelect[0x02]; UINT8 FeedbConnect; UINT8 Reserved[0x5]; } CMF_INSTRUMENT; typedef struct _dro_file_header { char cSignature[0x08]; UINT16 iVersionMajor; UINT16 iVersionMinor; } DRO_HEADER; typedef struct _dro_version_header_1 { UINT32 iLengthMS; UINT32 iLengthBytes; UINT32 iHardwareType; } DRO_VER_HEADER_1; typedef struct _dro_version_header_2 { UINT32 iLengthPairs; UINT32 iLengthMS; UINT8 iHardwareType; UINT8 iFormat; UINT8 iCompression; UINT8 iShortDelayCode; UINT8 iLongDelayCode; UINT8 iCodemapLength; } DRO_VER_HEADER_2; #define FCC_CMF 0x464D5443 // 'CTMF' #define FCC_DRO1 0x41524244 // 'DBRA' #define FCC_DRO2 0x4C504F57 // 'WOPL' extern UINT32 GetGZFileLength(const char* FileName); //bool OpenOtherFile(const char* FileName) INLINE UINT16 ReadLE16(const UINT8* Data); INLINE UINT32 ReadLE32(const UINT8* Data); INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue); static UINT32 GetMIDIDelay(UINT32* DelayLen); static UINT16 MIDINote2FNum(UINT8 Note, INT8 Pitch); static void SendMIDIVolume(UINT8 ChipID, UINT8 Channel, UINT8 Command, UINT8 ChnIns, UINT8 Volume); //void InterpretOther(UINT32 SampleCount); INLINE INT32 SampleVGM2Playback(INT32 SampleVal); INLINE INT32 SamplePlayback2VGM(INT32 SampleVal); extern UINT32 SampleRate; // Note: also used by some sound cores to determinate the chip sample rate extern UINT8 FileMode; extern VGM_HEADER VGMHead; extern UINT32 VGMDataLen; extern UINT8* VGMData; extern GD3_TAG VGMTag; CMF_HEADER CMFHead; UINT16 CMFInsCount; CMF_INSTRUMENT* CMFIns; DRO_HEADER DROHead; DRO_VER_HEADER_2 DROInf; UINT8* DROCodemap; extern UINT32 VGMPos; extern INT32 VGMSmplPos; extern INT32 VGMSmplPlayed; extern INT32 VGMSampleRate; extern UINT32 BlocksSent; extern UINT32 BlocksPlayed; extern bool VGMEnd; extern bool EndPlay; extern bool PausePlay; extern bool FadePlay; extern bool ForceVGMExec; extern UINT32 VGMMaxLoop; UINT32 CMFMaxLoop; extern UINT32 VGMMaxLoopM; extern UINT32 VGMCurLoop; extern UINT32 FadeTime; extern UINT32 VGMMaxLoop; extern bool ErrorHappened; extern UINT8 CmdList[0x100]; bool OpenOtherFile(const char* FileName) { gzFile hFile; UINT32 FileSize; UINT32 fccHeader; UINT32 CurPos; UINT32 TempLng; UINT16 FileVer; const char* TempStr; DRO_VER_HEADER_1 DRO_V1; FileSize = GetGZFileLength(FileName); FileMode = 0x00; hFile = gzopen(FileName, "rb"); if (hFile == NULL) return false; gzseek(hFile, 0x00, SEEK_SET); gzgetLE32(hFile, &fccHeader); switch(fccHeader) { case FCC_VGM: FileMode = 0xFF; // should already be opened break; case FCC_CMF: FileMode = 0x01; break; case FCC_DRO1: gzgetLE32(hFile, &fccHeader); if (fccHeader == FCC_DRO2) FileMode = 0x02; else FileMode = 0xFF; break; default: FileMode = 0xFF; break; } if (FileMode == 0xFF) goto OpenErr; VGMTag.strTrackNameE = L""; VGMTag.strTrackNameJ = L""; VGMTag.strGameNameE = L""; VGMTag.strGameNameJ = L""; VGMTag.strSystemNameE = L""; VGMTag.strSystemNameJ = L""; VGMTag.strAuthorNameE = L""; VGMTag.strAuthorNameJ = L""; VGMTag.strReleaseDate = L""; VGMTag.strCreator = L""; VGMTag.strNotes = L""; switch(FileMode) { case 0x00: // VGM File break; case 0x01: // CMF File case 0x02: // DosBox RAW OPL VGMTag.strGameNameE = (wchar_t*)malloc(0x10 * sizeof(wchar_t*)); wcscpy(VGMTag.strGameNameE, L" Player"); VGMTag.strSystemNameE = L"PC / MS-DOS"; break; } VGMDataLen = FileSize; switch(FileMode) { case 0x00: // VGM File // already done by OpenVGMFile break; case 0x01: // CMF File // Read Data VGMData = (UINT8*)malloc(VGMDataLen); if (VGMData == NULL) goto OpenErr; gzseek(hFile, 0x00, SEEK_SET); gzread(hFile, VGMData, VGMDataLen); #ifdef VGM_LITTLE_ENDIAN memcpy(&CMFHead, &VGMData[0x00], sizeof(CMF_HEADER)); #else CMFHead.fccCMF = ReadLE32(&VGMData[0x00]); CMFHead.shtVersion = ReadLE16(&VGMData[0x04]); CMFHead.shtOffsetInsData = ReadLE16(&VGMData[0x06]); CMFHead.shtOffsetMusData = ReadLE16(&VGMData[0x08]); CMFHead.shtTickspQuarter = ReadLE16(&VGMData[0x0A]); CMFHead.shtTickspSecond = ReadLE16(&VGMData[0x0C]); CMFHead.shtOffsetTitle = ReadLE16(&VGMData[0x0E]); CMFHead.shtOffsetAuthor = ReadLE16(&VGMData[0x10]); CMFHead.shtOffsetComments = ReadLE16(&VGMData[0x12]); memcpy(CMFHead.bytChnUsed, &VGMData[0x14], 0x10); CMFHead.shtInstrumentCount = ReadLE16(&VGMData[0x24]); CMFHead.shtTempo = ReadLE16(&VGMData[0x26]); #endif if (CMFHead.shtVersion == 0x0100) { CMFHead.shtInstrumentCount &= 0x00FF; CMFHead.shtTempo = (UINT16)(60.0 * CMFHead.shtTickspQuarter / CMFHead.shtTickspSecond + 0.5); } if (CMFHead.shtOffsetTitle) { TempStr = (char*)&VGMData[CMFHead.shtOffsetTitle]; TempLng = strlen(TempStr) + 0x01; VGMTag.strTrackNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); mbstowcs(VGMTag.strTrackNameE, TempStr, TempLng); } VGMTag.strGameNameE[0x00] = 'C'; VGMTag.strGameNameE[0x01] = 'M'; VGMTag.strGameNameE[0x02] = 'F'; if (CMFHead.shtOffsetAuthor) { TempStr = (char*)&VGMData[CMFHead.shtOffsetAuthor]; TempLng = strlen(TempStr) + 0x01; VGMTag.strAuthorNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); mbstowcs(VGMTag.strAuthorNameE, TempStr, TempLng); } if (CMFHead.shtOffsetComments) { TempStr = (char*)&VGMData[CMFHead.shtOffsetComments]; TempLng = strlen(TempStr) + 0x01; VGMTag.strNotes = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); mbstowcs(VGMTag.strNotes, TempStr, TempLng); } CMFInsCount = CMFHead.shtInstrumentCount; TempLng = CMFInsCount * sizeof(CMF_INSTRUMENT); CMFIns = (CMF_INSTRUMENT*)malloc(TempLng); memcpy(CMFIns, &VGMData[CMFHead.shtOffsetInsData], TempLng); memset(&VGMHead, 0x00, sizeof(VGM_HEADER)); VGMHead.lngEOFOffset = VGMDataLen; VGMHead.lngVersion = CMFHead.shtVersion; VGMHead.lngDataOffset = CMFHead.shtOffsetMusData; VGMSampleRate = CMFHead.shtTickspSecond; VGMHead.lngTotalSamples = 0; VGMHead.lngHzYM3812 = 3579545 | 0x40000000; break; case 0x02: // DosBox RAW OPL // Read Data VGMData = (UINT8*)malloc(VGMDataLen); if (VGMData == NULL) goto OpenErr; gzseek(hFile, 0x00, SEEK_SET); VGMDataLen = gzread(hFile, VGMData, VGMDataLen); VGMTag.strGameNameE[0x00] = 'D'; VGMTag.strGameNameE[0x01] = 'R'; VGMTag.strGameNameE[0x02] = 'O'; memset(&VGMHead, 0x00, sizeof(VGM_HEADER)); CurPos = 0x00; #ifdef VGM_LITTLE_ENDIAN memcpy(&DROHead, &VGMData[CurPos], sizeof(DRO_HEADER)); #else memcpy(DROHead.cSignature, &VGMData[CurPos + 0x00], 0x08); DROHead.iVersionMajor = ReadLE16( &VGMData[CurPos + 0x08]); DROHead.iVersionMinor = ReadLE16( &VGMData[CurPos + 0x0A]); #endif CurPos += sizeof(DRO_HEADER); memcpy(&TempLng, &VGMData[0x08], sizeof(UINT32)); if (TempLng & 0xFF00FF00) { // DosBox Version 0.61 // this version didn't contain Version Bytes CurPos = 0x08; DROHead.iVersionMajor = 0x00; DROHead.iVersionMinor = 0x00; } else if (! (TempLng & 0x0000FFFF)) { // DosBox Version 0.63 // the order of the Version Bytes is swapped in this version FileVer = DROHead.iVersionMinor; if (FileVer == 0x01) { DROHead.iVersionMinor = DROHead.iVersionMajor; DROHead.iVersionMajor = FileVer; } } VGMHead.lngEOFOffset = VGMDataLen; VGMHead.lngVersion = (DROHead.iVersionMajor << 8) | ((DROHead.iVersionMinor & 0xFF) << 0); VGMSampleRate = 1000; if (DROHead.iVersionMajor > 2) DROHead.iVersionMajor = 2; switch(DROHead.iVersionMajor) { case 0: // Version 0 (DosBox Version 0.61) case 1: // Version 1 (DosBox Version 0.63) switch(DROHead.iVersionMajor) { case 0: // Version 0 DRO_V1.iLengthMS = ReadLE32(&VGMData[CurPos + 0x00]); DRO_V1.iLengthBytes = ReadLE32(&VGMData[CurPos + 0x04]); DRO_V1.iHardwareType = VGMData[CurPos + 0x08]; CurPos += 0x09; break; case 1: // Version 1 DRO_V1.iLengthMS = ReadLE32(&VGMData[CurPos + 0x00]); DRO_V1.iLengthBytes = ReadLE32(&VGMData[CurPos + 0x04]); DRO_V1.iHardwareType = ReadLE32(&VGMData[CurPos + 0x08]); CurPos += 0x0C; break; } DROInf.iLengthPairs = DRO_V1.iLengthBytes >> 1; DROInf.iLengthMS = DRO_V1.iLengthMS; switch(DRO_V1.iHardwareType) { case 0x01: // Single OPL3 DROInf.iHardwareType = 0x02; break; case 0x02: // Dual OPL2 DROInf.iHardwareType = 0x01; break; default: DROInf.iHardwareType = (UINT8)DRO_V1.iHardwareType; break; } DROInf.iFormat = 0x00; DROInf.iCompression = 0x00; DROInf.iShortDelayCode = 0x00; DROInf.iLongDelayCode = 0x01; DROInf.iCodemapLength = 0x00; break; case 2: // Version 2 (DosBox Version 0.73) // sizeof(DRO_VER_HEADER_2) returns 0x10, but the exact size is 0x0E //memcpy(&DROInf, &VGMData[CurPos], 0x0E); DROInf.iLengthPairs = ReadLE32( &VGMData[CurPos + 0x00]); DROInf.iLengthMS = ReadLE32( &VGMData[CurPos + 0x04]); DROInf.iHardwareType = VGMData[CurPos + 0x08]; DROInf.iFormat = VGMData[CurPos + 0x09]; DROInf.iCompression = VGMData[CurPos + 0x0A]; DROInf.iShortDelayCode = VGMData[CurPos + 0x0B]; DROInf.iLongDelayCode = VGMData[CurPos + 0x0C]; DROInf.iCodemapLength = VGMData[CurPos + 0x0D]; CurPos += 0x0E; break; } if (DROInf.iCodemapLength) { DROCodemap = (UINT8*)malloc(DROInf.iCodemapLength * sizeof(UINT8)); memcpy(DROCodemap, &VGMData[CurPos], DROInf.iCodemapLength); CurPos += DROInf.iCodemapLength; } else { DROCodemap = NULL; } VGMHead.lngDataOffset = CurPos; VGMHead.lngTotalSamples = DROInf.iLengthMS; switch(DROInf.iHardwareType) { case 0x00: // Single OPL2 Chip VGMHead.lngHzYM3812 = 3579545; break; case 0x01: // Dual OPL2 Chip VGMHead.lngHzYM3812 = 3579545 | 0xC0000000; break; case 0x02: // Single OPL3 Chip VGMHead.lngHzYMF262 = 14318180; break; default: VGMHead.lngHzYM3812 = 3579545 | 0x40000000; break; } break; } gzclose(hFile); return true; OpenErr: gzclose(hFile); return false; } INLINE UINT16 ReadLE16(const UINT8* Data) { // read 16-Bit Word (Little Endian/Intel Byte Order) #ifdef VGM_LITTLE_ENDIAN return *(UINT16*)Data; #else return (Data[0x01] << 8) | (Data[0x00] << 0); #endif } INLINE UINT32 ReadLE32(const UINT8* Data) { // read 32-Bit Word (Little Endian/Intel Byte Order) #ifdef VGM_LITTLE_ENDIAN return *(UINT32*)Data; #else return (Data[0x03] << 24) | (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); #endif } INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue) { #ifdef VGM_LITTLE_ENDIAN return gzread(hFile, RetValue, 0x04); #else int RetVal; UINT8 Data[0x04]; RetVal = gzread(hFile, Data, 0x04); *RetValue = (Data[0x03] << 24) | (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); return RetVal; #endif } static UINT32 GetMIDIDelay(UINT32* DelayLen) { UINT32 CurPos; UINT32 DelayVal; CurPos = VGMPos; DelayVal = 0x00; do { DelayVal = (DelayVal << 7) | (VGMData[CurPos] & 0x7F); } while(VGMData[CurPos ++] & 0x80); if (DelayLen != NULL) *DelayLen = CurPos - VGMPos; return DelayVal; } static UINT16 MIDINote2FNum(UINT8 Note, INT8 Pitch) { const double CHIP_RATE = 3579545.0 / 72.0; // ~49716 double FreqVal; INT8 BlockVal; UINT16 KeyVal; FreqVal = 440.0 * pow(2, (Note - 69 + Pitch / 256.0) / 12.0); BlockVal = (Note / 12) - 1; if (BlockVal < 0x00) BlockVal = 0x00; else if (BlockVal > 0x07) BlockVal = 0x07; KeyVal = (UINT16)(FreqVal * pow(2, 20 - BlockVal) / CHIP_RATE + 0.5); return (BlockVal << 10) | KeyVal; // << (8+2) } static void SendMIDIVolume(UINT8 ChipID, UINT8 Channel, UINT8 Command, UINT8 ChnIns, UINT8 Volume) { bool RhythmOn; UINT8 TempByt; //UINT16 TempSht; UINT32 TempLng; UINT8 OpBase; // Operator Base CMF_INSTRUMENT* TempIns; UINT8 OpMask; INT8 OpVol; INT8 NoteVol; RhythmOn = (Channel >> 7) & 0x01; Channel &= 0x7F; // Refresh Total Level (Volume) TempIns = CMFIns + ChnIns; OpBase = (Channel / 0x03) * 0x08 + (Channel % 0x03); if (! RhythmOn) { TempLng = 0x01; OpMask = 0x03; } else { //TempLng = 0x01; switch(Command & 0x0F) { case 0x0B: // Bass Drum OpMask = 0x00; break; case 0x0F: // Hi Hat OpMask = 0x00; break; case 0x0C: // Snare Drum OpMask = 0x01; break; case 0x0D: // Tom Tom OpMask = 0x00; break; case 0x0E: // Cymbal OpMask = 0x01; break; default: OpMask = 0x00; break; } TempLng = OpMask; OpMask *= 0x03; } // Verified with PLAY.EXE OpVol = (Volume + 0x04) >> 3; OpVol = 0x10 - (OpVol << 1); if (OpVol < 0x00) OpVol >>= 1; NoteVol = (TempIns->ScaleLevel[TempLng] & 0x3F) + OpVol; if (NoteVol < 0x00) NoteVol = 0x00; TempByt = NoteVol | (TempIns->ScaleLevel[TempLng] & 0xC0); chip_reg_write(0x09, ChipID, 0x00, 0x40 | (OpBase + OpMask), TempByt); return; } void InterpretOther(UINT32 SampleCount) { static UINT8 LastCmd = 0x90; static UINT8 DrumReg[0x02] = {0x00, 0x00}; static UINT8 ChnIns[0x10]; static UINT8 ChnNote[0x20]; static INT8 ChnPitch[0x10]; INT32 SmplPlayed; UINT8 Command; UINT8 Channel; UINT8 TempByt; UINT16 TempSht; UINT32 TempLng; UINT32 DataLen; static UINT8 CurChip = 0x00; UINT8 OpBase; // Operator Base CMF_INSTRUMENT* TempIns; bool RhythmOn; bool NoteOn; UINT8 OpMask; if (VGMEnd) return; if (PausePlay && ! ForceVGMExec) return; switch(FileMode) { case 0x01: // CMF File Mode if (! SampleCount) { memset(ChnIns, 0xFF, 0x10); memset(ChnNote, 0xFF, 0x20); memset(ChnPitch, 0x00, 0x10); TempLng = VGMPos; SmplPlayed = VGMSmplPos; VGMPos = VGMHead.lngDataOffset; RhythmOn = false; while(! RhythmOn) { VGMSmplPos += GetMIDIDelay(&DataLen); VGMPos += DataLen; Command = VGMData[VGMPos]; if (Command & 0x80) VGMPos ++; else Command = LastCmd; Channel = Command & 0x0F; switch(Command & 0xF0) { case 0xF0: // End Of File switch(Command) { case 0xFF: switch(VGMData[VGMPos + 0x00]) { case 0x2F: VGMHead.lngTotalSamples = VGMSmplPos; VGMHead.lngLoopSamples = VGMHead.lngTotalSamples; RhythmOn = true; break; } break; default: VGMPos += 0x01; } break; case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0: VGMPos += 0x02; break; case 0xC0: case 0xD0: VGMPos += 0x01; break; } if (Command < 0xF0) LastCmd = Command; } VGMPos = TempLng; VGMSmplPos = SmplPlayed; } SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); while(true) { TempLng = GetMIDIDelay(&DataLen); if (VGMSmplPos + (INT32)TempLng > SmplPlayed) break; VGMSmplPos += TempLng; VGMPos += DataLen; Command = VGMData[VGMPos]; if (Command & 0x80) VGMPos ++; else Command = LastCmd; Channel = Command & 0x0F; if (DrumReg[0x00] & 0x20) { if (Channel < 0x0B) { CurChip = Channel / 0x06; Channel = Channel % 0x06; } else { Channel -= 0x0B; CurChip = Channel / 0x03; Channel = Channel % 0x03; if (CurChip == 0x01 && Channel == 0x00) Channel = 0x02; Channel += 0x06; // Map all drums to one chip CurChip = 0x00; } } else { CurChip = Channel / 0x09; Channel = Channel % 0x09; } CurChip = 0x00; RhythmOn = (Channel >= 0x06) && (DrumReg[CurChip] & 0x20); switch(Command & 0xF0) { case 0xF0: // End Of File switch(Command) { case 0xFF: switch(VGMData[VGMPos + 0x00]) { case 0x2F: if (CMFMaxLoop != 0x01) { VGMPos = VGMHead.lngDataOffset; VGMSmplPos -= VGMHead.lngLoopSamples; VGMSmplPlayed -= SampleVGM2Playback(VGMHead.lngLoopSamples); SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); VGMCurLoop ++; if (CMFMaxLoop && VGMCurLoop >= CMFMaxLoop) FadePlay = true; if (FadePlay && ! FadeTime) VGMEnd = true; } else { VGMEnd = true; break; } break; } break; default: VGMPos += 0x01; } break; case 0x80: case 0x90: TempSht = MIDINote2FNum(VGMData[VGMPos + 0x00], ChnPitch[Channel]); if ((Command & 0xF0) == 0x80) NoteOn = false; else NoteOn = VGMData[VGMPos + 0x01] ? true : false; if (! RhythmOn) // Set "Key On" TempSht |= (UINT8)NoteOn << 13; // << (8+5) if (NoteOn) { for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { if (ChnNote[(CurChip << 4) | Channel] == 0xFF) { ChnNote[(CurChip << 4) | Channel] = VGMData[VGMPos + 0x00]; break; } } if (CurChip >= 0x02) { CurChip = 0x00; ChnNote[(CurChip << 4) | Channel] = VGMData[VGMPos + 0x00]; } } else { for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { if (ChnNote[(CurChip << 4) | Channel] != 0xFF) { ChnNote[(CurChip << 4) | Channel] = 0xFF; break; } } } if (CurChip >= 0x02) CurChip = 0xFF; if (CurChip != 0xFF) { if (NoteOn) { if (! RhythmOn) SendMIDIVolume(CurChip, Channel | (RhythmOn << 7), Command, ChnIns[Command & 0x0F], VGMData[VGMPos + 0x01]); } if (NoteOn || ! RhythmOn) { chip_reg_write(0x09, CurChip, 0x00, 0xA0 | Channel, TempSht & 0xFF); chip_reg_write(0x09, CurChip, 0x00, 0xB0 | Channel, TempSht >> 8); } if (RhythmOn) { TempByt = 0x0F - (Command & 0x0F); DrumReg[CurChip] &= ~(0x01 << TempByt); if (NoteOn) chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[CurChip]); DrumReg[CurChip] |= (UINT8)NoteOn << TempByt; chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[CurChip]); } } VGMPos += 0x02; break; case 0xB0: NoteOn = false; switch(VGMData[VGMPos + 0x00]) { case 0x66: // Marker break; case 0x67: // Rhythm Mode if (! VGMData[VGMPos + 0x01]) { // Set Rhythm Mode off DrumReg[0x00] = 0xC0; DrumReg[0x01] = 0xC0; } else { // Set Rhythm Mode on DrumReg[0x00] = 0xE0; DrumReg[0x01] = 0xE0; } chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[0x00]); chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[0x01]); break; case 0x68: // Pitch Upward ChnPitch[Channel] = +VGMData[VGMPos + 0x01]; NoteOn = true; break; case 0x69: // Pitch Downward ChnPitch[Channel] = -VGMData[VGMPos + 0x01]; NoteOn = true; break; } if (NoteOn) { for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { TempByt = ChnNote[(CurChip << 4) | Channel]; if (! RhythmOn && TempByt != 0xFF) { TempSht = MIDINote2FNum(TempByt, ChnPitch[Channel]); TempSht |= 0x01 << 13; // << (8+5) chip_reg_write(0x09, CurChip, 0x00, 0xA0 | Channel, TempSht & 0xFF); chip_reg_write(0x09, CurChip, 0x00, 0xB0 | Channel, TempSht >> 8); } } } VGMPos += 0x02; break; case 0xC0: TempByt = VGMData[VGMPos + 0x00]; if (TempByt >= CMFInsCount) { //VGMPos += 0x01; //break; TempByt %= CMFInsCount; } TempIns = CMFIns + TempByt; ChnIns[Command & 0x0F] = TempByt; OpBase = (Channel / 0x03) * 0x08 + (Channel % 0x03); if (! RhythmOn) { OpMask = 0x03; } else { switch(Command & 0x0F) { case 0x0B: // Bass Drum OpMask = 0x03; break; case 0x0F: // Hi Hat OpMask = 0x01; //Channel = 0x01; // PLAY.EXE really does this sometimes //OpBase = 0x01; break; case 0x0C: // Snare Drum OpMask = 0x02; break; case 0x0D: // Tom Tom OpMask = 0x01; break; case 0x0E: // Cymbal OpMask = 0x02; break; default: OpMask = 0x03; break; } } for (CurChip = 0x00; CurChip < 0x02; CurChip ++) { TempByt = 0x00; if (OpMask & 0x01) { // Write Operator 1 chip_reg_write(0x09, CurChip, 0x00, 0x20 | (OpBase + 0x00), TempIns->Character[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x40 | (OpBase + 0x00), TempIns->ScaleLevel[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x60 | (OpBase + 0x00), TempIns->AttackDelay[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x80 | (OpBase + 0x00), TempIns->SustnRelease[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0xE0 | (OpBase + 0x00), TempIns->WaveSelect[TempByt]); TempByt ++; } if (OpMask & 0x02) { // Write Operator 2 chip_reg_write(0x09, CurChip, 0x00, 0x20 | (OpBase + 0x03), TempIns->Character[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x40 | (OpBase + 0x03), TempIns->ScaleLevel[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x60 | (OpBase + 0x03), TempIns->AttackDelay[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0x80 | (OpBase + 0x03), TempIns->SustnRelease[TempByt]); chip_reg_write(0x09, CurChip, 0x00, 0xE0 | (OpBase + 0x03), TempIns->WaveSelect[TempByt]); TempByt ++; } chip_reg_write(0x09, CurChip, 0x00, 0xC0 | Channel, TempIns->FeedbConnect); } VGMPos += 0x01; break; case 0xA0: case 0xE0: VGMPos += 0x02; break; case 0xD0: VGMPos += 0x01; break; } if (Command < 0xF0) LastCmd = Command; if (VGMEnd) break; } break; case 0x02: // DosBox RAW OPL File Mode NoteOn = false; if (! SampleCount) { // was done during Init (Emulation Core / Chip Mapper for real FM), // but Chip Mapper's init-routine now works different switch(DROInf.iHardwareType) { case 0x00: // OPL 2 for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); chip_reg_write(0x09, 0x00, 0x00, 0x08, 0x00); chip_reg_write(0x09, 0x00, 0x00, 0x01, 0x00); break; case 0x01: // Dual OPL 2 for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); chip_reg_write(0x09, 0x00, 0x00, 0x08, 0x00); chip_reg_write(0x09, 0x00, 0x00, 0x01, 0x00); //Sleep(1); for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x09, 0x01, 0x00, TempByt, 0x00); chip_reg_write(0x09, 0x01, 0x00, 0x08, 0x00); chip_reg_write(0x09, 0x01, 0x00, 0x01, 0x00); break; case 0x02: // OPL 3 for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x0C, 0x00, 0x00, TempByt, 0x00); chip_reg_write(0x0C, 0x00, 0x00, 0x08, 0x00); chip_reg_write(0x0C, 0x00, 0x00, 0x01, 0x00); //Sleep(1); for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x0C, 0x00, 0x01, TempByt, 0x00); //chip_reg_write(0x0C, 0x00, 0x01, 0x05, 0x00); chip_reg_write(0x0C, 0x00, 0x01, 0x04, 0x00); break; default: for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); break; } Sleep(1); NoteOn = true && (DROHead.iVersionMajor < 2); OpBase = 0x00; } SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); while(VGMSmplPos <= SmplPlayed) { Command = VGMData[VGMPos + 0x00]; if (Command == DROInf.iShortDelayCode) Command = 0x00; else if (Command == DROInf.iLongDelayCode) Command = 0x01; else { switch(DROHead.iVersionMajor) { case 0: case 1: if (Command <= 0x01) Command = 0xFF; break; case 2: Command = 0xFF; break; } } // DRO v0/v1 only: "Delay-Command" fix if (NoteOn) // "Delay"-Command during init-phase? { if (Command < OpBase) // out of operator range? { NoteOn = false; // it's a delay } else { OpBase = Command; // it's a command Command = 0xFF; } } DRO_CommandSwitch: switch(Command) { case 0x00: // 1-Byte Delay VGMSmplPos += 0x01 + VGMData[VGMPos + 0x01]; VGMPos += 0x02; break; case 0x01: // 2-Byte Delay switch(DROHead.iVersionMajor) { case 0: case 1: memcpy(&TempSht, &VGMData[VGMPos + 0x01], 0x02); if ((TempSht & 0xFF00) == 0xBD00) { Command = 0xFF; goto DRO_CommandSwitch; } VGMSmplPos += 0x01 + TempSht; VGMPos += 0x03; break; case 2: VGMSmplPos += (0x01 + VGMData[VGMPos + 0x01]) << 8; VGMPos += 0x02; break; } break; case 0x02: // Use 1st OPL Chip case 0x03: // Use 2nd OPL Chip CurChip = Command & 0x01; if (CurChip > 0x00 && DROInf.iHardwareType == 0x00) { //CurChip = 0x00; if (! CmdList[0x02]) { printf("More chips used than defined in header!\n"); CmdList[0x02] = true; } } VGMPos += 0x01; break; case 0x04: // Escape VGMPos += 0x01; // No break (execute following Register) default: Command = VGMData[VGMPos + 0x00]; if (DROCodemap) { CurChip = (Command & 0x80) ? 0x01 : 0x00; Command &= 0x7F; if (Command < DROInf.iCodemapLength) Command = DROCodemap[Command]; else Command = Command; switch(DROInf.iHardwareType) { case 0x00: if (CurChip) { if (! CmdList[0x02]) { printf("More chips used than defined in header!\n"); CmdList[0x02] = true; } //CurChip = 0x00; //Command = 0x00; } break; case 0x01: case 0x02: break; } } switch(DROInf.iHardwareType) { case 0x00: // OPL 2 if (CurChip > 0x00) break; chip_reg_write(0x09, 0x00, 0x00, Command, VGMData[VGMPos + 0x01]); break; case 0x01: chip_reg_write(0x09, CurChip, 0x00, Command, VGMData[VGMPos + 0x01]); break; case 0x02: // OPL 3 chip_reg_write(0x0C, 0x00, CurChip, Command, VGMData[VGMPos + 0x01]); break; default: chip_reg_write(0x09, CurChip, 0x00, Command, VGMData[VGMPos + 0x01]); break; } VGMPos += 0x02; break; } if (VGMPos >= VGMDataLen) { if (VGMHead.lngTotalSamples != (UINT32)VGMSmplPos) { printf("Warning! Header Samples: %u\t Counted Samples: %u\n", VGMHead.lngTotalSamples, VGMSmplPos); VGMHead.lngTotalSamples = VGMSmplPos; ErrorHappened = true; } VGMEnd = true; } if (VGMEnd) break; } break; } return; } INLINE INT32 SampleVGM2Playback(INT32 SampleVal) { return (INT32)((INT64)SampleVal * SampleRate / VGMSampleRate); } INLINE INT32 SamplePlayback2VGM(INT32 SampleVal) { return (INT32)((INT64)SampleVal * VGMSampleRate / SampleRate); } ================================================ FILE: VGMPlay/VGMPlay_Intf.h ================================================ // VGMPlay_Intf.h: VGMPlay Interface Header File // //#define NO_WCHAR_FILENAMES #ifndef WIN32 // Linux uses UTF-8 Unicode and has no special wide-character file routines. #define NO_WCHAR_FILENAMES #endif typedef struct waveform_16bit_stereo { INT16 Left; INT16 Right; } WAVE_16BS; typedef struct waveform_32bit_stereo { INT32 Left; INT32 Right; } WAVE_32BS; void VGMPlay_Init(void); void VGMPlay_Init2(void); void VGMPlay_Deinit(void); char* FindFile(const char* FileName); char* FindFile_List(const char** FileNameList); UINT32 GetGZFileLength(const char* FileName); bool OpenVGMFile(const char* FileName); void CloseVGMFile(void); void FreeGD3Tag(GD3_TAG* TagData); UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); UINT32 CalcSampleMSec(UINT64 Value, UINT8 Mode); UINT32 CalcSampleMSecExt(UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead); const char* GetChipName(UINT8 ChipID); const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType); UINT32 GetChipClock(VGM_HEADER* FileHead, UINT8 ChipID, UINT8* RetSubType); #ifndef NO_WCHAR_FILENAMES UINT32 GetGZFileLengthW(const wchar_t* FileName); bool OpenVGMFileW(const wchar_t* FileName); UINT32 GetVGMFileInfoW(const wchar_t* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); #endif INT32 SampleVGM2Playback(INT32 SampleVal); INT32 SamplePlayback2VGM(INT32 SampleVal); void PlayVGM(void); void StopVGM(void); void RestartVGM(void); void PauseVGM(bool Pause); void SeekVGM(bool Relative, INT32 PlayBkSamples); void RefreshMuting(void); void RefreshPanning(void); void RefreshPlaybackOptions(void); UINT32 FillBuffer(WAVE_16BS* Buffer, UINT32 BufferSize); ================================================ FILE: VGMPlay/VGMPlay_Updates.txt ================================================ Update list since 0.4.0u3 (03.12.2012) - replaced some larger swich statements with function pointers (small speed up) - fixed Stereo flag in SN76496 OPL mapper - optimized playback routine (huge speed up, >20% for single-chip VGMs) - writing to a second chip if only the first one is defined doesn't cause crashes anymore - shortened the Chip Reset, Chip Mute and Chip Panning routines - added OKIM6295, OKIM6258 and UPD7759 to DAC Stream Control - slight improvements on OPL Hardware Playback - fixed bug where [VGMPlay Linux] was unable to show Unicode characters - added multiple paths for VGMPlay.ini and OPL4 ROM - [VGMPlay Linux] is now checking for "vgmplay.ini" as well as "VGMPlay.ini" - cleaned the code a bit - [VGMPlay] now frees its memory properly when quitting 17.04.2013 and later: - improved skipping code for OPL hardware (to hopefully reduce noise after seeking) - made Makefile print short information instead of full gcc command lines - added possibility to override volume (+gain) of VGMs when using OPL hardware - [VGMPlay] made it use stderr for error messages - added optional debug output for DAC stream control (current data block, frequency) - fixed a bug with data blocks being re-read when restarting playback (wasted memory) - [VGMPlay] fixed bug when alternating between OPL hardware playback and emulation without restarting VGMPlay (e.g. via playlist with OPL and non-OPL VGMs) - fixed crash where the NES ROM could be written to invalid offsets - added alternative AY8910 core (EMU2149) - added NES DPCM address overflow behaviour, added alternative DPCM range (based on NSFPlay) - [VGMPlay] prevented it from writing too quickly to the OPL chip on fast computers 15.06.2013 and later: - [VGMPlay] console title now changes to "Song (Game) - VGM Player" while playing - [VGMPlay] made it fall back to Japanese tags if English ones are empty - [VGMPlay] made Console title use Unicode (yes, that works - not like the in-Console text) - [in_vgm] made ANSI GetFileInfo function fall back to Japanese tags if English ones are not present - [in_vgm] fixed Unicode -> ANSI filename conversion bug (caused Unicode Tag function to fail with some non-ANSI filenames) - [in_vgm] fixed File Info Dialog falling back to tags of the other language (it isn't supposed to do that) - [VGMPlay] some Codepage fixes for Windows version, hopefully it now behaves correctly in all cases. The Linux version should now be able to print non-ANSI Unicode characters in tags. - [VGMPlay Windows] now writes Unicode to the console (via WinAPI), so with the non-raster-fonts Japanese characters should work now. (although I only saw square boxes in WinXP, copying and pasting into Notepad worked at least) - [in_vgm] Fixed bug where "Chip Rate" == 0 didn't work and would get reset to 44100 when loading the plugin. - fixed bug where YM2612 Database Block caused DAC Stream Control to malfunction or crash (The MAME devs were right - realloc IS evil.) - [VGMPlay Linux] added support for libao 09.09.2013 and later: - added WonderSwan support - added better SN-PSG name display - made it work on Big Endian machines (Thanks to nextvolume for sending me a patch that helped to find the critical spots) - cleaned code that loads VGMs a bit - shortened the Chip Stop routine - applied MAME fixes to YM2413/YMF262 (thread safe now), K051649 (added test register, changed sound generation code a bit), K054539 (replaced sound generation code, might improve reverb), YMZ280B (minor stuff), YMF271 (FM code replaced, sounds different now!) - added NES APU database block type - added NES APU to 0x68 command (RAM write from database) and fixed its dual chip support - ported NES APU/DMC emulator from NSFPlay 2.2 - ported NES FDS emulator from NSFPlay 2.3 - fixed MAME NES emulator reset - fixed MAME NES (and maybe others?) unmuting all channels when restarting a song or seeking back - added EMU2149 to YM2203, YM2608 and YM2610, fixes wrong AY-PSG tones in some VGMs (bug is still present with MAME's AY core) - [VGMPlay] modified makefile to make it easier to compile VGMPlay using MSYS/MinGW - [in_vgm] fixed crash when trying to mute channels after opening Winamp, but before any song was played - [in_vgm] added options from VGMPlay.ini to in_vgm.ini - [in_vgm] fixed track length display bug after changing the playback rate - [VGMPlay Linux] fixed warnings that are printed from ym2612.c - added VRC7 mode for YM2413 (reducing features and using separate instrument set) - [in_vgm] made OK button of File Dialog return another error code to Winamp to prevent a playlist bug Updates since 0.4.0u4 (02.11.2013) - added code for muting channels to K053260 (looks like I forgot that one) and K054539 (lost when applying MAME fixes) - made K054539 use a proper clock (with a fallback for older VGMs) - added a small hack that allows mid-song sample rate changes for the OKIM6258 chip (I'll make a proper implementation for 0.41) - [VGMPlay] fixed absolute paths in m3u files - [VGMPlay] fixed path handling, makes paths that contain / and \ work in both Windows and Unix, fixes m3u Windows paths under Unix - fixed pitch of YM2608 ADPCM Tom Tom and Rim Shot (reported by MaliceX) - fixed YM2612 Timer A (there are still tiny rounding errors though), this fixed the CSM mode - fixed probable YM2612 bug (CSM mode was able to prevent KeyOn/Off commands on channels other than FM3) - fixed noise calculation in MAME's AY8910 (applied latest MAME update) - fixed a few crashes related to corrupted VGM headers with invalid offsets and insane clock values (though not all) - [VGMPlay] MuteWTCh# now works in VGMPlay.ini - fixed clicks in YMF278B core (introduced when porting it from openMSX) - [VGMPlay] fixed length display when VGMs play slower/faster after using the PlaybackRate option - fixed bug where VGZs with additional 00s at the end won't play (because VGMPlay will read an unpacked size of 0 bytes) - changed Endianess of SCSP RAM data to Big Endian (as it should be) - applied MAME QSound fixes (key on register, chip initialization) - removed last A from AY-3-8910A and variants - added an option to force OKIM6258 into 12-bit mode (makes it louder sometimes, but an internal 12-bit calculation seems to be more correct) - VGMs using the OKIM6258 or OKIM6295 are now half as loud (affects global volume only, not chip-relative volume) - [VGMPlay] fixed crash that happened on Linux when trying to display a large Notes tag (thanks to vampi-the-frog for the report) - OKIM6258 lowers its output signal faster if there's no incoming data (reduces clicks) Updates since 0.4.0u5 (13.04.2014) - fixed panning in AdLibEmu with 4-op instruments (it used the bits from Channel+0 instead of Channel+3) - fixed panning in AdLibEmu for rhythm channels - [VGMPlay] fixed YM2608/YM2610 DeltaT-channel not being muteable via MuteMask_PCM - [VGMPlay] fixed muting of YM278B's FM drums - applied MAME YM2612 Timer fix (said to fix SFX in Streets of Rage 2, see MAME Testers 05049) - fixed C140 sub chip type and C140/System 21 banking (using current MAME code) - added code to handle "real" C140 clock - [VGMPlay] fixed "make install" (the share/vgmplay/ folder was created without "execute" rights for users) - improved OKIM6258 data cache (should reduce clicks a bit) - YMF278B: fixed OPL3 register writes and OPL3 chip clock - applied MAME K053260 DPCM decoding fix - applied MAME OPLx Key Scale Level fix - added 40h-byte-FIFO to UPD7759 chip when in Slave mode, based on how Sega Pico games work - fixed T6W28 muting (Noise channel muting was controlled by chip #2, affected in_vgm only) Thanks to the developers of foo_input_vgm for noticing this bug. - added NMK112 banking to OKIM6295 emulation - added callback function for chip sample rate change -> replaces OKIM6258 hack -> fixes OPN SSG with MAME AY emulation -> makes OKIM6295 clock changes work - added routines for wide-character file names (only used by in_vgm for now) - fixed RAM write range check for RF5C68 and RF5C164 - made OKIM6258 FIFO queue larger, added RemoveDCOfs option - [VGMPlay] fixed tag display under Windows 95 - fixed YM2151 internal sample clipping (works like YM2612 now, can reduce distortion) - fixed SegaPCM volume register bitmask [thanks ctr for finding the bug] - fix potential crash in MultiPCM chip when playing invalid instruments - fixed buggy frequency writes in K051649 by backporting some MAME changes - added VSU, SAA1099 support - fixed high beep in NSFPlay NES core with triangle at frequency 0 and OPT_TRI_NULL option - [VGMPlay] fixed YM2612 DAC not being muteable via MuteMask - added ES5503, ES5506, Seta X1-010, Namco C352, Irem GA20 support - [VGMPlay] fixed Loop Length (is now adjusted for changed playback rates) Updates since 0.4.0u6 (30.05.2015) - fixed SAA1099 muting (muting channel 1 or 4 prevented the envelope generator from running, thus muting 0-2 or 3-5 respectively) - made SAA1099 output bipolar - [VGMPlay] fix compiling under Linux (broken since Wide-Charcter file name support) - fixed ES5503 sample rate (divider was off by one) - applied fix from VOGONS that makes the SAA1099 core actually use the chip clock - fixed YMF278B's FM part being emulated at wrong sample rate - fixed Irem GA20 channel muting - fixed playing non-NMK banked OKIM6295 VGMs after NMK-banked ones - fixed SN76489 PSG muting (muting didn't work if the frequency was set to 0 to play PCM) - [misc] added vgm2wav (thanks libertyernie) - improved DAC Stream Control for HuC6280 - [VGMPlay] added warning for zlib seeking bug - [VGMPlay + in_vgm] fixed bugs with .ini file reading - fixed YMF278B sample looping - repalced C352 sound core with new one written by superctr - added option to disable rarely used C352 rear channels - [in_vgm] added option to disable caching of VGM info (tags/etc.) - added option to enforce silence at the end of <1.50 VGMs - fixed memory leak when seeking back in songs that use the YM2610 (thanks to GTheGuardian for reporting) - fixed bug with X1-010 PCM loops Updates since 0.4.0u7 (04.05.2016) - fixed looping samples in YMF278B (was broken since 0.4.0u7) - fixed OKIM6258 ADPCM algorithm (thanks, XM6 source code) - "fixed" some Project2612 VGMs (worked around incorrect register order in the initialization block of Kega Fusion logs) - [VGMPlay] fixed YM2608/YM2610 "MutePCMCh" option not working - fixed bug with YM2612 "PseudoStereo" option - fixed YMF278B FM<->Wavetable volume balance, added code to emulate the "FM mix" register - fixed YM2151 to use 10 bits per channel instead of 9 (improves Toms in Sharp X1 Space Harrier), thanks MovieMovies1 - improved GameBoy emulation by porting current MAME core (ported from libvgm) - improved C352 emulation by porting ctr's updates from libvgm Updates since 0.4.0u8 (16.04.2017) - [in_vgm] fixed a crash and incorrect track times (thanks to NewRisingSun for the bug report) - fixed mid-sample frequency changes in DAC Stream Control (thanks NewRisingSun) - fixed SAA1099 sample rate and noise volume (thanks NewRisingSun) - [in_vgm] fixed being unable to mute 2nd MultiPCM chip (thanks to Alianger for the bug report) - added Nuked OPN2 core (YM2612 emulation) (thanks Nuke.YKT) - improved C352 emulation (thanks ctr) - added command for AY8910 panning (thanks NewRisingSun) - fixed YMF278B FM<->Wavetable volume balance again (thanks to ctr for a test VGM and thanks to l_oliveira for the hardware recording) - added (very basic) YMF278B RAM block support - slightly improved YMF271 emulation, thanks to GTheGuardian and kirishima - added new and accurate QSound sound core (thanks ctr) - slightly improved YMF278B emulation - swapped YM2612 emulator type IDs 1 and 2, so that Nuked OPN2 is selectable in in_vgm - [VGMPlay] added "OutputDevice" configuration option (thanks ctr for the "-DeviceId" commandline option) - fixed SN76496 variant being incorrectly called NCR7496 instead of NCR8496 - [in_vgm] made "Tag fallback" option work (finally, after 5 years!) - [in_vgm] made "disable file information cache" option selectable via configuration - [in_vgm] fix VGM information dialog using cached data even if file was changed Updates since 0.4.0u9 (24.12.2018) - improved VRC7 preset instruments (thanks Nuke.YKT) - [VGMPlay Linux] added support for DBus MPRIS and XDG integration (thanks Tasos Sahanidis) - [VGMPlay Windows] added support for Media Keys - added NukedOPLL (thanks Nuke.YKT) - updated EMU2413 core (thanks Mitsutaka Okazaki) - added NukedOPM (thanks Nuke.YKT) ================================================ FILE: VGMPlay/XMasFiles/SWJ-SQRC01_1C.h ================================================ // SWJ-SQRC01_1C.vgz UINT8 FF1ws_1C[0x1908] = { 0x1F, 0x8B, 0x08, 0x00, 0xD4, 0x7A, 0x2F, 0x52, 0x02, 0x00, 0xD4, 0x98, 0x57, 0xCC, 0x0E, 0x51, 0x10, 0x86, 0xC7, 0xFA, 0x7D, 0x7A, 0x0D, 0x42, 0x5C, 0x10, 0x12, 0x2E, 0x08, 0xA2, 0x25, 0x7A, 0xFB, 0x95, 0x04, 0x21, 0xD1, 0xE2, 0x42, 0x39, 0x21, 0x24, 0x12, 0xD1, 0xAD, 0x16, 0xBD, 0xF7, 0xDE, 0x7B, 0xEF, 0xBD, 0x1B, 0xDC, 0x6D, 0x24, 0x88, 0x1E, 0xA2, 0x04, 0x97, 0x6E, 0x48, 0x5C, 0x48, 0x44, 0x94, 0x67, 0x8F, 0xC3, 0xE2, 0x60, 0xD5, 0xE0, 0x4B, 0x26, 0xF1, 0xAD, 0x9D, 0x77, 0xDE, 0x77, 0x66, 0xCE, 0x9C, 0xF9, 0xFE, 0x2E, 0xFD, 0x06, 0x94, 0xEB, 0xDA, 0x4B, 0x64, 0x70, 0x0E, 0xF9, 0xF0, 0x69, 0xDA, 0x53, 0xE4, 0x71, 0xC9, 0x72, 0x72, 0xB5, 0x80, 0x48, 0xA5, 0x1E, 0x65, 0xE4, 0xF3, 0xCF, 0x1A, 0xF9, 0x87, 0x3E, 0x0F, 0xAB, 0x7E, 0xF2, 0x75, 0x90, 0xE6, 0x11, 0xCD, 0x27, 0x9A, 0x5F, 0x54, 0x44, 0x73, 0x88, 0x06, 0xA2, 0x39, 0x45, 0xB3, 0x44, 0x73, 0x89, 0x66, 0x44, 0x73, 0xF3, 0x46, 0x01, 0xD1, 0x82, 0xA2, 0x79, 0x45, 0x8B, 0x34, 0xD1, 0xE2, 0xA2, 0x45, 0x33, 0x83, 0xB4, 0x50, 0x29, 0x2D, 0x1C, 0x98, 0xD6, 0x41, 0x24, 0x13, 0xDE, 0x44, 0x32, 0x11, 0x9B, 0x84, 0x4D, 0xC6, 0xA6, 0x60, 0x53, 0xB1, 0x69, 0xD8, 0x74, 0x6C, 0x86, 0x44, 0x32, 0x13, 0x9B, 0x85, 0xCD, 0xC6, 0xE6, 0x60, 0x73, 0xB1, 0x79, 0xD8, 0x7C, 0x19, 0x1A, 0xC9, 0x02, 0x5E, 0x5A, 0x88, 0x2D, 0xC2, 0x16, 0x63, 0x4B, 0xB0, 0xA5, 0xD8, 0x32, 0x6C, 0x39, 0xB6, 0x82, 0x17, 0x57, 0x62, 0xAB, 0xB0, 0xD5, 0xD8, 0x1A, 0x6C, 0x2D, 0xB6, 0x0E, 0x5B, 0x2F, 0xC3, 0x22, 0xD9, 0xF0, 0x2A, 0x92, 0x8D, 0xCB, 0x22, 0xD9, 0x54, 0x3D, 0x92, 0xCD, 0x2F, 0x23, 0xD9, 0x32, 0x35, 0x92, 0xAD, 0xCD, 0x22, 0xD9, 0x56, 0x2C, 0x92, 0xED, 0x2D, 0x22, 0xD9, 0xD1, 0x27, 0x92, 0x9D, 0xE5, 0x23, 0xD9, 0x55, 0x3B, 0x92, 0xDD, 0xFB, 0x22, 0xD9, 0xC3, 0xFB, 0x7B, 0x4F, 0x44, 0xB2, 0x6F, 0x78, 0x24, 0xFB, 0x6B, 0x8C, 0x00, 0xA0, 0x39, 0x00, 0xE5, 0x01, 0x78, 0x03, 0xC0, 0x13, 0x00, 0xEA, 0x00, 0xB0, 0x06, 0x80, 0x0C, 0x00, 0x97, 0x01, 0x78, 0x03, 0x00, 0xB6, 0x0B, 0xDB, 0x1D, 0x00, 0xD0, 0x0F, 0x80, 0x0D, 0x00, 0x08, 0x00, 0xC5, 0x06, 0x6B, 0x56, 0x69, 0xCD, 0x95, 0x7B, 0x70, 0x24, 0x07, 0x00, 0x3E, 0x08, 0x91, 0x43, 0x10, 0x39, 0x0C, 0x91, 0x23, 0x10, 0x39, 0x0A, 0x91, 0x63, 0x10, 0x39, 0x0E, 0x91, 0x13, 0x10, 0x39, 0x49, 0x9C, 0x53, 0x10, 0x39, 0x0D, 0x11, 0xE5, 0xFD, 0x33, 0x10, 0x39, 0x0B, 0x91, 0x73, 0x35, 0x86, 0x03, 0xD0, 0x1C, 0x80, 0xF2, 0x00, 0xBC, 0x01, 0xE0, 0x09, 0x00, 0x75, 0x00, 0x58, 0x03, 0x40, 0x06, 0x80, 0xCB, 0x00, 0xBC, 0x01, 0x00, 0x3B, 0x85, 0x9D, 0x86, 0x88, 0xF6, 0x03, 0x60, 0x03, 0x00, 0x02, 0x00, 0x44, 0x32, 0xBD, 0x35, 0x77, 0xD6, 0x20, 0x2D, 0xD2, 0xD6, 0x54, 0x0D, 0x34, 0x6F, 0xCD, 0xA1, 0x9A, 0x0F, 0xCB, 0x5F, 0xD3, 0xDC, 0xC8, 0x68, 0xBE, 0xEC, 0x21, 0x9A, 0x3F, 0xDB, 0xCC, 0xCC, 0xD2, 0xBC, 0xD9, 0xA6, 0x6C, 0x5E, 0xCD, 0xDB, 0xD9, 0xDC, 0xE5, 0x69, 0x27, 0xFE, 0xBF, 0x85, 0xB9, 0x96, 0x51, 0x69, 0xA8, 0x39, 0x72, 0x8F, 0xD4, 0xE0, 0xB9, 0xE6, 0xCC, 0x0A, 0xB5, 0x48, 0x7B, 0x53, 0x23, 0xD0, 0x3C, 0xD9, 0x43, 0x41, 0x31, 0xF3, 0x63, 0x1F, 0xB0, 0xFA, 0x80, 0xD0, 0xD2, 0x34, 0xE2, 0x79, 0x67, 0x73, 0x2B, 0xA3, 0x79, 0xFA, 0x0E, 0x8D, 0x51, 0x96, 0x67, 0x69, 0xFE, 0xF2, 0xA6, 0x21, 0x4F, 0xC3, 0x21, 0x9A, 0xB7, 0xAF, 0x39, 0x50, 0x50, 0xA5, 0xC6, 0x08, 0x0D, 0xCE, 0xDB, 0x67, 0x44, 0x05, 0x61, 0x81, 0x8D, 0x9A, 0x6D, 0x3D, 0x6F, 0xC7, 0x9E, 0x43, 0x62, 0xCF, 0x0E, 0x41, 0xCC, 0x6D, 0x4A, 0x16, 0x9E, 0x43, 0x63, 0xCF, 0xFD, 0x78, 0x36, 0xC4, 0xF3, 0x39, 0x9E, 0x2E, 0xF6, 0x50, 0x18, 0x9A, 0xD9, 0x2E, 0x7E, 0xB6, 0x69, 0xFA, 0x69, 0xEC, 0x21, 0xE8, 0x33, 0x57, 0x33, 0xCE, 0x9F, 0x6F, 0xE5, 0x63, 0xF4, 0x7C, 0x45, 0xCD, 0xCD, 0x0C, 0x1C, 0x46, 0xC2, 0x81, 0x67, 0x12, 0x6A, 0xA6, 0xB2, 0xA9, 0x86, 0x67, 0x11, 0xA2, 0xE6, 0x30, 0x0F, 0x32, 0x71, 0x1E, 0x2E, 0xE1, 0x55, 0x8E, 0xEF, 0x81, 0x39, 0x5E, 0xF0, 0x7D, 0x06, 0x3A, 0x86, 0x1A, 0xB4, 0xD1, 0x9C, 0xB9, 0x4C, 0xBD, 0x4F, 0xA5, 0x8F, 0xF8, 0x92, 0xEC, 0xC5, 0x59, 0x04, 0xB2, 0x84, 0x12, 0xF2, 0xDD, 0x21, 0x3F, 0x04, 0xF2, 0x9F, 0x78, 0xC7, 0x14, 0x1C, 0xF1, 0x44, 0x3A, 0x04, 0x49, 0xD7, 0x7B, 0xBF, 0x81, 0xF8, 0x9D, 0xFA, 0xDC, 0xCF, 0xB4, 0xF8, 0xCC, 0xE7, 0x86, 0x13, 0x4A, 0x8A, 0xE3, 0x58, 0xC8, 0x1B, 0x32, 0x5A, 0x33, 0xCF, 0x34, 0x77, 0xCE, 0x77, 0xEA, 0x86, 0xA2, 0x0E, 0xAA, 0xD6, 0x1D, 0x85, 0x25, 0xF2, 0xBE, 0x57, 0xB8, 0x22, 0x0B, 0x85, 0xD4, 0x1C, 0x19, 0xE8, 0x24, 0x5C, 0x18, 0x87, 0xAB, 0xEF, 0x55, 0xC7, 0x0F, 0x97, 0x48, 0xEB, 0x0B, 0xC5, 0xE5, 0x3E, 0x45, 0x97, 0x14, 0x5F, 0xD6, 0xC1, 0x82, 0x2E, 0x0E, 0x14, 0x8F, 0x98, 0xEA, 0xEF, 0xB3, 0x1F, 0xC6, 0xFC, 0xE6, 0x59, 0x7E, 0x25, 0x3F, 0xF0, 0x5B, 0x6E, 0xF9, 0x95, 0xB3, 0xFC, 0xA8, 0xA6, 0x84, 0x48, 0xBB, 0x93, 0x14, 0xC1, 0x8F, 0x35, 0xD4, 0xF1, 0xFB, 0xA8, 0xE3, 0xC6, 0xC3, 0xEF, 0x05, 0xFC, 0x7E, 0x44, 0xD3, 0x34, 0x7C, 0x4A, 0x6A, 0xCE, 0x0C, 0x6E, 0xDF, 0x1B, 0xEA, 0xA0, 0x0B, 0x75, 0x7F, 0xA4, 0x66, 0x9D, 0xD2, 0x5C, 0xB9, 0x46, 0x68, 0x26, 0x5B, 0x73, 0x07, 0xA6, 0x8A, 0x13, 0x98, 0x1C, 0xB9, 0x19, 0xAE, 0xF6, 0x58, 0x22, 0xD6, 0xB6, 0x6C, 0x27, 0xB3, 0x24, 0x16, 0x4C, 0x4F, 0x80, 0x18, 0x1F, 0x2B, 0x0E, 0xE2, 0xFB, 0xD2, 0xAC, 0x07, 0x7B, 0x0C, 0xA5, 0x49, 0xE7, 0x93, 0xC8, 0x58, 0x89, 0x4F, 0x9F, 0x71, 0x9A, 0x59, 0xC6, 0xA1, 0xC7, 0xCF, 0xB1, 0x28, 0x9F, 0xB0, 0x18, 0x01, 0x03, 0x34, 0xC6, 0x18, 0xED, 0x03, 0xBA, 0x96, 0x21, 0xE0, 0x12, 0xE2, 0x5A, 0xA3, 0x41, 0xE0, 0xF0, 0xEC, 0x48, 0x90, 0xA1, 0x31, 0x9B, 0x2B, 0xB0, 0x99, 0x3B, 0x12, 0x36, 0x23, 0x34, 0xAB, 0x86, 0xE6, 0xCA, 0x98, 0x5A, 0x8E, 0x54, 0x0E, 0x4B, 0x19, 0x62, 0x08, 0x41, 0x82, 0x03, 0xCD, 0x26, 0x94, 0xA5, 0x47, 0x3D, 0xDB, 0x04, 0x24, 0xC1, 0x06, 0x09, 0x47, 0x24, 0x67, 0x32, 0xF6, 0x10, 0xD3, 0x2D, 0x78, 0x37, 0xA4, 0xDE, 0xB5, 0xC4, 0x08, 0x97, 0x2D, 0xD7, 0xB2, 0xE8, 0x72, 0x87, 0xBC, 0x9C, 0x59, 0xFD, 0x59, 0xD3, 0xAE, 0x0C, 0x51, 0xE9, 0x37, 0x85, 0x23, 0x00, 0x19, 0x08, 0xF8, 0xF9, 0xE9, 0x9F, 0x1F, 0xB2, 0xBC, 0xE5, 0x32, 0xEB, 0x55, 0x3A, 0xFB, 0x9B, 0xE7, 0x72, 0x17, 0x3E, 0x5B, 0x4D, 0xA3, 0xEF, 0x6E, 0xAA, 0x93, 0x05, 0xDF, 0xE5, 0xAB, 0x24, 0x6D, 0x51, 0x99, 0x9E, 0x70, 0x87, 0x72, 0xC8, 0xD7, 0x54, 0xFA, 0xC7, 0xF3, 0x3A, 0x4A, 0xB7, 0xA2, 0x74, 0xA1, 0x55, 0x1A, 0xDA, 0xA8, 0x73, 0x9C, 0xAE, 0x6C, 0xFE, 0x45, 0xE4, 0x11, 0x68, 0x25, 0xB9, 0x70, 0x8A, 0xA3, 0xDB, 0x67, 0x35, 0x4D, 0xDB, 0x00, 0xB6, 0x7C, 0xE7, 0x3D, 0x18, 0xA3, 0xD2, 0xF9, 0xCE, 0x75, 0x95, 0x4E, 0x7C, 0xFB, 0xBC, 0xF3, 0x05, 0xCF, 0xF9, 0x52, 0x2E, 0xFC, 0xAC, 0x2A, 0x7A, 0x46, 0xF6, 0xA3, 0xF9, 0x24, 0x08, 0x56, 0x93, 0x1F, 0x3D, 0xD6, 0xE0, 0x7B, 0x93, 0x61, 0xBC, 0xC7, 0x12, 0x1B, 0xF5, 0x2F, 0x38, 0x17, 0xA1, 0x1B, 0x4C, 0x55, 0x9C, 0xCA, 0x1C, 0xF6, 0x5C, 0x24, 0x87, 0x7F, 0x1A, 0xBE, 0x45, 0x5C, 0xE3, 0x64, 0x07, 0xC9, 0x10, 0x68, 0x86, 0x8E, 0x13, 0x74, 0xDD, 0x7D, 0xF4, 0x7B, 0x1A, 0xC2, 0x38, 0x43, 0x53, 0x2C, 0x8B, 0x05, 0x5F, 0x67, 0x31, 0x44, 0xE5, 0x30, 0x1A, 0x2E, 0xD8, 0xAB, 0xA3, 0xEE, 0x8F, 0xE9, 0xB0, 0xAD, 0xDD, 0xD4, 0x69, 0xB9, 0x8F, 0x96, 0x3B, 0x08, 0x39, 0xF2, 0x53, 0x2A, 0x56, 0xA2, 0x82, 0x7E, 0xF5, 0xE3, 0xDB, 0xDB, 0x64, 0xAA, 0xC7, 0xC1, 0x57, 0x71, 0x22, 0x9E, 0x33, 0x56, 0xC5, 0xD7, 0x7A, 0xC1, 0xE5, 0x61, 0xC8, 0x57, 0x55, 0x8C, 0x50, 0xB9, 0x34, 0x92, 0xED, 0xEA, 0x09, 0xDB, 0xD5, 0x7D, 0xB6, 0xAB, 0x8B, 0x6C, 0x57, 0xA7, 0xD9, 0xAE, 0x76, 0xB1, 0x5D, 0xAD, 0x64, 0xBB, 0x9A, 0xC1, 0x76, 0x15, 0xB2, 0x5D, 0xF5, 0x65, 0xBB, 0xEA, 0xCC, 0x76, 0x95, 0xCD, 0x76, 0x55, 0x93, 0xED, 0xAA, 0x3C, 0xDB, 0x55, 0xD1, 0xC1, 0x94, 0x73, 0x94, 0x3D, 0xFC, 0xB9, 0x47, 0xB3, 0x9A, 0x5C, 0x60, 0x35, 0xB9, 0xC7, 0x6A, 0xF2, 0x9A, 0xD5, 0xE4, 0x0D, 0xAB, 0x09, 0x76, 0xF4, 0x29, 0xAB, 0xC9, 0x25, 0x56, 0x13, 0x65, 0x35, 0xA9, 0xC5, 0x6A, 0x52, 0x81, 0xD5, 0x24, 0x07, 0xAB, 0x89, 0x44, 0xA2, 0xD8, 0x99, 0x22, 0xAC, 0x26, 0x35, 0x58, 0x4D, 0x9A, 0xB3, 0x9A, 0x9C, 0x8F, 0x4F, 0x7D, 0x59, 0xA7, 0x64, 0x25, 0x1C, 0xB1, 0xFC, 0x2B, 0x4D, 0x13, 0x18, 0xCF, 0xA0, 0xB2, 0x33, 0x98, 0x42, 0x68, 0x0B, 0x35, 0xDF, 0x0C, 0x9B, 0xCB, 0x90, 0x3C, 0x85, 0x6E, 0x6A, 0xB5, 0x8B, 0x55, 0xF1, 0x4E, 0x5F, 0x37, 0x19, 0xDF, 0xCD, 0xD1, 0x10, 0xFF, 0xCE, 0x64, 0x12, 0xDD, 0xAE, 0x3B, 0xB2, 0xE9, 0x0E, 0x54, 0xBB, 0xC5, 0x81, 0x9E, 0xA1, 0x52, 0x31, 0x16, 0x4F, 0x82, 0x85, 0xE0, 0x95, 0x37, 0x75, 0xF0, 0x97, 0xA1, 0xC4, 0xC7, 0xBF, 0xB3, 0xC9, 0x76, 0xB1, 0x8B, 0x32, 0x3B, 0xDE, 0x45, 0x14, 0xD3, 0xC2, 0x55, 0x93, 0x88, 0xAC, 0xB7, 0x44, 0xFA, 0xB0, 0xFE, 0xC4, 0xD9, 0x77, 0xB3, 0xD5, 0x34, 0x0F, 0x58, 0x7A, 0xC9, 0xEA, 0x24, 0xB2, 0x3A, 0x99, 0xAC, 0x4E, 0x21, 0xAB, 0x53, 0xC9, 0xEA, 0x34, 0xB2, 0x3A, 0x9D, 0xAC, 0xCE, 0x08, 0x59, 0x7A, 0xC9, 0xEA, 0x2C, 0xB2, 0x3A, 0x9B, 0xAC, 0xCE, 0x21, 0xAB, 0x73, 0xC9, 0xEA, 0x3C, 0xB2, 0x2A, 0x0B, 0x35, 0x47, 0x66, 0xC8, 0x3B, 0x56, 0x77, 0x58, 0x37, 0xAC, 0xF0, 0xC0, 0x91, 0x2A, 0x0A, 0x01, 0x48, 0xD9, 0xA0, 0xA1, 0x3D, 0x94, 0x39, 0x48, 0x89, 0xF8, 0x84, 0x28, 0x3D, 0x64, 0xE2, 0x27, 0x72, 0x47, 0x73, 0xE4, 0x72, 0x78, 0x5B, 0xED, 0x75, 0xF6, 0x1E, 0xCF, 0xB4, 0x4E, 0xB0, 0xC0, 0x05, 0xCB, 0xB4, 0x49, 0x70, 0xDC, 0x38, 0x9B, 0xFF, 0x01, 0x67, 0xEB, 0xA7, 0xBC, 0x12, 0x1C, 0x24, 0x0B, 0xC9, 0x94, 0xC1, 0xF1, 0x5E, 0xD8, 0xD8, 0x62, 0x2E, 0x70, 0xFC, 0x52, 0x30, 0x2D, 0x37, 0x20, 0xE8, 0xE7, 0xA0, 0x0F, 0xDC, 0xEC, 0x30, 0x0D, 0x86, 0x38, 0x6E, 0xA9, 0x38, 0x89, 0xC6, 0x3E, 0x09, 0xB7, 0x85, 0x10, 0xFB, 0x3A, 0x48, 0x5B, 0x0B, 0xD2, 0xEA, 0x8B, 0x20, 0x0B, 0xBF, 0xA8, 0x8E, 0x97, 0x3D, 0x90, 0xEF, 0xCF, 0xF6, 0xCF, 0xE1, 0xF8, 0xD9, 0xFE, 0xF1, 0xAA, 0xF9, 0x7C, 0x2E, 0xFC, 0x4A, 0xF5, 0x2F, 0xC0, 0xC7, 0x56, 0xCA, 0xE3, 0x93, 0x52, 0xA9, 0xB4, 0xFC, 0xFC, 0x64, 0xC5, 0xB7, 0xFE, 0x07, 0x15, 0x77, 0x19, 0xFE, 0x7B, 0x15, 0xEF, 0xE3, 0xF1, 0xF9, 0x81, 0xB3, 0xD9, 0x27, 0xA9, 0xF8, 0xC2, 0x9F, 0x3E, 0x98, 0x7E, 0x86, 0x87, 0xFC, 0xB9, 0x9E, 0xF1, 0xCB, 0xED, 0x67, 0xF8, 0x37, 0x57, 0xFC, 0x42, 0x6A, 0xC5, 0x53, 0xCE, 0xD4, 0xEF, 0xAA, 0xF8, 0xD0, 0x24, 0x3F, 0xA3, 0x35, 0xEB, 0xBC, 0xE6, 0xCA, 0xD2, 0x02, 0x32, 0x8A, 0xF5, 0x84, 0x3D, 0x8B, 0x3F, 0xC7, 0xF0, 0xAB, 0xFF, 0xB2, 0xA9, 0xF8, 0xD1, 0xC4, 0x0E, 0x99, 0xD8, 0xBB, 0x58, 0x03, 0x88, 0xC3, 0xC5, 0x66, 0x71, 0xB8, 0xE8, 0xF8, 0xFE, 0x9E, 0x39, 0xCB, 0x20, 0x6B, 0x5F, 0x12, 0xD5, 0x5E, 0x7F, 0x33, 0xDD, 0xCF, 0x16, 0x16, 0x0A, 0x1B, 0xBD, 0xED, 0xE7, 0x27, 0xB1, 0x9E, 0x2D, 0x8D, 0x0B, 0x81, 0x7B, 0x92, 0x56, 0x0F, 0x34, 0x91, 0xC2, 0xBB, 0x0E, 0xD0, 0xEB, 0x99, 0xF1, 0x83, 0x34, 0xF3, 0x48, 0x73, 0xE7, 0x46, 0x02, 0x7B, 0x56, 0x82, 0x5D, 0xCE, 0xEE, 0x59, 0x76, 0x3B, 0x70, 0xC4, 0xBE, 0xD1, 0x02, 0xBC, 0x5F, 0x04, 0xFC, 0xD4, 0x03, 0x3B, 0xE4, 0x8B, 0x9C, 0xC3, 0xCF, 0x39, 0xCF, 0xF7, 0x38, 0x7B, 0xAD, 0xC5, 0xEF, 0xAD, 0xBE, 0x31, 0xE5, 0xAA, 0x1F, 0x43, 0x7B, 0x77, 0x77, 0x0A, 0x74, 0xCA, 0x79, 0x4E, 0x52, 0x9C, 0x74, 0xCA, 0xD7, 0x52, 0x9C, 0xD4, 0xCC, 0xBF, 0x2F, 0xD9, 0x63, 0x97, 0xB3, 0x93, 0x8F, 0x7C, 0xD7, 0x2B, 0x83, 0x6C, 0xA6, 0x3F, 0x6B, 0x94, 0xD3, 0x1F, 0x1A, 0x25, 0xDB, 0x35, 0x0A, 0x99, 0x77, 0x01, 0x53, 0x1B, 0x25, 0xA9, 0xEB, 0xC7, 0xF7, 0x47, 0xA3, 0x8F, 0x8B, 0x89, 0x7B, 0xCA, 0x3C, 0x48, 0x2B, 0x62, 0xA8, 0x59, 0x6D, 0x50, 0xE0, 0xB7, 0x8A, 0x5D, 0x04, 0xA1, 0x6D, 0x5B, 0x65, 0x78, 0x4C, 0x6D, 0xD6, 0x8F, 0xB5, 0xA3, 0x3F, 0x35, 0x52, 0x98, 0xFF, 0x40, 0xFB, 0x5D, 0x78, 0xC7, 0xBC, 0x7C, 0x48, 0xBB, 0x7C, 0x9A, 0xF8, 0x0F, 0x0B, 0xAC, 0xE5, 0x6D, 0x3A, 0x04, 0xEF, 0x35, 0x84, 0xF6, 0xB4, 0x36, 0xFC, 0xB4, 0x7B, 0xEC, 0xF2, 0xDA, 0xD4, 0x46, 0x6C, 0xEF, 0xCA, 0xE3, 0x74, 0x80, 0x21, 0x66, 0x46, 0xEA, 0xC4, 0x21, 0x1E, 0x5A, 0x5A, 0xA4, 0xDC, 0x57, 0xA9, 0x55, 0x18, 0xF9, 0x6E, 0xF2, 0x84, 0x1F, 0xB5, 0x52, 0xE5, 0x3F, 0xD2, 0x4A, 0x5B, 0x3F, 0xBA, 0x9B, 0x9C, 0x88, 0x1F, 0x98, 0x39, 0x29, 0xBD, 0x39, 0x7E, 0xF0, 0x57, 0x66, 0x8E, 0xD7, 0x48, 0x3F, 0xDD, 0xAE, 0x70, 0x4E, 0x9F, 0x93, 0x2D, 0xBC, 0x46, 0xFA, 0xBE, 0x99, 0xF3, 0x27, 0x7B, 0xC8, 0x9F, 0x47, 0xBF, 0x3E, 0x3E, 0xFD, 0x79, 0xF4, 0x96, 0x57, 0xAB, 0xC7, 0x69, 0x23, 0x0C, 0xA2, 0x52, 0xA2, 0xF4, 0x7B, 0x04, 0x8B, 0x2A, 0x0A, 0x22, 0x48, 0xA6, 0x4B, 0x88, 0x44, 0xA4, 0xB5, 0x82, 0x65, 0x8A, 0x48, 0xC8, 0x27, 0x88, 0xB4, 0x6D, 0xD0, 0x6E, 0xC2, 0x1A, 0x28, 0x68, 0x28, 0x42, 0x41, 0x91, 0x32, 0x07, 0xC8, 0x2D, 0x72, 0x03, 0xCE, 0x92, 0x26, 0x27, 0xC8, 0x78, 0x3C, 0x9F, 0x9F, 0x9F, 0x9F, 0xF9, 0x3E, 0x81, 0xA3, 0x34, 0xC8, 0x08, 0xF6, 0x9B, 0xBF, 0x37, 0x6F, 0xDE, 0x37, 0xEB, 0x3F, 0xBF, 0x5E, 0x3C, 0x0F, 0x3E, 0x9A, 0x47, 0xB4, 0x47, 0x11, 0xFD, 0x33, 0x3A, 0xD2, 0x46, 0x40, 0x53, 0x17, 0xC7, 0x6F, 0xAB, 0xF5, 0x0D, 0x08, 0x45, 0x23, 0x7C, 0xB2, 0x46, 0x98, 0x49, 0x07, 0xB8, 0xF7, 0x47, 0xF0, 0xFE, 0x91, 0x53, 0xF7, 0x4E, 0x8B, 0x8F, 0x39, 0xD9, 0xE4, 0xE7, 0xA4, 0x95, 0x35, 0x4A, 0xCC, 0xA8, 0xED, 0xCD, 0x85, 0xAF, 0xF3, 0xE7, 0xDE, 0xCC, 0x0D, 0xF6, 0xBE, 0x17, 0x4C, 0x08, 0x6E, 0xE7, 0x4F, 0xDC, 0xE6, 0xA8, 0xCB, 0x26, 0x47, 0x67, 0x07, 0x7C, 0x41, 0xB5, 0x60, 0x9E, 0xE2, 0xF5, 0x0B, 0xF8, 0x07, 0x8A, 0x97, 0x80, 0x7E, 0xB5, 0x08, 0xE0, 0x6C, 0xB3, 0x27, 0x53, 0xF2, 0x44, 0x88, 0xEC, 0x2A, 0x62, 0x40, 0xB2, 0x5B, 0x8E, 0xFD, 0xA3, 0xC7, 0x6E, 0x9F, 0x99, 0xDA, 0xCE, 0xD3, 0x73, 0x6F, 0x3D, 0xD9, 0x14, 0x7B, 0x67, 0x4F, 0x04, 0xEC, 0xC7, 0xCB, 0x17, 0x59, 0x67, 0x4F, 0x7D, 0x93, 0x75, 0x66, 0xAF, 0x1C, 0xEC, 0x15, 0x8A, 0x63, 0x84, 0xC8, 0xB2, 0x9D, 0xBB, 0x80, 0xEC, 0xBD, 0x8C, 0x95, 0x90, 0x60, 0x3D, 0x96, 0xB4, 0xCD, 0x25, 0xF0, 0xEE, 0xAB, 0x46, 0xF4, 0x97, 0xFF, 0x5E, 0x77, 0x81, 0x9A, 0x58, 0x7D, 0x44, 0xFA, 0x7C, 0xAD, 0x67, 0x96, 0x23, 0x7D, 0x3D, 0x5D, 0x9D, 0x7A, 0x26, 0x4F, 0x3F, 0xA7, 0x31, 0x70, 0x24, 0xF8, 0xDB, 0x79, 0x6C, 0x03, 0x4B, 0xA9, 0xCE, 0x91, 0x49, 0xE7, 0x8F, 0xED, 0xFC, 0xCF, 0x0C, 0x4D, 0x54, 0xF6, 0x95, 0x3F, 0x1E, 0xD8, 0x6C, 0xE7, 0xE4, 0xF4, 0x7E, 0x89, 0xCD, 0xDA, 0x3F, 0x83, 0x9C, 0xA6, 0x09, 0x9F, 0x5E, 0x39, 0x10, 0x93, 0xA7, 0x21, 0x0C, 0x57, 0x44, 0x4E, 0x20, 0x58, 0x33, 0x8A, 0x19, 0xB4, 0xEF, 0x6B, 0xA5, 0x72, 0x07, 0x9A, 0x39, 0xEE, 0xBE, 0xDE, 0x0F, 0x67, 0xBE, 0x32, 0x0C, 0x23, 0x36, 0xFF, 0x67, 0xE6, 0x70, 0x1C, 0x54, 0xE2, 0xEF, 0xE1, 0xCA, 0xE6, 0xC3, 0x3C, 0x20, 0x06, 0x8D, 0x55, 0x1E, 0x0E, 0xF3, 0x5D, 0x7A, 0x07, 0x12, 0xF2, 0x3A, 0xD8, 0xDF, 0xE2, 0x58, 0x8B, 0x28, 0x16, 0xBD, 0xA8, 0x30, 0xCB, 0x3B, 0xF0, 0xC3, 0x1E, 0x41, 0x8C, 0x6B, 0x10, 0x20, 0x6B, 0x53, 0x1D, 0x46, 0x6E, 0xFC, 0x28, 0x8C, 0xC7, 0x90, 0x80, 0x03, 0x97, 0x3A, 0x28, 0xDE, 0xB9, 0x23, 0x93, 0xA5, 0x23, 0xD7, 0x2B, 0x50, 0xE8, 0x17, 0x90, 0x9F, 0x91, 0x3E, 0x80, 0x33, 0x3B, 0x9B, 0xB8, 0x1D, 0x0E, 0x59, 0x4E, 0x28, 0x07, 0xE7, 0xC9, 0x5C, 0x3D, 0x8F, 0xDB, 0xE1, 0xE0, 0x95, 0x24, 0xF3, 0x3D, 0xCB, 0x93, 0x89, 0xA7, 0x02, 0x76, 0xA1, 0x4B, 0x32, 0x76, 0x4F, 0x50, 0x81, 0x36, 0x67, 0x7D, 0x2A, 0xD6, 0x1B, 0x5E, 0x99, 0xC0, 0xE4, 0x40, 0xCA, 0xEC, 0xC1, 0xB5, 0x5C, 0x60, 0x1C, 0x5D, 0xC5, 0xD1, 0x28, 0xF0, 0xE9, 0x1A, 0x11, 0xCF, 0x68, 0x00, 0x10, 0xDE, 0x1D, 0x1D, 0x5C, 0xE0, 0x2E, 0x15, 0xB8, 0xE6, 0xD8, 0xD0, 0x6C, 0xDE, 0xF9, 0xF5, 0x45, 0x74, 0x47, 0x90, 0x30, 0x0A, 0x0C, 0x47, 0x8E, 0xB9, 0xD7, 0x83, 0xC9, 0xD8, 0x8B, 0x81, 0xCE, 0x01, 0x34, 0x32, 0x2A, 0x9B, 0xDA, 0xA5, 0x77, 0x3B, 0x84, 0xF0, 0x0A, 0x08, 0x0F, 0x8E, 0x19, 0x92, 0xFC, 0x77, 0x60, 0xC5, 0x5B, 0x28, 0xED, 0xA4, 0x69, 0x87, 0x3A, 0x82, 0xC7, 0x9A, 0x8E, 0xF5, 0x88, 0xBF, 0xAE, 0x45, 0xF5, 0x82, 0x21, 0xEB, 0x67, 0x72, 0x7F, 0xF3, 0xB3, 0xCA, 0xBA, 0x66, 0xF3, 0x78, 0x3E, 0x88, 0xF3, 0x06, 0xD2, 0x7A, 0xCE, 0xEA, 0x69, 0xEC, 0xA9, 0x22, 0xB3, 0xCF, 0x42, 0x7A, 0xA8, 0x00, 0x94, 0x25, 0x18, 0x56, 0x53, 0x30, 0xD1, 0x14, 0x40, 0xD1, 0x68, 0x0A, 0x10, 0x80, 0x79, 0x6E, 0xD9, 0x15, 0x4C, 0x09, 0x71, 0xD7, 0x30, 0x40, 0xC4, 0xAD, 0x38, 0xBA, 0x0D, 0x36, 0x85, 0x94, 0xCC, 0xB0, 0x69, 0xF9, 0x9A, 0x76, 0x9A, 0xD7, 0x8F, 0x35, 0xF1, 0xBF, 0x22, 0x91, 0x13, 0x34, 0xE2, 0x04, 0x9D, 0xA7, 0x7C, 0x83, 0x6B, 0x0D, 0x01, 0x92, 0xA4, 0x04, 0x15, 0xBE, 0x16, 0xDA, 0xCF, 0xEC, 0x25, 0x0A, 0x20, 0x0E, 0xD1, 0xA8, 0x50, 0xE9, 0x33, 0x50, 0x31, 0x4B, 0x8F, 0x96, 0xF0, 0xCD, 0xBA, 0xF4, 0xAD, 0x99, 0x96, 0x82, 0x8C, 0xE0, 0xBC, 0x42, 0xA5, 0x12, 0xA8, 0x90, 0x2A, 0xE4, 0x7D, 0xC2, 0xF0, 0x99, 0x12, 0xAD, 0x0E, 0xDF, 0xA2, 0x70, 0xE7, 0xC6, 0x57, 0x71, 0xF1, 0x3A, 0xCC, 0x0C, 0x80, 0xCF, 0x85, 0xE7, 0x97, 0x79, 0x71, 0xE1, 0x19, 0x68, 0x59, 0x60, 0x1C, 0x74, 0x49, 0x02, 0x27, 0x2D, 0xB5, 0xD3, 0x01, 0x6A, 0xC2, 0x3C, 0x33, 0xE1, 0xBD, 0x8D, 0x68, 0x3B, 0x74, 0x6F, 0x58, 0x03, 0x5B, 0xA0, 0x84, 0xB8, 0xBA, 0x4D, 0x0C, 0x78, 0xE3, 0x0A, 0x2B, 0xA1, 0xEE, 0x1B, 0xF9, 0x70, 0xCC, 0x0A, 0x6E, 0x8D, 0xF3, 0x46, 0x50, 0x70, 0xAC, 0x12, 0x55, 0xC1, 0xB5, 0x7A, 0xFE, 0x18, 0x31, 0x0A, 0x7B, 0xED, 0x82, 0x61, 0x95, 0xC1, 0x16, 0x99, 0x5D, 0x8A, 0x87, 0xDB, 0xC4, 0x62, 0xC1, 0xF4, 0x0A, 0xCC, 0x2A, 0x24, 0x03, 0x21, 0x49, 0x44, 0x5C, 0xB4, 0x42, 0xF1, 0xFE, 0x77, 0x52, 0x86, 0x10, 0x2C, 0xA8, 0x7E, 0x63, 0x72, 0x57, 0xC6, 0x51, 0x1C, 0x82, 0xBA, 0xDC, 0x03, 0xD5, 0xD0, 0x27, 0xD9, 0x2A, 0xDB, 0x61, 0xC5, 0x0A, 0x43, 0xC1, 0xF5, 0x2B, 0x48, 0xDF, 0x23, 0x8C, 0x95, 0xD1, 0x0E, 0x05, 0x5F, 0x46, 0xFC, 0x34, 0x83, 0xF8, 0xC2, 0x8C, 0x2F, 0x63, 0xDD, 0x33, 0xC2, 0x38, 0xE7, 0x9B, 0x4A, 0xB5, 0x96, 0x85, 0x66, 0x6D, 0xC9, 0x77, 0xC1, 0xA2, 0x91, 0x5B, 0x6C, 0xCB, 0x4B, 0x12, 0x4C, 0x4B, 0xD0, 0xE8, 0x32, 0x6C, 0xCA, 0xE8, 0x9E, 0x24, 0xD1, 0xC9, 0x1D, 0xA9, 0xC2, 0x1D, 0x89, 0xEA, 0x7B, 0xBF, 0x50, 0xA6, 0x3B, 0xA8, 0xAF, 0xA8, 0x53, 0xEF, 0x32, 0xD4, 0x17, 0x03, 0x40, 0xEB, 0x8B, 0x7E, 0xAB, 0x3B, 0xAD, 0xAF, 0x43, 0xB8, 0xF3, 0xC0, 0xC9, 0x97, 0x21, 0xAF, 0xBF, 0xA2, 0xBE, 0xEC, 0xC7, 0xA0, 0xD5, 0xDB, 0xA8, 0xD6, 0x77, 0xB2, 0x29, 0xC9, 0x0F, 0x64, 0x61, 0xAC, 0xFA, 0x1C, 0x2A, 0x8E, 0x97, 0x63, 0xC8, 0x6F, 0x58, 0x6A, 0x10, 0xB1, 0xB3, 0x5D, 0xDF, 0xAA, 0x30, 0x80, 0xBA, 0xF8, 0xBE, 0xF5, 0x45, 0x0D, 0xD9, 0x01, 0x17, 0xEE, 0x93, 0x92, 0x3B, 0x02, 0xF3, 0x5E, 0x68, 0x07, 0x9A, 0xEF, 0xF9, 0xEA, 0xB4, 0xAB, 0x8C, 0x78, 0x43, 0x5C, 0x7C, 0xBD, 0xAA, 0xEF, 0x0E, 0x49, 0xE2, 0x8E, 0x68, 0x5C, 0x17, 0x24, 0x6E, 0x51, 0xDF, 0x6D, 0x21, 0x37, 0xA0, 0x4F, 0x21, 0x37, 0xA8, 0xC6, 0x4A, 0x94, 0xDB, 0x2F, 0x1B, 0xF8, 0x92, 0xF5, 0x13, 0xCA, 0x8F, 0xDE, 0x64, 0xF4, 0xBC, 0x7C, 0xCB, 0x03, 0x98, 0x60, 0x25, 0xC3, 0x18, 0x1E, 0x10, 0x53, 0xF4, 0xC2, 0x14, 0x89, 0x13, 0x79, 0x1A, 0x48, 0x9F, 0x8C, 0x64, 0x12, 0x28, 0x07, 0xA2, 0xFE, 0xF7, 0x22, 0x12, 0x3D, 0xC4, 0x27, 0xCD, 0x82, 0x11, 0xA3, 0x31, 0xBE, 0x71, 0x0B, 0x34, 0x3E, 0x3C, 0x0B, 0x7E, 0x3C, 0x48, 0x8B, 0x10, 0x1F, 0x25, 0x5A, 0xCC, 0x10, 0xFE, 0x5D, 0x1C, 0xFF, 0x7B, 0xB3, 0x94, 0xDC, 0xFD, 0x5F, 0xFB, 0x31, 0x95, 0x98, 0x42, 0x88, 0x25, 0x62, 0x46, 0xA1, 0x8B, 0xA4, 0x5C, 0x31, 0x29, 0xA3, 0xED, 0xFF, 0xF6, 0x72, 0x25, 0xAF, 0x3F, 0x45, 0x51, 0xFC, 0x9A, 0x43, 0x8A, 0x94, 0x4C, 0xE5, 0x87, 0x42, 0x64, 0x9E, 0xE7, 0x79, 0x26, 0x22, 0xAC, 0x2C, 0x9E, 0x29, 0x4A, 0x86, 0x2F, 0xBE, 0xCF, 0xAC, 0x64, 0xA1, 0x64, 0x8A, 0x05, 0x52, 0x94, 0x58, 0xA0, 0xA4, 0x64, 0xCA, 0x7F, 0x80, 0x28, 0x0B, 0x0B, 0x2C, 0x14, 0x76, 0xB2, 0xA6, 0x0C, 0x9F, 0xF7, 0xB9, 0xC3, 0xE7, 0xDD, 0x77, 0xCD, 0x13, 0x7D, 0xF9, 0x0E, 0xF7, 0x9E, 0xF9, 0x9E, 0x73, 0xEE, 0xBD, 0xE7, 0xBC, 0xAD, 0xF7, 0x1A, 0xAF, 0xB9, 0xD7, 0xA4, 0x45, 0x8E, 0x1C, 0x20, 0xE4, 0x98, 0x3A, 0x46, 0x53, 0x52, 0x0D, 0xAA, 0x95, 0x50, 0x93, 0x24, 0xFA, 0x36, 0x0A, 0x77, 0x09, 0x08, 0x33, 0x10, 0x3A, 0xF8, 0xA4, 0xDE, 0x73, 0x14, 0x19, 0x5A, 0x0F, 0xCA, 0x77, 0x90, 0x0C, 0xC7, 0x42, 0x06, 0xDE, 0x8F, 0x9E, 0xE4, 0xD8, 0xD7, 0xCD, 0x0B, 0x5C, 0x23, 0x98, 0xAB, 0xF9, 0xF8, 0x3E, 0xB3, 0x84, 0xA7, 0x63, 0x4B, 0x90, 0x9A, 0x7B, 0xD8, 0x6D, 0x6D, 0x31, 0x15, 0x3C, 0x28, 0x62, 0x1B, 0x8A, 0xD3, 0x7A, 0x69, 0x67, 0x51, 0xF3, 0x98, 0x0E, 0x96, 0x30, 0xB1, 0x48, 0xBA, 0x65, 0x5E, 0x48, 0x7F, 0xE5, 0x4A, 0x99, 0xF3, 0x30, 0xE7, 0xDC, 0x65, 0xCA, 0x6B, 0x4A, 0x57, 0x17, 0x4B, 0x74, 0x2A, 0x05, 0x50, 0xA1, 0xB4, 0x3C, 0x5B, 0xFC, 0x95, 0xFD, 0x4E, 0x97, 0x96, 0xDE, 0x43, 0x0E, 0x45, 0x75, 0x2C, 0x15, 0x8A, 0x59, 0xDD, 0x5A, 0xFA, 0xDA, 0xAE, 0x76, 0xD9, 0x71, 0x90, 0x8A, 0x84, 0xA8, 0xF1, 0x47, 0xD8, 0x5A, 0xBA, 0x6D, 0xEC, 0xF7, 0x75, 0x21, 0x07, 0x83, 0x6B, 0x47, 0x7D, 0x71, 0x39, 0x31, 0x58, 0xE4, 0xDE, 0xAC, 0x40, 0xBF, 0x0F, 0x20, 0xFC, 0x85, 0x16, 0xB3, 0x84, 0xEE, 0x00, 0x34, 0xE1, 0xEA, 0xBE, 0xA1, 0x10, 0x5C, 0xB8, 0x41, 0xE8, 0x61, 0x13, 0x6E, 0x88, 0xAC, 0x13, 0x35, 0xCD, 0x43, 0xAA, 0xCA, 0x65, 0xE6, 0xB4, 0xE8, 0x32, 0x73, 0x46, 0xE3, 0x02, 0xD3, 0x14, 0xF9, 0x7E, 0xD1, 0xC0, 0xCF, 0xE0, 0x29, 0xC6, 0x5F, 0xF3, 0xF8, 0xBB, 0x12, 0x3F, 0x64, 0x80, 0x4A, 0x35, 0xE1, 0x07, 0x5F, 0x56, 0x65, 0xFE, 0x32, 0x32, 0xEC, 0x9A, 0x4B, 0x2B, 0x8D, 0xE9, 0x91, 0x76, 0xCD, 0xDE, 0xC0, 0x73, 0xA9, 0x82, 0x7E, 0x46, 0xC6, 0x0D, 0x31, 0x28, 0x16, 0x44, 0xA2, 0xA0, 0x3D, 0x90, 0x14, 0xAA, 0xA4, 0x8B, 0x23, 0x67, 0x49, 0x49, 0x1C, 0x43, 0x72, 0xEB, 0x02, 0xB6, 0x69, 0x7F, 0xA5, 0xE3, 0xDC, 0x58, 0x27, 0xE9, 0xD2, 0x3F, 0x18, 0x45, 0x12, 0xF0, 0xC6, 0xF5, 0x99, 0xEF, 0x8C, 0xF5, 0x33, 0x4D, 0x31, 0x07, 0x0C, 0x40, 0x03, 0x76, 0x54, 0x0E, 0xC8, 0xB6, 0x5A, 0x58, 0x7E, 0xAB, 0x04, 0xCB, 0x7E, 0xCE, 0x83, 0x1F, 0x23, 0x2C, 0x18, 0xB4, 0x87, 0x02, 0x2C, 0x4D, 0x57, 0xF2, 0x88, 0x71, 0x30, 0x1D, 0xB3, 0x65, 0x32, 0xD7, 0xE2, 0x11, 0xAD, 0xA4, 0x45, 0x90, 0xF7, 0x85, 0x44, 0x66, 0x68, 0x88, 0x38, 0xD4, 0xA4, 0x5D, 0x58, 0xF2, 0xA5, 0xD0, 0x95, 0xA2, 0x1D, 0xD6, 0xA7, 0xFC, 0xBF, 0x08, 0xA7, 0x53, 0x92, 0xB7, 0x17, 0x7B, 0xD9, 0x51, 0x81, 0x8D, 0x5C, 0x34, 0x58, 0xDB, 0xE1, 0x85, 0x2D, 0x87, 0x37, 0xA8, 0x98, 0x53, 0x57, 0x39, 0x65, 0x39, 0xBB, 0x59, 0x93, 0xC6, 0x14, 0x93, 0xEF, 0x28, 0x2C, 0x61, 0x8C, 0x17, 0x51, 0x6A, 0x0D, 0x64, 0xCC, 0x63, 0xDC, 0x07, 0x8C, 0x2B, 0xFD, 0x68, 0x3A, 0xC2, 0xDC, 0x96, 0x9F, 0x83, 0x4A, 0xF9, 0x6E, 0x04, 0x85, 0x20, 0xF4, 0xA1, 0x12, 0xBA, 0xC4, 0xC2, 0x22, 0x50, 0x57, 0xEA, 0x5E, 0x07, 0x0F, 0x95, 0xDD, 0xB8, 0xAC, 0xB8, 0xE2, 0x29, 0xC3, 0x7E, 0xF5, 0xA0, 0x4B, 0x9C, 0x28, 0x3D, 0x79, 0x4B, 0x9B, 0x2E, 0x31, 0xB1, 0x7A, 0xDE, 0xBC, 0xA0, 0xED, 0x09, 0xBD, 0x25, 0xC7, 0x62, 0x04, 0x1B, 0x3D, 0x38, 0xEE, 0x4D, 0xF3, 0xB0, 0x5C, 0xE5, 0x2F, 0x6B, 0x7E, 0xA5, 0x98, 0x2C, 0x6B, 0x0D, 0xCA, 0x39, 0xB6, 0x2D, 0x1D, 0x52, 0x0F, 0xF0, 0x07, 0x59, 0x81, 0xDE, 0xC5, 0x58, 0xF2, 0xD9, 0x84, 0x28, 0xE1, 0x7D, 0xDC, 0x9C, 0x87, 0x51, 0x91, 0xB3, 0x5C, 0x83, 0xDA, 0xE1, 0x70, 0xF4, 0xC4, 0x83, 0xAA, 0x73, 0x4D, 0xED, 0x3A, 0x45, 0x42, 0xE8, 0xB4, 0x03, 0x98, 0xD2, 0x8C, 0x72, 0xB5, 0x79, 0xEE, 0xEA, 0x4B, 0x11, 0x1E, 0xDF, 0xDB, 0xC8, 0x4E, 0x0F, 0x4E, 0xF3, 0xA4, 0x5B, 0x5C, 0xE3, 0xEE, 0x17, 0xC9, 0x93, 0xBC, 0x39, 0x5D, 0xD3, 0x90, 0x7D, 0xD1, 0x79, 0xDA, 0x5F, 0x91, 0xE9, 0xB3, 0xE6, 0x8C, 0x0B, 0x7F, 0x55, 0xA6, 0x74, 0x63, 0xFF, 0x47, 0xA0, 0x32, 0xEF, 0xE9, 0xDF, 0x12, 0xA8, 0xDB, 0x44, 0x9C, 0xAC, 0x08, 0x74, 0x48, 0xDD, 0x3A, 0xB7, 0x9F, 0x95, 0x29, 0x48, 0x22, 0x07, 0x7F, 0x2E, 0x53, 0x45, 0x74, 0xBB, 0x6F, 0x11, 0x1E, 0x45, 0xF4, 0xFA, 0x37, 0x22, 0x3A, 0xC3, 0x93, 0x82, 0x7A, 0x2A, 0x5A, 0x12, 0x26, 0xD1, 0x4E, 0x0E, 0xA2, 0x3D, 0xDF, 0x54, 0xF1, 0x7D, 0xEC, 0x3F, 0x97, 0xAE, 0x22, 0x3A, 0xCF, 0x4D, 0x7E, 0xEC, 0x0C, 0x82, 0xC9, 0x2A, 0xAA, 0x83, 0x22, 0x1A, 0x8A, 0xE3, 0xC0, 0x89, 0x44, 0x51, 0x15, 0x78, 0x10, 0xC6, 0xBE, 0x17, 0x51, 0xE3, 0x88, 0x2E, 0x97, 0x1A, 0x1F, 0xA1, 0xC9, 0xAD, 0x12, 0x7F, 0xEE, 0x55, 0x31, 0xD4, 0xBB, 0xCC, 0x38, 0xCF, 0xD9, 0xA2, 0x00, 0x0B, 0x7E, 0x15, 0x5C, 0x49, 0xD1, 0x8F, 0x73, 0x8C, 0x7D, 0x39, 0xB2, 0xC9, 0xEF, 0xA5, 0x92, 0x8A, 0x52, 0x5A, 0xCC, 0xE9, 0xD2, 0xA1, 0xE1, 0x1D, 0x8A, 0x74, 0x1C, 0x1B, 0xDE, 0x5C, 0x19, 0x1E, 0xB4, 0x23, 0xC3, 0x1B, 0x1E, 0x52, 0x49, 0xC9, 0x7E, 0xE6, 0x8F, 0x52, 0xC9, 0x1C, 0x0B, 0x3A, 0xDA, 0xF5, 0xD2, 0xEA, 0xD2, 0xE5, 0xE0, 0xD2, 0x49, 0xED, 0x72, 0x2F, 0x34, 0x8D, 0x4B, 0x0A, 0xB4, 0xAF, 0x05, 0x89, 0x0B, 0x94, 0x52, 0x6A, 0x5F, 0x4B, 0x51, 0x13, 0xED, 0xFF, 0x4A, 0x26, 0x25, 0xE0, 0xC8, 0xF4, 0xC8, 0x52, 0x6A, 0x7A, 0x3F, 0x50, 0xF4, 0x61, 0x99, 0x9E, 0x8B, 0x2C, 0xDB, 0x93, 0x64, 0xB2, 0x5F, 0x92, 0x4C, 0xD2, 0x9B, 0x8A, 0x0E, 0x60, 0x91, 0xF9, 0x7B, 0xAF, 0x72, 0x52, 0xF8, 0xB1, 0x6C, 0x7F, 0x2E, 0x99, 0x9C, 0x95, 0x26, 0x93, 0xA9, 0xCD, 0xA7, 0x09, 0x25, 0x44, 0xF8, 0x0B, 0x36, 0xFF, 0x93, 0xC9, 0x24, 0xC8, 0xC1, 0xAB, 0x7A, 0x52, 0x3F, 0xE8, 0x5B, 0x89, 0x64, 0xA4, 0x8F, 0x24, 0x8D, 0xC4, 0xAF, 0xED, 0x94, 0xEA, 0x57, 0xD2, 0xBF, 0xC5, 0x61, 0x8C, 0x51, 0x3E, 0x06, 0x42, 0xA3, 0x14, 0x72, 0xDB, 0x9F, 0xB6, 0x35, 0x6F, 0x86, 0x78, 0xC3, 0x41, 0x43, 0x8F, 0x6A, 0x1E, 0x5A, 0x10, 0x58, 0x48, 0xD4, 0xE4, 0x39, 0xCC, 0x60, 0x1B, 0x52, 0xAC, 0x1D, 0xD4, 0x81, 0x1F, 0xFF, 0xDD, 0x7C, 0x94, 0xB3, 0x6B, 0x2E, 0x9D, 0x63, 0x37, 0xE3, 0x1A, 0x61, 0xAA, 0xE4, 0xA5, 0x07, 0xFD, 0xF8, 0xA0, 0xF4, 0x5C, 0x59, 0xA9, 0x90, 0x49, 0x0A, 0x15, 0xF0, 0xB4, 0xD3, 0x7D, 0x4E, 0xCD, 0x62, 0x24, 0x05, 0xBD, 0xA6, 0xB0, 0x27, 0x29, 0xB0, 0x5A, 0xC9, 0xD0, 0xA3, 0x6C, 0x55, 0xE4, 0x45, 0x16, 0xE5, 0xC0, 0x6C, 0xF1, 0xC2, 0x67, 0xD2, 0x15, 0xBA, 0x7D, 0x38, 0xFB, 0x9B, 0x49, 0xAA, 0x08, 0xA0, 0x1C, 0xEA, 0x90, 0xA4, 0x66, 0x45, 0xC9, 0x6A, 0x15, 0xCB, 0xE1, 0x42, 0x16, 0x62, 0xAC, 0xAA, 0xA1, 0x5A, 0x01, 0x77, 0xB6, 0x4B, 0x59, 0xF7, 0x25, 0x29, 0xAB, 0x9B, 0x95, 0x6E, 0xEE, 0xD9, 0x8B, 0xE4, 0xF1, 0xE0, 0x73, 0xE3, 0xC3, 0x4C, 0x60, 0x78, 0x02, 0x16, 0x3B, 0x0A, 0xC0, 0x9F, 0x8F, 0xDF, 0x4F, 0x67, 0x13, 0x9D, 0x04, 0x7A, 0x28, 0x6E, 0x52, 0xB8, 0xD3, 0xB0, 0xA8, 0xAD, 0x2A, 0xD8, 0xD7, 0xC8, 0x8A, 0xAA, 0x7D, 0x5B, 0xE5, 0x28, 0x06, 0xBA, 0xC4, 0xBF, 0x41, 0x3B, 0x4E, 0xA6, 0x55, 0xC0, 0x5B, 0x40, 0xE4, 0xBF, 0xA0, 0xC9, 0x1E, 0x16, 0x99, 0xB7, 0x50, 0xA7, 0x36, 0x17, 0xBC, 0xAE, 0xAF, 0x8E, 0x42, 0xF7, 0xE0, 0xC7, 0xBA, 0x2A, 0xE6, 0x63, 0xEB, 0x88, 0xC1, 0x72, 0xA5, 0x4C, 0x76, 0x93, 0x3A, 0xA1, 0x6C, 0xAA, 0xC8, 0x97, 0xEC, 0x24, 0x6D, 0x5F, 0xBE, 0x8A, 0xA1, 0x17, 0x46, 0x7E, 0xDC, 0x2B, 0x59, 0x0E, 0x20, 0x17, 0x8A, 0xF6, 0xF1, 0xC5, 0xC4, 0xF8, 0x6F, 0xA0, 0x1C, 0xA2, 0x64, 0x27, 0xDD, 0x36, 0xA6, 0x68, 0x5D, 0xD6, 0x29, 0xF3, 0xA8, 0xCB, 0x34, 0x66, 0x6A, 0x74, 0xCE, 0x52, 0x1A, 0xCE, 0x70, 0x39, 0xB5, 0x63, 0xE9, 0x98, 0x87, 0xED, 0x3A, 0x66, 0x31, 0x47, 0xB0, 0x65, 0xFB, 0x55, 0xC3, 0xAF, 0xA7, 0x46, 0x4F, 0x86, 0xF0, 0x6E, 0x28, 0x98, 0x8C, 0x31, 0x26, 0x86, 0x9F, 0x2D, 0x4E, 0x8D, 0xBE, 0x2C, 0x10, 0x50, 0x21, 0xDE, 0x30, 0x82, 0x27, 0x71, 0x76, 0x6A, 0xFE, 0x2D, 0xA5, 0xC5, 0xEC, 0xE5, 0x29, 0x7B, 0x89, 0xE5, 0xC4, 0x4B, 0x3B, 0x39, 0xB9, 0xCD, 0x13, 0xE7, 0xFC, 0x3D, 0xCD, 0xD5, 0x1C, 0x93, 0xD9, 0x84, 0xA0, 0xB6, 0x03, 0xDF, 0xC5, 0xCD, 0x7C, 0x08, 0xFE, 0xF1, 0x4C, 0x36, 0x92, 0x02, 0xFD, 0xB1, 0x31, 0x72, 0x5D, 0xFF, 0xD8, 0x12, 0xB7, 0xA2, 0x5E, 0xF0, 0x3E, 0xEA, 0x05, 0x9F, 0xA3, 0x5C, 0xF0, 0x13, 0xCA, 0x04, 0xDF, 0xA1, 0x4C, 0xF0, 0x21, 0xCA, 0x04, 0xEF, 0xA1, 0x4C, 0x70, 0x18, 0xCA, 0x04, 0x7B, 0xA2, 0x4C, 0xB0, 0x11, 0xCA, 0x03, 0xDB, 0xA2, 0x3C, 0x70, 0x08, 0xCA, 0x03, 0xA7, 0xA2, 0x3C, 0xF0, 0x44, 0xFE, 0xBD, 0x56, 0xCC, 0x4D, 0xDF, 0xEB, 0xC5, 0xDC, 0x74, 0xAF, 0xF1, 0x46, 0x88, 0xF0, 0xD5, 0xBD, 0x66, 0x8D, 0xB7, 0xA3, 0x17, 0x13, 0xE1, 0xE3, 0xFA, 0x67, 0x3E, 0x26, 0x42, 0xBD, 0x98, 0x78, 0xDD, 0xC4, 0xEB, 0x16, 0x5E, 0xB7, 0x0D, 0x7A, 0x31, 0xF1, 0xBA, 0x6B, 0x4A, 0xBD, 0x98, 0xC6, 0x3D, 0x26, 0xC2, 0xE0, 0x7E, 0x63, 0xDE, 0xBD, 0x16, 0xCD, 0x37, 0x15, 0xCD, 0x7C, 0x7D, 0x98, 0x02, 0x79, 0x33, 0x7B, 0xD0, 0xDC, 0x56, 0x7C, 0xC1, 0xC3, 0x29, 0xBF, 0x43, 0x44, 0xC8, 0xAD, 0xD7, 0x41, 0x0E, 0xE7, 0x8A, 0x78, 0xDC, 0xD6, 0xE2, 0x20, 0x12, 0xA5, 0xA2, 0x87, 0xF3, 0xA1, 0xEB, 0x4C, 0xCF, 0xED, 0x66, 0x8D, 0xB1, 0xFD, 0x72, 0x76, 0x1A, 0xD9, 0x19, 0x17, 0x00, 0x20, 0x63, 0x1C, 0xA0, 0xA2, 0x67, 0x1A, 0xB0, 0xAE, 0xB4, 0x01, 0x3E, 0xCE, 0x7E, 0xCE, 0xD9, 0x93, 0x0A, 0x5C, 0x90, 0x35, 0x60, 0xF2, 0x9B, 0xCB, 0x80, 0x02, 0x12, 0x41, 0x03, 0x66, 0xBA, 0x6C, 0x11, 0x59, 0x20, 0xE6, 0x3F, 0xE5, 0xFC, 0x4B, 0x6D, 0x54, 0xAB, 0x76, 0x90, 0x30, 0x4F, 0xEB, 0xE4, 0x95, 0x14, 0x18, 0x52, 0x3E, 0x8D, 0x5E, 0xFF, 0xB2, 0xCA, 0x99, 0xCC, 0x39, 0x58, 0xC3, 0x72, 0x6C, 0xC9, 0x76, 0xE1, 0xB2, 0xF9, 0x97, 0x78, 0xB6, 0x9D, 0xD5, 0x29, 0xCF, 0x2E, 0x75, 0x8A, 0x79, 0x9E, 0xF7, 0x0D, 0x9E, 0x4D, 0x89, 0xE7, 0x47, 0xCC, 0xA5, 0xC9, 0xAD, 0xC3, 0x0B, 0x5A, 0x41, 0x97, 0x78, 0x5E, 0xE2, 0x78, 0x7E, 0x82, 0x91, 0xD3, 0x30, 0x93, 0x19, 0x50, 0x2B, 0xF6, 0xA0, 0x63, 0x93, 0x2C, 0xCE, 0x57, 0xB6, 0xA6, 0x7C, 0x88, 0xFD, 0x2C, 0xB0, 0x5F, 0xCC, 0x46, 0x35, 0x76, 0x57, 0x8A, 0x18, 0x21, 0xBE, 0xFC, 0xBC, 0x1C, 0x8F, 0x4B, 0xC0, 0x6C, 0x2B, 0x57, 0x4A, 0xE8, 0x34, 0xA8, 0xF3, 0xB4, 0xBF, 0xBF, 0xD7, 0xA2, 0x99, 0x36, 0x70, 0x60, 0x41, 0x00, 0xC0, 0x26, 0x26, 0x1F, 0xF9, 0xAE, 0x21, 0x48, 0x0D, 0xDD, 0x98, 0xAE, 0x89, 0xB4, 0x71, 0x54, 0x02, 0x02, 0x16, 0x9B, 0xEA, 0x17, 0x43, 0x09, 0x77, 0x60, 0x83, 0xD2, 0x03, 0xE5, 0x99, 0xE8, 0xA1, 0x9E, 0xDA, 0x1E, 0xF5, 0x20, 0xFE, 0xA1, 0x3F, 0xD9, 0x9E, 0xD0, 0x9D, 0xA6, 0x24, 0x80, 0x94, 0x52, 0x90, 0xED, 0x5C, 0x2D, 0x6C, 0x4F, 0xB3, 0xCB, 0xB2, 0xC9, 0x9E, 0x51, 0x1B, 0xA7, 0x30, 0xF3, 0x4E, 0x36, 0x49, 0xB6, 0x07, 0xCD, 0x4A, 0x8F, 0xD7, 0x28, 0xBB, 0x67, 0x9C, 0xFB, 0x18, 0x9A, 0x31, 0x78, 0xEF, 0xF1, 0xBD, 0x48, 0x04, 0x67, 0x4F, 0xB1, 0x4F, 0x7C, 0x57, 0x70, 0xA4, 0x43, 0x57, 0x34, 0x4C, 0xF6, 0xF1, 0x2E, 0x70, 0x22, 0xEB, 0xBD, 0x06, 0xEB, 0x3D, 0xB1, 0xA3, 0xA0, 0x6E, 0x38, 0xAF, 0x7B, 0x62, 0xA9, 0xF1, 0x1C, 0x9D, 0xFD, 0xE7, 0x0C, 0x4C, 0x80, 0xD4, 0x95, 0x96, 0x6B, 0xD5, 0x60, 0xC4, 0x33, 0xB7, 0x47, 0x3A, 0x64, 0xA3, 0x7C, 0x99, 0x21, 0x05, 0x5B, 0x64, 0xDE, 0xB1, 0x78, 0x67, 0xC1, 0xD1, 0x68, 0xDE, 0x75, 0xC8, 0x26, 0x29, 0x0B, 0x42, 0x20, 0x5F, 0xCE, 0x32, 0x0F, 0x86, 0x03, 0x08, 0xC2, 0x05, 0x46, 0x99, 0x85, 0x30, 0xC2, 0x24, 0x90, 0x0E, 0x68, 0xAD, 0xA0, 0xBB, 0x82, 0x3D, 0xEE, 0xE7, 0x10, 0xCD, 0x3F, 0x32, 0xE0, 0x0D, 0x2D, 0xF4, 0x58, 0x8B, 0x2C, 0x62, 0x47, 0xC0, 0xB7, 0x25, 0x78, 0x89, 0x12, 0xCE, 0x97, 0xCD, 0xEF, 0x35, 0x5D, 0x8C, 0x06, 0x75, 0x88, 0x1E, 0xCC, 0xFA, 0x13, 0x46, 0x09, 0x96, 0x62, 0x01, 0x58, 0x97, 0x12, 0xD1, 0x8A, 0x99, 0xD8, 0x2D, 0x71, 0xE2, 0xC5, 0x8E, 0xAA, 0xF1, 0x78, 0xE7, 0x59, 0xF1, 0xD4, 0x86, 0x4E, 0x08, 0xBF, 0x56, 0xBC, 0xFE, 0x5E, 0x9E, 0xC2, 0xE4, 0xF2, 0x74, 0x42, 0x5E, 0x42, 0x68, 0x64, 0xB3, 0xEE, 0x85, 0x3D, 0xB4, 0x16, 0x6F, 0x47, 0x96, 0xEF, 0xBD, 0xD7, 0xB4, 0x57, 0x41, 0xD6, 0x28, 0x4C, 0x68, 0xE2, 0x40, 0x1D, 0x44, 0x18, 0xF7, 0x77, 0x65, 0xC0, 0x45, 0xAD, 0xBB, 0xE8, 0x03, 0x5B, 0xF2, 0xA5, 0x8F, 0x8D, 0x42, 0x95, 0x5E, 0x8F, 0x9D, 0x20, 0x10, 0xE0, 0xE3, 0x47, 0x2C, 0xA9, 0x20, 0x7D, 0x53, 0x54, 0x91, 0x1E, 0x57, 0xA2, 0xEF, 0xDB, 0x1E, 0x6D, 0x53, 0x77, 0x86, 0x7C, 0xA9, 0xE8, 0x12, 0xEC, 0xFD, 0xE3, 0x6A, 0xE9, 0xE4, 0x16, 0x5E, 0x15, 0xD3, 0x4A, 0x9D, 0xD3, 0x7B, 0xF4, 0xCB, 0xDC, 0xAC, 0xB8, 0xB3, 0x2A, 0x32, 0xDE, 0xC0, 0x03, 0x83, 0xC6, 0x16, 0x56, 0xD6, 0xE0, 0x4C, 0xB2, 0x51, 0x61, 0x06, 0x4C, 0x4A, 0x2F, 0x66, 0x53, 0x43, 0x15, 0x28, 0xDD, 0xB6, 0x7F, 0xB4, 0x06, 0x1F, 0x77, 0x02, 0xC7, 0xD9, 0x94, 0x07, 0xED, 0xBF, 0x5D, 0x1B, 0x74, 0xF0, 0x6B, 0x35, 0x9B, 0xFB, 0xB6, 0xE9, 0xDC, 0x80, 0xB0, 0x9B, 0xF8, 0x3B, 0x7E, 0xB8, 0x02, 0x7F, 0x6A, 0xD0, 0x38, 0xB7, 0x8F, 0xEC, 0x08, 0xE7, 0x05, 0xC4, 0xDB, 0x28, 0x5C, 0xE5, 0xBD, 0x68, 0x1E, 0x84, 0x8A, 0x8B, 0x1A, 0x4F, 0x97, 0x11, 0x5D, 0x30, 0x78, 0x77, 0x8A, 0x69, 0xCE, 0x65, 0x33, 0x7F, 0x5C, 0x3D, 0xA4, 0x6B, 0xE9, 0x9F, 0xAA, 0x1E, 0x3A, 0x51, 0x2B, 0x38, 0xA9, 0xBB, 0x13, 0x1E, 0x09, 0x3C, 0xDD, 0xF7, 0x91, 0x74, 0xAF, 0x04, 0xB2, 0x48, 0x9F, 0xA1, 0x1B, 0xEF, 0x46, 0xBA, 0xA5, 0xB5, 0x87, 0xF6, 0x1C, 0x99, 0x3B, 0xA5, 0x90, 0x54, 0xCC, 0x0A, 0x73, 0xF9, 0x2F, 0x44, 0x13, 0xC6, 0x1F, 0xDE, 0xB7, 0x83, 0x64, 0xFC, 0x72, 0xD1, 0x8E, 0x6A, 0x33, 0xA6, 0xFE, 0x4C, 0xD1, 0x8E, 0xF8, 0x0E, 0xC7, 0x52, 0xD4, 0x9C, 0xCB, 0xC4, 0xE7, 0x8A, 0xC9, 0x32, 0xA1, 0xB4, 0xB7, 0x50, 0x97, 0x82, 0x51, 0x60, 0xD0, 0x55, 0x43, 0x30, 0x89, 0x94, 0xE6, 0xD2, 0xAD, 0x32, 0x98, 0xA4, 0xB0, 0x3C, 0x4D, 0x80, 0xE8, 0x61, 0x9C, 0x00, 0xD3, 0xB4, 0x22, 0xB2, 0x9E, 0x96, 0xF3, 0xFC, 0x52, 0xB9, 0xD6, 0xB8, 0x5F, 0x2C, 0xE7, 0x39, 0xB7, 0xC5, 0x9A, 0xB1, 0x4E, 0x9F, 0xFE, 0xA6, 0x09, 0x90, 0xBB, 0xED, 0x51, 0x53, 0xC7, 0x6F, 0x56, 0x20, 0xF0, 0x8A, 0x7B, 0x4D, 0x3D, 0xDC, 0x18, 0xA9, 0x0A, 0xC1, 0x6E, 0xD2, 0x92, 0x8A, 0xB4, 0x25, 0xE5, 0x92, 0x0B, 0xB1, 0x28, 0x2A, 0xB6, 0x68, 0xB5, 0x46, 0x9B, 0x86, 0x1D, 0x15, 0x1E, 0xC9, 0xC5, 0xE2, 0xEA, 0xB5, 0x60, 0x2D, 0x70, 0xAA, 0x1B, 0x2A, 0x19, 0xEF, 0xAF, 0x96, 0x1D, 0x34, 0x7C, 0xBF, 0xEC, 0x80, 0xCB, 0x57, 0x86, 0x7B, 0x39, 0x31, 0xDC, 0xB6, 0x91, 0xE1, 0x42, 0x5C, 0xDE, 0xE0, 0xA8, 0x20, 0xAA, 0x85, 0x30, 0xA8, 0x12, 0xEF, 0x2A, 0xE9, 0x34, 0x8C, 0x7D, 0x52, 0x93, 0x63, 0x63, 0x7F, 0x6B, 0x6E, 0x19, 0x29, 0xD0, 0x5F, 0xAA, 0xC3, 0x55, 0x61, 0x95, 0x1A, 0x61, 0xD4, 0x86, 0x06, 0x13, 0xA2, 0x8B, 0x91, 0x99, 0xD3, 0x57, 0xD2, 0xC2, 0xA2, 0xE3, 0x7D, 0x86, 0x33, 0xAE, 0x0D, 0xE8, 0xF9, 0xA8, 0x7C, 0xA5, 0xAB, 0xAE, 0x95, 0xAF, 0xDC, 0x14, 0x9C, 0x25, 0x25, 0x69, 0x4A, 0x05, 0xDA, 0x0D, 0x91, 0xB3, 0xFC, 0x9B, 0xC5, 0xAE, 0xA9, 0xB3, 0x4C, 0xAD, 0x28, 0x56, 0x72, 0xBA, 0x52, 0x54, 0xE3, 0xC6, 0x08, 0x20, 0x5F, 0x22, 0x67, 0x39, 0x2F, 0x71, 0x96, 0x52, 0xA5, 0xFC, 0x46, 0xEA, 0x2C, 0xA5, 0x2D, 0xD9, 0xDB, 0xCF, 0x16, 0x11, 0xCB, 0xDE, 0x54, 0xB7, 0x14, 0xF3, 0x9D, 0x06, 0x64, 0x5D, 0x20, 0xC7, 0x4C, 0x92, 0xDC, 0xD4, 0x15, 0xA4, 0xCE, 0x72, 0xD2, 0xD7, 0x9D, 0x65, 0x2A, 0x2C, 0x40, 0x4C, 0xDD, 0x49, 0xBA, 0xD6, 0x7E, 0x5C, 0x5A, 0x27, 0xDE, 0x7F, 0x5C, 0x5E, 0xA7, 0xB3, 0x3D, 0x49, 0xE1, 0x5C, 0xE4, 0x4A, 0xFE, 0xD8, 0x08, 0x6A, 0x89, 0x8C, 0x14, 0x0C, 0x52, 0xF6, 0x7E, 0xBA, 0xB6, 0x93, 0xDE, 0xD2, 0x07, 0x84, 0xD1, 0x5F, 0xBB, 0x5F, 0x4F, 0x02, 0xC2, 0x92, 0x5A, 0xEC, 0x2D, 0xEB, 0x96, 0xC5, 0x88, 0x43, 0xEE, 0x97, 0xBE, 0xC9, 0x61, 0x5C, 0x1D, 0xA1, 0xD3, 0x9C, 0xC8, 0xD0, 0xED, 0xE5, 0xFB, 0x7B, 0xF4, 0x86, 0x62, 0x0B, 0x32, 0x6F, 0xBB, 0xCD, 0x96, 0xB3, 0xC1, 0x8C, 0x51, 0x3F, 0xDD, 0xCF, 0xA1, 0xFE, 0x13, 0xFA, 0x30, 0xF9, 0x97, 0x1A, 0x36, 0xBE, 0xEE, 0x3C, 0x02, 0xF9, 0xB2, 0xF7, 0xAA, 0xED, 0x38, 0xC7, 0xB0, 0x90, 0x89, 0x0F, 0x99, 0xA4, 0x2B, 0xE9, 0x5E, 0x7C, 0x6E, 0xFC, 0x3E, 0x14, 0x76, 0x13, 0xBC, 0xDA, 0x30, 0xBE, 0xF9, 0x64, 0x10, 0x96, 0x0F, 0xCE, 0x2B, 0x2E, 0x64, 0x37, 0xDB, 0x24, 0x5F, 0xE5, 0xE6, 0xA2, 0xDC, 0x27, 0x91, 0x35, 0xBF, 0xF2, 0xD2, 0xC6, 0x1F, 0xD5, 0x86, 0xEF, 0x52, 0x7F, 0xEA, 0x9D, 0x7D, 0xEE, 0x48, 0xC5, 0xF6, 0x27, 0x84, 0xAD, 0x9D, 0xEA, 0x2B, 0x73, 0x05, 0x0E, 0xC7, 0xBC, 0x72, 0xD4, 0x35, 0x21, 0x47, 0xA5, 0xB0, 0x69, 0x5E, 0x43, 0xEB, 0xB4, 0x1A, 0x5F, 0x29, 0xBF, 0x03, 0x12, 0x42, 0xFC, 0x04, 0xEC, 0xFA, 0x57, 0xE1, 0x42, 0x89, 0xF4, 0x2E, 0x31, 0x5C, 0x40, 0xF3, 0x41, 0xA8, 0x9D, 0x2B, 0x39, 0x59, 0x02, 0xE7, 0x6B, 0xCF, 0x5B, 0x69, 0x2E, 0xD8, 0x23, 0xF3, 0x14, 0xF8, 0x60, 0xEE, 0x77, 0x28, 0xE3, 0xFD, 0xF5, 0x87, 0x05, 0x1F, 0x5A, 0xE4, 0x4C, 0xB9, 0x45, 0x2E, 0x5B, 0xC4, 0xCD, 0x10, 0x57, 0x21, 0xC6, 0xA9, 0x2D, 0x6E, 0x71, 0xB1, 0x6B, 0xE6, 0xF7, 0x10, 0x37, 0x4F, 0x6A, 0xA9, 0xA9, 0x09, 0x05, 0x8A, 0x52, 0x0D, 0xB9, 0x71, 0x0F, 0x1C, 0xFC, 0xC9, 0xF6, 0xB8, 0xB4, 0x3B, 0xCC, 0x3D, 0xAC, 0x4E, 0x3D, 0xD2, 0x5B, 0xFD, 0x73, 0x51, 0x20, 0xA7, 0xC1, 0x05, 0x36, 0xC0, 0x08, 0x07, 0x78, 0x9C, 0xD3, 0x2E, 0x75, 0x75, 0x78, 0x6F, 0xA1, 0xDC, 0xA9, 0x43, 0x87, 0x7B, 0xA1, 0x42, 0xE8, 0x0F, 0x73, 0xC3, 0xD1, 0xFF, 0xE4, 0x2A, 0x8C, 0x9A, 0x44, 0x80, 0xB9, 0x9E, 0x79, 0x52, 0x26, 0x68, 0x2F, 0x00, 0xED, 0x05, 0x44, 0xDB, 0x01, 0x01, 0x59, 0xD0, 0x1A, 0x1C, 0xB4, 0x63, 0x36, 0xE0, 0x07, 0x8A, 0x04, 0x49, 0x2D, 0x80, 0xC0, 0x28, 0xBA, 0xC0, 0x53, 0xAF, 0xC2, 0x5E, 0x87, 0x78, 0x60, 0x98, 0xF6, 0x1D, 0x40, 0xFC, 0x57, 0x20, 0x7A, 0xD9, 0xA3, 0xF5, 0x5E, 0x3C, 0xEA, 0xF6, 0x10, 0xB8, 0x5E, 0x27, 0x55, 0xA1, 0x6C, 0x11, 0x73, 0x98, 0x2D, 0xCD, 0x8A, 0xB9, 0x1A, 0x98, 0x83, 0xC0, 0x0B, 0xFE, 0x20, 0xEC, 0x08, 0x5E, 0x43, 0x0A, 0x4F, 0xF9, 0x8C, 0xD8, 0x8B, 0x60, 0x8D, 0x07, 0x6D, 0x39, 0x76, 0xDF, 0x81, 0xB6, 0x5A, 0x44, 0x5B, 0xAE, 0x58, 0x25, 0x05, 0x46, 0x41, 0xB5, 0x1E, 0x02, 0xF1, 0x16, 0xFB, 0x0C, 0xD5, 0x8D, 0x82, 0x03, 0xEC, 0x3F, 0x6F, 0x00, 0x17, 0x5D, 0x29, 0x1A, 0x3C, 0xDB, 0x0D, 0xEC, 0x96, 0xF9, 0xB0, 0x0E, 0xB7, 0x57, 0xEE, 0x6B, 0x99, 0x94, 0x2F, 0x77, 0x37, 0x1B, 0x7A, 0xDE, 0x82, 0xCF, 0x63, 0xD3, 0x76, 0x5A, 0x1C, 0xD9, 0x28, 0xFB, 0x25, 0x1C, 0x6F, 0xD7, 0x79, 0xDA, 0x56, 0x6B, 0xE1, 0xC8, 0xBE, 0x39, 0xFF, 0xA2, 0x0F, 0xDF, 0x9C, 0xDB, 0xA4, 0x6A, 0x47, 0xF8, 0xB5, 0x31, 0xD7, 0x0D, 0x71, 0x61, 0x96, 0x2C, 0x7F, 0xAE, 0xCF, 0xBF, 0x30, 0x5E, 0x17, 0x58, 0x80, 0x58, 0x1E, 0xB5, 0xC5, 0xED, 0xB9, 0xF5, 0xBB, 0xA0, 0x08, 0x8B, 0xE6, 0x2A, 0x73, 0x39, 0x18, 0xED, 0x75, 0xBC, 0xD7, 0xF8, 0x71, 0x03, 0x2A, 0x10, 0x84, 0x2E, 0xE3, 0xD1, 0x15, 0x91, 0xA4, 0x05, 0xE6, 0x84, 0x43, 0x56, 0x28, 0x58, 0xCE, 0xBF, 0xB8, 0x25, 0x88, 0x84, 0x19, 0x4D, 0xC5, 0xF6, 0xB6, 0x78, 0x91, 0x10, 0x17, 0x67, 0x1D, 0xE4, 0x45, 0x47, 0xEE, 0x4E, 0x57, 0x24, 0x92, 0x63, 0x62, 0x2B, 0x0E, 0x9B, 0x07, 0xA3, 0xB3, 0x91, 0x71, 0x24, 0x2E, 0x2A, 0x0B, 0x75, 0x84, 0x2D, 0x70, 0x8C, 0x49, 0xD7, 0xB9, 0x25, 0x6C, 0x67, 0xDC, 0xF3, 0xB4, 0x26, 0xF4, 0x14, 0xAE, 0xB1, 0x89, 0x6A, 0x40, 0x1C, 0x15, 0xC5, 0x12, 0x2F, 0x9F, 0x73, 0x2C, 0x3B, 0xA3, 0xAB, 0x95, 0x40, 0x87, 0x06, 0x81, 0x0E, 0x15, 0x5E, 0xBA, 0xF2, 0x4D, 0x85, 0x44, 0x07, 0x25, 0x86, 0x42, 0xA9, 0xAA, 0x00, 0xA0, 0xCC, 0x75, 0x61, 0x3C, 0xBB, 0xC5, 0x38, 0x09, 0x60, 0x7E, 0x37, 0xDE, 0x33, 0x4C, 0x44, 0x6D, 0xB7, 0x70, 0xB3, 0xA6, 0xB9, 0x32, 0x12, 0x3E, 0x70, 0x98, 0x90, 0xAB, 0xC6, 0xB2, 0x6F, 0x8F, 0x7D, 0xDE, 0x23, 0x42, 0x7A, 0x88, 0x89, 0xB9, 0x0D, 0xFD, 0xC1, 0xEB, 0x0F, 0x55, 0x3B, 0x03, 0x65, 0xD3, 0xA3, 0xD2, 0xFB, 0xDE, 0xAE, 0xD2, 0x75, 0x6D, 0xCA, 0x4D, 0x0D, 0xF8, 0xD7, 0x2B, 0xFF, 0x09, 0xCF, 0x97, 0xD6, 0xB6, 0xF6, 0xCA, 0x5D, 0x85, 0x77, 0x26, 0xEB, 0xDC, 0xA9, 0xD8, 0x1E, 0xB5, 0xEF, 0x84, 0x07, 0x60, 0xE6, 0x08, 0x0E, 0xB9, 0x7D, 0x8E, 0x25, 0xB8, 0x94, 0x7A, 0xD7, 0xB8, 0xBC, 0x38, 0x69, 0xC6, 0x27, 0xC2, 0xA8, 0x5D, 0x99, 0x76, 0xA4, 0x56, 0xE5, 0x69, 0x69, 0x30, 0xAA, 0xAB, 0x59, 0x4F, 0x0E, 0xE3, 0x27, 0x30, 0x88, 0xE5, 0x1F, 0x61, 0xC8, 0x55, 0xB0, 0x8E, 0x1F, 0x5D, 0x3F, 0x7D, 0x39, 0x79, 0x91, 0xD4, 0x92, 0xC4, 0xA5, 0x9D, 0x4B, 0x5C, 0xE6, 0xFC, 0xF6, 0x63, 0x2B, 0x37, 0x25, 0x4F, 0x40, 0xC9, 0xC6, 0xFC, 0xB5, 0xC7, 0x4C, 0xAE, 0x99, 0xB9, 0x6A, 0x68, 0x83, 0x69, 0x64, 0xCC, 0x07, 0x63, 0xCC, 0x3C, 0x93, 0x99, 0x2D, 0x66, 0xAB, 0x69, 0x30, 0x53, 0xF0, 0x6E, 0x2B, 0xFE, 0xAE, 0x37, 0xAB, 0x4D, 0xF1, 0x67, 0x86, 0x59, 0x67, 0x36, 0x98, 0x0C, 0x9F, 0x1B, 0xF0, 0x3E, 0xC3, 0xFB, 0xAD, 0x1C, 0xBB, 0x83, 0xBF, 0x2E, 0x33, 0x1B, 0xF1, 0xCD, 0x2A, 0x8C, 0xAD, 0x99, 0xC5, 0x26, 0xE7, 0xEF, 0x0D, 0x66, 0x2A, 0xBE, 0x5D, 0x8F, 0x57, 0xCD, 0xF8, 0x3F, 0x43, 0xCC, 0x20, 0xFE, 0xED, 0x6F, 0x06, 0xE3, 0x7D, 0x7F, 0xBC, 0x1B, 0x8D, 0x6F, 0x97, 0x16, 0x70, 0x89, 0x69, 0x07, 0x31, 0xAF, 0xE6, 0x27, 0x63, 0x16, 0x63, 0xEE, 0x36, 0xC2, 0x6D, 0x30, 0xB3, 0xCD, 0x34, 0x33, 0x06, 0xFF, 0x0F, 0x06, 0xD4, 0x56, 0x66, 0x26, 0xC6, 0x6C, 0x28, 0xB0, 0x91, 0xCA, 0xD5, 0x1C, 0xB1, 0x82, 0xB3, 0x97, 0x61, 0xD6, 0x22, 0xD3, 0x10, 0xCD, 0x5D, 0x08, 0x68, 0x99, 0xD9, 0xC1, 0xF1, 0x0D, 0x8E, 0x86, 0x11, 0xC4, 0x3E, 0x9C, 0xFF, 0x0E, 0x01, 0xAE, 0x2F, 0x11, 0x23, 0x48, 0xC8, 0x5B, 0x60, 0x00, 0x00}; ================================================ FILE: VGMPlay/XMasFiles/XMasBonus.h ================================================ // WEWISH.CMF - We Wish You A Merry Christmas const unsigned char WEWISH_CMF[0x2245] = { 0x43, 0x54, 0x4D, 0x46, 0x01, 0x01, 0x28, 0x00, 0xE8, 0x00, 0x28, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x0C, 0x00, 0x90, 0x00, 0x31, 0xA1, 0x1C, 0x80, 0x41, 0x92, 0x01, 0x3B, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xB1, 0xD2, 0x80, 0x81, 0xF1, 0x03, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x4F, 0x00, 0xF1, 0xD2, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x4E, 0x00, 0xDA, 0xF9, 0x25, 0x15, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x12, 0x4F, 0x00, 0xF2, 0xF2, 0x60, 0x72, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x8A, 0x40, 0xF1, 0xF1, 0x11, 0xB3, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0xA8, 0xD6, 0x4C, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF8, 0xD6, 0xB5, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF7, 0xD6, 0xB5, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF5, 0xD6, 0xB5, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xDE, 0x00, 0x00, 0xF7, 0x10, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xB2, 0x03, 0x5D, 0xDA, 0x02, 0x18, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x67, 0x01, 0x00, 0x69, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xB1, 0x69, 0x00, 0x00, 0xC1, 0x02, 0x00, 0xB2, 0x69, 0x00, 0x00, 0xC2, 0x03, 0x00, 0xB3, 0x69, 0x00, 0x00, 0xC3, 0x05, 0x00, 0xB4, 0x69, 0x00, 0x00, 0xC4, 0x05, 0x00, 0xB5, 0x69, 0x00, 0x00, 0xC5, 0x02, 0x00, 0xBB, 0x69, 0x00, 0x00, 0xCB, 0x06, 0x00, 0xBC, 0x69, 0x00, 0x00, 0xCC, 0x07, 0x00, 0xBE, 0x69, 0x00, 0x00, 0xCE, 0x09, 0x00, 0xBF, 0x69, 0x00, 0x00, 0xCF, 0x0A, 0x55, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x2A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x33, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x33, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x2C, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x39, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0A, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x35, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x31, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x49, 0x78, 0x00, 0x92, 0x49, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x49, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x30, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x35, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, 0x35, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x58, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9C, 0x26, 0x5F, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x4B, 0x78, 0x00, 0xC1, 0x00, 0x00, 0x91, 0x43, 0x72, 0x00, 0xC2, 0x04, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x43, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x0A, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x20, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3C, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3C, 0x72, 0x00, 0x94, 0x33, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x33, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3C, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x43, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x95, 0x20, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x95, 0x25, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x25, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x45, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x39, 0x72, 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x45, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, 0x00, 0x00, 0x95, 0x24, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x95, 0x22, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3E, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x29, 0x00, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x3E, 0x72, 0x00, 0x94, 0x35, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x48, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x3E, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x95, 0x22, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x43, 0x65, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x43, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x43, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x27, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x43, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x95, 0x25, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x43, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x25, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x48, 0x65, 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x40, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x29, 0x00, 0x0B, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x48, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x48, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x95, 0x24, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x29, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x41, 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, 0x29, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x22, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x22, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x24, 0x00, 0x00, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x95, 0x25, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x25, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x41, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, 0x22, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x22, 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x43, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0C, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x95, 0x20, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x95, 0x20, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0xC0, 0x01, 0x00, 0x90, 0x3F, 0x65, 0x00, 0xC2, 0x03, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x00, 0xCF, 0x0B, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2C, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x41, 0x65, 0x00, 0x92, 0x41, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x95, 0x27, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x37, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x37, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x46, 0x65, 0x00, 0x92, 0x46, 0x72, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x37, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x27, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x48, 0x65, 0x00, 0x92, 0x48, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x46, 0x65, 0x00, 0x92, 0x46, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2C, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x4B, 0x65, 0x00, 0x92, 0x4B, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x30, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x30, 0x00, 0x00, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x41, 0x65, 0x00, 0x92, 0x41, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x31, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x90, 0x46, 0x65, 0x00, 0x92, 0x46, 0x72, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3D, 0x00, 0x00, 0x93, 0x41, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x33, 0x6E, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3D, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x95, 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x9C, 0x26, 0x5F, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3D, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0xC2, 0x03, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3A, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x9C, 0x26, 0x68, 0x0B, 0x26, 0x00, 0x00, 0x26, 0x6A, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x9C, 0x26, 0x6D, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3A, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0xC1, 0x02, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x00, 0xCF, 0x0A, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x33, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x33, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x2C, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x39, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x35, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x2E, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x31, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x49, 0x78, 0x00, 0x92, 0x49, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x49, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x30, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x35, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, 0x35, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x58, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x5F, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x4E, 0x78, 0x00, 0xC1, 0x00, 0x00, 0x91, 0x46, 0x72, 0x00, 0xC2, 0x04, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x65, 0x00, 0x9F, 0x2B, 0x58, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9F, 0x2B, 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9F, 0x2B, 0x58, 0x0A, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9F, 0x2B, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x0B, 0x92, 0x42, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2A, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x95, 0x23, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x36, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x36, 0x00, 0x00, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x95, 0x23, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x28, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x95, 0x28, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x95, 0x25, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x41, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x2C, 0x00, 0x0B, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, 0x41, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4E, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x41, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4E, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, 0x41, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x95, 0x25, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x95, 0x2A, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x28, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x28, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x93, 0x43, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x90, 0x58, 0x78, 0x00, 0x91, 0x50, 0x72, 0x00, 0x92, 0x4C, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x43, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x58, 0x00, 0x00, 0x91, 0x50, 0x00, 0x00, 0x92, 0x4C, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3F, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x95, 0x27, 0x00, 0x00, 0x93, 0x43, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x95, 0x2C, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x44, 0x72, 0x00, 0x94, 0x3F, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x93, 0x44, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, 0x25, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x25, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, 0x0B, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x28, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x95, 0x28, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, 0x44, 0x72, 0x00, 0x94, 0x40, 0x72, 0x00, 0x95, 0x25, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x93, 0x44, 0x00, 0x00, 0x94, 0x40, 0x00, 0x00, 0x95, 0x25, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x40, 0x72, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0E, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0E, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0E, 0x93, 0x42, 0x00, 0x00, 0x94, 0x40, 0x00, 0x00, 0x95, 0x2A, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3F, 0x72, 0x00, 0x95, 0x23, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x00, 0x9F, 0x2B, 0x58, 0x0E, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x00, 0x9F, 0x2B, 0x00, 0x52, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x95, 0x23, 0x00, 0x00, 0xFF, 0x2F, 0x00, 0xFF}; // lemmings_012_tim7.vgz - Lemmings: Ronda Alla Turca const unsigned char TIM7_VGZ[0x065B] = { 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xED, 0x5B, 0xCD, 0x6F, 0x1B, 0x45, 0x14, 0x7F, 0x6B, 0x77, 0x9D, 0x28, 0x87, 0xBA, 0xB2, 0x95, 0x9E, 0xDD, 0x18, 0x28, 0x0A, 0xD0, 0xAC, 0x93, 0x12, 0x04, 0xA7, 0x36, 0x31, 0x02, 0x09, 0xAA, 0xF2, 0x51, 0x15, 0xE9, 0x69, 0x57, 0xAA, 0x93, 0xB8, 0xAE, 0x45, 0xD2, 0x4A, 0xFD, 0xE0, 0xEB, 0x54, 0x28, 0x48, 0x48, 0xAC, 0x4F, 0x9C, 0x7C, 0x41, 0xA4, 0xFD, 0x07, 0x9C, 0xCD, 0x25, 0x7B, 0x40, 0x6A, 0x81, 0x7B, 0xFE, 0x00, 0x5F, 0x5A, 0x08, 0x12, 0x11, 0x17, 0x2E, 0xDC, 0x79, 0x33, 0xDE, 0xD9, 0xEC, 0xDA, 0xEB, 0xCD, 0x8E, 0xD7, 0x9B, 0x38, 0x81, 0xB5, 0x62, 0xAF, 0x67, 0xF6, 0xCD, 0xBE, 0xDF, 0x7B, 0x33, 0xBF, 0xDF, 0xCE, 0xB3, 0x72, 0xB5, 0xB6, 0x56, 0x58, 0x78, 0x15, 0xE0, 0x7D, 0x05, 0xDC, 0xE3, 0xD7, 0x39, 0x80, 0xBF, 0xBF, 0x9F, 0x86, 0x35, 0xE8, 0x7C, 0x76, 0x1F, 0xEF, 0x42, 0xFF, 0xA3, 0xF9, 0xC3, 0x3C, 0xC8, 0x1C, 0xA8, 0x14, 0xF0, 0xA7, 0xC7, 0x38, 0x0E, 0x78, 0x6D, 0x07, 0x97, 0xFF, 0xC1, 0xFB, 0x19, 0x7C, 0x30, 0x86, 0x4F, 0x53, 0xF8, 0x3B, 0xE0, 0xE3, 0x2C, 0x16, 0x00, 0x8B, 0x19, 0xBC, 0x00, 0xB8, 0x08, 0xD8, 0x3A, 0x89, 0x3F, 0xD6, 0xB0, 0x35, 0x85, 0x95, 0x5D, 0x5C, 0xD9, 0xC5, 0x2F, 0x55, 0xFC, 0x5A, 0xC5, 0x67, 0x29, 0xDC, 0x49, 0xE1, 0x13, 0x05, 0xCF, 0x28, 0xF8, 0x9C, 0x82, 0x17, 0x01, 0xCB, 0x80, 0x1B, 0x59, 0x5C, 0x6F, 0xE3, 0xC6, 0x39, 0x5C, 0xDA, 0xC5, 0xEA, 0x2E, 0x7E, 0xA5, 0xE2, 0x37, 0x2A, 0xFE, 0x96, 0xC2, 0x3F, 0x52, 0xF8, 0xB3, 0x82, 0x53, 0x0A, 0x3E, 0xAF, 0xE0, 0x02, 0xE0, 0x9B, 0x80, 0x56, 0x1A, 0x1F, 0xB6, 0xD1, 0x9A, 0xC5, 0xCD, 0x4C, 0xC5, 0x98, 0xC4, 0x8D, 0x93, 0xB8, 0x6E, 0x31, 0x43, 0x2B, 0x87, 0x0F, 0x2D, 0x6A, 0xAF, 0x68, 0x93, 0xD8, 0x4A, 0x39, 0xF7, 0x65, 0xBD, 0x35, 0xD1, 0x5B, 0x63, 0xBD, 0x7A, 0x90, 0x49, 0x69, 0x12, 0xAF, 0xED, 0xE2, 0xF2, 0x2E, 0xDE, 0x57, 0xF1, 0x41, 0x0A, 0x9F, 0x2A, 0x1C, 0x4B, 0x06, 0x0B, 0x0A, 0x16, 0x15, 0xBC, 0x30, 0x8E, 0x8B, 0xE3, 0x62, 0xCC, 0x17, 0xC4, 0x98, 0xB3, 0xDC, 0x9C, 0x79, 0x52, 0xD9, 0x9A, 0xC6, 0x56, 0xA6, 0xD3, 0x5B, 0x69, 0x4C, 0x47, 0x08, 0x4B, 0x46, 0xB8, 0x97, 0xC3, 0xF5, 0xF3, 0xB8, 0x31, 0xC3, 0x87, 0x3A, 0x8F, 0xD6, 0x1C, 0x73, 0xDE, 0x0D, 0x85, 0x95, 0x77, 0xC6, 0x37, 0x64, 0xDD, 0xF3, 0xA3, 0x73, 0x20, 0xB7, 0x45, 0x63, 0xDB, 0x81, 0x2C, 0x7C, 0xF6, 0x23, 0x6A, 0x32, 0x37, 0xCC, 0x38, 0x88, 0xB6, 0x39, 0x22, 0x72, 0x7E, 0x9B, 0x0D, 0xA5, 0x77, 0x10, 0x35, 0x45, 0x23, 0x1F, 0x3F, 0x2A, 0xA2, 0x26, 0xB6, 0x8A, 0xDC, 0xBC, 0x2A, 0xCC, 0xAB, 0x9E, 0x28, 0xF9, 0xC7, 0xA4, 0x1B, 0xB5, 0xD2, 0x02, 0x51, 0x96, 0x07, 0x61, 0x96, 0xF7, 0x52, 0x10, 0xE6, 0x19, 0x5E, 0xE6, 0x5B, 0x07, 0xE6, 0x69, 0x3E, 0x19, 0xE6, 0xB9, 0x89, 0x1B, 0x04, 0xEA, 0x2D, 0x8B, 0xDE, 0x32, 0xEB, 0x35, 0x82, 0x4C, 0xE8, 0xD6, 0x72, 0xD1, 0xB0, 0x84, 0xB9, 0xE5, 0xDC, 0xB1, 0x77, 0x4C, 0xB9, 0xE9, 0xD7, 0xE3, 0xA7, 0xDE, 0xC7, 0x4F, 0x1F, 0xB4, 0xB6, 0xE8, 0x6D, 0xB3, 0x5E, 0x3B, 0x4E, 0x7E, 0x7D, 0x37, 0x62, 0xCB, 0x90, 0x66, 0x8B, 0x9C, 0xFF, 0x7E, 0x67, 0x28, 0xCE, 0xDD, 0x6E, 0x53, 0xDE, 0xCF, 0xF2, 0x2B, 0x3B, 0x29, 0x16, 0xD1, 0x23, 0x57, 0x5B, 0x63, 0xA2, 0x37, 0xDB, 0x0D, 0x39, 0x12, 0x90, 0x31, 0x0F, 0x90, 0xA2, 0x30, 0x2F, 0x32, 0xF3, 0xAD, 0x88, 0x28, 0x2C, 0x81, 0xA2, 0x27, 0x89, 0xDD, 0xF3, 0x6A, 0x20, 0x14, 0x66, 0x3C, 0x14, 0x8D, 0xE9, 0xC8, 0x2B, 0xEB, 0x6C, 0x00, 0x8A, 0xEE, 0x5C, 0x8C, 0xF5, 0x45, 0x61, 0x8F, 0x02, 0x0A, 0x37, 0x17, 0xEE, 0x2A, 0x53, 0x45, 0x84, 0xBD, 0xF1, 0x77, 0x07, 0x77, 0xC9, 0xCD, 0xF1, 0x5C, 0x6E, 0xCE, 0xEC, 0xAF, 0x62, 0xB9, 0xC8, 0x2A, 0x96, 0xDF, 0x47, 0xC5, 0xF4, 0x50, 0x15, 0xD3, 0x82, 0x4C, 0x8C, 0x78, 0x2A, 0x66, 0x0E, 0x4F, 0xC5, 0xF4, 0x20, 0x15, 0x2B, 0x25, 0xA0, 0x62, 0x5A, 0xA8, 0x8A, 0xD9, 0xC3, 0x53, 0x31, 0x6D, 0x34, 0x54, 0xCC, 0x88, 0x42, 0xF5, 0x11, 0xD4, 0xA1, 0x94, 0x80, 0x8A, 0x19, 0xF1, 0x54, 0x4C, 0x8B, 0x22, 0xD0, 0x7E, 0xE1, 0x68, 0x1C, 0x4D, 0x15, 0xB3, 0x93, 0x54, 0x31, 0xF3, 0x58, 0xA8, 0x98, 0x1D, 0x4F, 0xC5, 0xB4, 0x49, 0xDF, 0xB4, 0x0C, 0x51, 0xB1, 0xC6, 0x91, 0x55, 0xB1, 0xAD, 0x44, 0x55, 0x8C, 0xC7, 0x21, 0x92, 0x8A, 0x65, 0xC8, 0x19, 0xB6, 0x7C, 0x24, 0x1E, 0x3C, 0x8A, 0x1E, 0xB0, 0x79, 0x47, 0x32, 0x18, 0xD8, 0xB4, 0x67, 0x71, 0x65, 0x70, 0x63, 0x6E, 0x8F, 0x15, 0x3D, 0xDA, 0x84, 0x1B, 0xF9, 0xEE, 0x5E, 0xB9, 0xB5, 0x9F, 0xE7, 0x6A, 0xD5, 0x31, 0x57, 0x05, 0x9B, 0xB9, 0x63, 0x9E, 0x96, 0xE4, 0x76, 0xD7, 0x25, 0x3F, 0x1C, 0x1F, 0x9B, 0x39, 0x51, 0xEA, 0x61, 0x33, 0x97, 0xF9, 0xB9, 0xA6, 0x0F, 0xCA, 0x66, 0x92, 0x91, 0xCF, 0x7A, 0x54, 0x52, 0x78, 0xD5, 0x9B, 0x0E, 0x6D, 0x32, 0x2C, 0x1D, 0x76, 0x92, 0xE9, 0xD0, 0x82, 0xD2, 0xA1, 0xC7, 0x4B, 0x47, 0x49, 0x2A, 0x1D, 0x56, 0x9C, 0x74, 0xB0, 0xB5, 0x10, 0x75, 0x2B, 0x91, 0x11, 0x19, 0x51, 0x45, 0x46, 0x6A, 0xCE, 0x93, 0xD2, 0x9E, 0x3E, 0x7A, 0x1F, 0x08, 0x29, 0x23, 0xAA, 0x9F, 0xB7, 0x45, 0x12, 0x1D, 0x6F, 0xBB, 0x7A, 0x05, 0x52, 0x73, 0x7A, 0x20, 0xB9, 0x17, 0xD1, 0x33, 0x82, 0xA2, 0xA7, 0x45, 0xCC, 0x88, 0xEA, 0x97, 0xFB, 0xFC, 0xDE, 0xC3, 0x2A, 0x6B, 0xF4, 0x03, 0x77, 0x74, 0xD3, 0x35, 0xD9, 0x16, 0x4F, 0x95, 0x65, 0x4F, 0x46, 0x58, 0x2F, 0x8F, 0xF0, 0xD0, 0x17, 0x48, 0x9F, 0x74, 0x68, 0x41, 0xE9, 0x30, 0x42, 0xD3, 0x61, 0x86, 0xA6, 0xA3, 0x31, 0x7A, 0xE9, 0xD0, 0x83, 0xD2, 0x61, 0x84, 0xA6, 0xC3, 0x83, 0x51, 0x0E, 0xD1, 0x30, 0x8B, 0x81, 0xB9, 0xBD, 0x6D, 0x54, 0xBF, 0xCA, 0x9E, 0x6C, 0x31, 0x50, 0x8B, 0xB7, 0x8D, 0xF2, 0xF2, 0x86, 0x3D, 0x7A, 0xC5, 0x40, 0x2D, 0x68, 0x1B, 0xA5, 0x87, 0x6E, 0xA3, 0x1A, 0x09, 0x17, 0x03, 0xB5, 0x78, 0xDB, 0x28, 0xE3, 0x40, 0x8A, 0x81, 0x5A, 0xBF, 0x2D, 0xCF, 0xB0, 0xB7, 0x51, 0x5A, 0xBC, 0x6D, 0x94, 0x21, 0x5F, 0x0C, 0x8C, 0x35, 0x63, 0x87, 0xBD, 0x8D, 0x3A, 0x62, 0x65, 0xB4, 0x3E, 0xDB, 0x28, 0xBD, 0x6B, 0x03, 0x72, 0x34, 0x8B, 0x81, 0x5E, 0x14, 0xA5, 0xD0, 0x6D, 0x94, 0x19, 0x8A, 0xC2, 0xFE, 0xBF, 0x18, 0x18, 0xB9, 0x18, 0x38, 0x2C, 0x15, 0xD3, 0x87, 0xA7, 0x62, 0xC7, 0xA3, 0x18, 0x18, 0x8B, 0xE5, 0x22, 0x14, 0x03, 0xF5, 0x78, 0x2A, 0x56, 0x3A, 0x5E, 0xC5, 0xC0, 0x24, 0x54, 0x2C, 0x7A, 0x31, 0xD0, 0x8C, 0xAD, 0x62, 0x8D, 0xD1, 0xF8, 0x49, 0x6B, 0xEB, 0x50, 0x55, 0x4C, 0x8B, 0xAC, 0x62, 0x66, 0x92, 0x25, 0xCD, 0x21, 0xAA, 0x98, 0x11, 0x43, 0xC5, 0x1A, 0x87, 0xAA, 0x62, 0x66, 0xA8, 0x8A, 0x79, 0x66, 0x91, 0x9C, 0xDA, 0x8E, 0x66, 0x31, 0xD0, 0x0C, 0x2D, 0x06, 0x36, 0x0E, 0xBC, 0x18, 0xA8, 0xC5, 0xAB, 0x3E, 0x19, 0x07, 0x58, 0x0C, 0x34, 0xE3, 0x15, 0x03, 0x0D, 0xF9, 0x62, 0x60, 0x78, 0x3A, 0xEC, 0x63, 0x57, 0x0C, 0x34, 0x25, 0x8B, 0x81, 0x31, 0xAB, 0x4F, 0xBA, 0x7C, 0xF5, 0x29, 0xD1, 0x62, 0x60, 0xBF, 0xE8, 0x0D, 0xBD, 0xFA, 0x54, 0x8A, 0x5C, 0x7D, 0xB2, 0xFF, 0xC3, 0xC5, 0x40, 0x3D, 0x28, 0x1D, 0xA5, 0x04, 0xD2, 0xA1, 0x47, 0xAE, 0xCD, 0xCA, 0x21, 0xCA, 0xB2, 0x41, 0x24, 0x25, 0xC3, 0x27, 0x10, 0x95, 0x3A, 0xAE, 0xD4, 0xB9, 0x84, 0x9D, 0xC0, 0x67, 0x69, 0xDC, 0x49, 0xE3, 0x93, 0x09, 0x3C, 0x03, 0x5C, 0xC2, 0x4E, 0x61, 0x79, 0x7C, 0x8F, 0xE5, 0x22, 0x95, 0x13, 0x89, 0x61, 0x66, 0xF0, 0x46, 0x1D, 0x3F, 0xAE, 0xE3, 0xB7, 0x2A, 0x7E, 0x77, 0x02, 0xFF, 0x4C, 0xE3, 0x5F, 0x69, 0xFC, 0x65, 0x02, 0x5F, 0x04, 0x7C, 0x49, 0xC1, 0xB7, 0x4F, 0xE1, 0x3B, 0xE3, 0xE4, 0x30, 0x3E, 0x6A, 0xE2, 0x26, 0x27, 0x4C, 0x2B, 0xCB, 0x73, 0x31, 0xE3, 0x7B, 0xF6, 0x66, 0x8D, 0x19, 0xD6, 0xA8, 0x7B, 0x2E, 0x90, 0xAA, 0x17, 0x31, 0x2B, 0xE6, 0x8C, 0x7C, 0x45, 0xA5, 0x33, 0x19, 0x1C, 0x73, 0xDC, 0xCC, 0x3B, 0xAE, 0x96, 0x3C, 0x9E, 0xE8, 0x41, 0xAE, 0x6A, 0x7E, 0x57, 0x7D, 0x17, 0x70, 0x4F, 0x64, 0x95, 0x48, 0x6C, 0x5A, 0xE5, 0x1F, 0xA7, 0x39, 0x04, 0xD7, 0x73, 0x7B, 0x40, 0xFA, 0x8D, 0x7B, 0xDF, 0x2E, 0x15, 0x1E, 0x4C, 0x02, 0x9C, 0xF0, 0x76, 0x50, 0x88, 0x5D, 0xBC, 0x57, 0x59, 0xE4, 0x96, 0x6A, 0x53, 0xAC, 0x3B, 0x95, 0x57, 0x0C, 0xC8, 0x55, 0x0B, 0x37, 0x39, 0x2F, 0x05, 0x2E, 0x55, 0x71, 0x25, 0x9F, 0xA8, 0x1E, 0x12, 0x93, 0xF0, 0xDF, 0x23, 0x2B, 0xD1, 0x5D, 0x75, 0x14, 0x9C, 0x3B, 0x13, 0x4C, 0xE9, 0x5E, 0x6A, 0xED, 0x60, 0xC9, 0xE1, 0xA3, 0x0C, 0x0B, 0xBB, 0x1E, 0x82, 0x65, 0x30, 0xCE, 0xE4, 0x41, 0x73, 0x63, 0x35, 0x98, 0x04, 0xB8, 0xEE, 0x39, 0xE4, 0xA3, 0x7A, 0x66, 0x85, 0x3A, 0x50, 0x9D, 0x44, 0x15, 0xAC, 0xEB, 0xA6, 0x32, 0xE3, 0xCC, 0x76, 0x89, 0x38, 0x3B, 0x94, 0x25, 0x16, 0xAF, 0xDA, 0x15, 0xB4, 0x58, 0x1E, 0x32, 0x66, 0xCE, 0x4B, 0x3D, 0x52, 0xFA, 0x7E, 0xC7, 0xDF, 0x9F, 0x99, 0x73, 0x09, 0x30, 0x73, 0x3E, 0x80, 0x99, 0xA3, 0xD0, 0xDD, 0x21, 0x32, 0xB3, 0x94, 0x88, 0x94, 0x82, 0x98, 0x59, 0x8A, 0x15, 0xED, 0xE1, 0x31, 0x73, 0x63, 0x04, 0x98, 0xD9, 0x8E, 0xCD, 0xCC, 0x8D, 0x24, 0x99, 0x59, 0x9F, 0x0C, 0xFE, 0x81, 0x7B, 0x88, 0xCC, 0x6C, 0x0F, 0xC4, 0xCC, 0x5E, 0xC9, 0x30, 0xA2, 0x31, 0xB3, 0x76, 0x74, 0x98, 0xD9, 0x4E, 0x80, 0x99, 0x1B, 0x03, 0x31, 0xB3, 0xEE, 0x61, 0x66, 0xAD, 0x2F, 0x33, 0x5F, 0x7F, 0x6B, 0x65, 0xAE, 0x00, 0x0A, 0xC0, 0x12, 0xFD, 0x5D, 0x81, 0x3A, 0xAC, 0x41, 0x01, 0x5E, 0x83, 0x37, 0xE8, 0xFD, 0x03, 0xB8, 0x05, 0x37, 0x61, 0x05, 0x2A, 0x74, 0x7E, 0x11, 0x56, 0xE9, 0xC5, 0xCE, 0xAE, 0xC0, 0x3D, 0xB8, 0x0D, 0xCB, 0x74, 0xDE, 0xF9, 0x0F, 0x9F, 0x2A, 0x59, 0xAC, 0x91, 0xDD, 0x4D, 0xA8, 0xC1, 0x1D, 0xDE, 0xF6, 0x1E, 0x2C, 0xD2, 0x75, 0x33, 0xF4, 0x77, 0x09, 0x3E, 0x84, 0x57, 0xA0, 0x0C, 0x97, 0xE9, 0x93, 0x1D, 0x97, 0x68, 0xC4, 0x2F, 0xC8, 0xF2, 0x36, 0xDC, 0xE5, 0xDF, 0x4B, 0xF0, 0x3A, 0xBD, 0x4A, 0x74, 0x76, 0x95, 0x5A, 0xD9, 0x1D, 0xAA, 0xF0, 0x39, 0xD9, 0x2D, 0xD0, 0x27, 0xFB, 0x06, 0x34, 0x12, 0xF3, 0xE1, 0x13, 0xFA, 0xCE, 0x6C, 0xAA, 0xE4, 0x4D, 0x01, 0xAE, 0xD3, 0xF9, 0x2D, 0xEE, 0x27, 0xF3, 0xA7, 0x4C, 0x7E, 0x5E, 0xE6, 0xAD, 0x75, 0x6E, 0xFF, 0x32, 0x9D, 0xAF, 0x52, 0x7F, 0x8D, 0x5E, 0x9D, 0xEB, 0x3F, 0xA5, 0x9E, 0xBB, 0x70, 0x83, 0x5F, 0x7B, 0x8B, 0x7C, 0x5C, 0xA0, 0xF7, 0xCF, 0xE0, 0x1C, 0x4C, 0xD0, 0x6B, 0x81, 0xC6, 0xB8, 0xE3, 0x5C, 0xC7, 0xEE, 0x54, 0x80, 0xA9, 0x7D, 0x71, 0x4F, 0xD1, 0xB7, 0x25, 0xEE, 0xE7, 0x47, 0x74, 0xE5, 0x2A, 0xDD, 0xB9, 0x46, 0xAD, 0x0C, 0x3F, 0xBB, 0x7E, 0x8D, 0xCE, 0x57, 0x68, 0xC4, 0x7B, 0x34, 0x6E, 0xA1, 0x07, 0xF1, 0xBF, 0x46, 0x65, 0x12, 0x63, 0x46, 0x35, 0x00, 0x00}; // lem_xmas_001_jb.dro - Xmas Lemmings: Jingle Bells const unsigned char JB_DRO[0x3100] = { 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0x02, 0x00, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, 0xC3, 0xAB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x7B, 0x7A, 0x01, 0x04, 0x05, 0x08, 0xBD, 0x20, 0x40, 0x60, 0x80, 0xE0, 0x21, 0x41, 0x61, 0x81, 0xE1, 0x22, 0x42, 0x62, 0x82, 0xE2, 0x23, 0x43, 0x63, 0x83, 0xE3, 0x24, 0x44, 0x64, 0x84, 0xE4, 0x25, 0x45, 0x65, 0x85, 0xE5, 0x28, 0x48, 0x68, 0x88, 0xE8, 0x29, 0x49, 0x69, 0x89, 0xE9, 0x2A, 0x4A, 0x6A, 0x8A, 0xEA, 0x2B, 0x4B, 0x6B, 0x8B, 0xEB, 0x2C, 0x4C, 0x6C, 0x8C, 0xEC, 0x2D, 0x4D, 0x6D, 0x8D, 0xED, 0x30, 0x50, 0x70, 0x90, 0xF0, 0x31, 0x51, 0x71, 0x91, 0xF1, 0x32, 0x52, 0x72, 0x92, 0xF2, 0x33, 0x53, 0x73, 0x93, 0xF3, 0x34, 0x54, 0x74, 0x94, 0xF4, 0x35, 0x55, 0x75, 0x95, 0xF5, 0xA0, 0xB0, 0xC0, 0xA1, 0xB1, 0xC1, 0xA2, 0xB2, 0xC2, 0xA3, 0xB3, 0xC3, 0xA4, 0xB4, 0xC4, 0xA5, 0xB5, 0xC5, 0xA6, 0xB6, 0xC6, 0xA7, 0xB7, 0xC7, 0xA8, 0xB8, 0xC8, 0x4B, 0x02, 0x5A, 0x03, 0x4D, 0x99, 0x5C, 0x59, 0x4E, 0x06, 0x5D, 0x07, 0x77, 0x23, 0x79, 0x0B, 0x4F, 0x01, 0x5E, 0x02, 0x78, 0x32, 0x77, 0x44, 0x7A, 0x0C, 0x77, 0x67, 0x7A, 0x0C, 0x77, 0x8B, 0x7A, 0x0D, 0x77, 0xB2, 0x7A, 0x0D, 0x77, 0xDB, 0x7A, 0x0D, 0x78, 0x12, 0x7A, 0xA3, 0x77, 0x65, 0x7A, 0x00, 0x78, 0x2F, 0x7A, 0x0C, 0x77, 0x99, 0x7A, 0x0D, 0x77, 0xCF, 0x7A, 0x0D, 0x77, 0x05, 0x78, 0x32, 0x7A, 0x0C, 0x77, 0x23, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0D, 0x78, 0x12, 0x77, 0xCF, 0x78, 0x2F, 0x7A, 0x0D, 0x77, 0x99, 0x7A, 0x0C, 0x77, 0x65, 0x7A, 0x0D, 0x77, 0x34, 0x7A, 0x0D, 0x77, 0x06, 0x7A, 0x0C, 0x77, 0xDB, 0x7A, 0x00, 0x78, 0x2E, 0x7A, 0x1A, 0x77, 0xB2, 0x7A, 0x0D, 0x77, 0x8B, 0x7A, 0x0C, 0x77, 0x67, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0D, 0x77, 0x23, 0x7A, 0x0D, 0x78, 0x0E, 0x7B, 0x02, 0x7A, 0x7B, 0x5C, 0x89, 0x4E, 0x36, 0x5D, 0x47, 0x4F, 0x03, 0x5E, 0x00, 0x79, 0x00, 0x4B, 0x22, 0x5A, 0x21, 0x7A, 0x00, 0x77, 0xB2, 0x78, 0x22, 0x7A, 0x6C, 0x77, 0xDB, 0x7A, 0x0D, 0x77, 0x06, 0x78, 0x23, 0x7A, 0x0C, 0x77, 0x34, 0x7A, 0x0D, 0x77, 0x65, 0x7A, 0x0D, 0x77, 0x99, 0x7A, 0x0D, 0x77, 0xCF, 0x7A, 0x0C, 0x77, 0x05, 0x78, 0x26, 0x7A, 0x1B, 0x77, 0x23, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0C, 0x77, 0x67, 0x7A, 0x0D, 0x77, 0x8B, 0x7A, 0x0D, 0x77, 0xB2, 0x7A, 0x0C, 0x77, 0xDB, 0x7A, 0x0D, 0x77, 0x06, 0x78, 0x27, 0x7A, 0x0D, 0x78, 0x07, 0x7B, 0x03, 0x7A, 0x13, 0x00, 0x20, 0x04, 0xC0, 0x07, 0xF2, 0x16, 0xF2, 0x08, 0xF2, 0x17, 0xF2, 0x61, 0x08, 0x05, 0x27, 0x14, 0x32, 0x7A, 0x00, 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, 0x60, 0x33, 0x0C, 0x53, 0x1B, 0xAA, 0x0D, 0x5A, 0x1C, 0x1A, 0x64, 0x0C, 0x0A, 0x21, 0x19, 0x21, 0x0B, 0x97, 0x1A, 0x80, 0x11, 0x53, 0x20, 0xAA, 0x12, 0x5A, 0x21, 0x1A, 0x67, 0x0C, 0x7A, 0x00, 0x0F, 0x21, 0x1E, 0x21, 0x10, 0x97, 0x1F, 0x80, 0x25, 0x53, 0x34, 0xAA, 0x26, 0x5A, 0x35, 0x1A, 0x6A, 0x0C, 0x23, 0x21, 0x32, 0x21, 0x24, 0x97, 0x33, 0x80, 0x68, 0xB2, 0x69, 0x2A, 0x2A, 0xE2, 0x39, 0xE3, 0x2B, 0xF3, 0x7A, 0x00, 0x3A, 0xF3, 0x2C, 0x02, 0x6D, 0x0E, 0x28, 0x26, 0x37, 0x32, 0x29, 0x42, 0x38, 0x0E, 0x6B, 0xB2, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x45, 0x60, 0x13, 0x60, 0x33, 0x62, 0x05, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0A, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xD9, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x68, 0xB2, 0x69, 0x2A, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0E, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x0A, 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDA, 0x63, 0x2E, 0x66, 0x2E, 0x7A, 0x00, 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x44, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x69, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x69, 0x0B, 0x7A, 0x6D, 0x60, 0x13, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x7A, 0x36, 0x63, 0x0E, 0x66, 0x0E, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x62, 0x65, 0x63, 0x2B, 0x69, 0x0F, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x69, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x62, 0x05, 0x63, 0x2E, 0x7A, 0x00, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x68, 0x06, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0x00, 0x62, 0xCF, 0x63, 0x2B, 0x65, 0x44, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0B, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x69, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x2B, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0B, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x68, 0x05, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x13, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x5F, 0x05, 0x60, 0x36, 0x62, 0x05, 0x63, 0x2E, 0x65, 0x8B, 0x66, 0x2E, 0x7A, 0x00, 0x69, 0x0A, 0x68, 0x06, 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x7A, 0x00, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDA, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x2E, 0x7A, 0x00, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x68, 0xB2, 0x69, 0x2A, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x2E, 0x7A, 0x00, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0E, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0E, 0x69, 0x0F, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x0A, 0x63, 0x2E, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x44, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x69, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x69, 0x0B, 0x7A, 0x6D, 0x60, 0x13, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x33, 0x7A, 0x35, 0x63, 0x0E, 0x66, 0x0E, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x62, 0x65, 0x63, 0x2B, 0x7A, 0x00, 0x69, 0x0F, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x69, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x05, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x2A, 0x65, 0x05, 0x66, 0x2E, 0x68, 0x05, 0x69, 0x32, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0A, 0x66, 0x0E, 0x69, 0x12, 0x7A, 0xA4, 0x60, 0x16, 0x60, 0x36, 0x63, 0x2A, 0x66, 0x2E, 0x69, 0x32, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0A, 0x66, 0x0E, 0x69, 0x12, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, 0x62, 0x99, 0x63, 0x27, 0x65, 0x99, 0x66, 0x2B, 0x68, 0x99, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x66, 0x0B, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x62, 0x06, 0x63, 0x27, 0x65, 0x06, 0x66, 0x2B, 0x7A, 0x00, 0x68, 0x06, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x66, 0x0B, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0xB2, 0x60, 0x32, 0x62, 0xB2, 0x63, 0x26, 0x65, 0xB2, 0x66, 0x2A, 0x68, 0xB2, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x06, 0x66, 0x0A, 0x69, 0x0E, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, 0x17, 0x65, 0x09, 0x01, 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, 0x15, 0x00, 0x60, 0x12, 0x5F, 0x05, 0x60, 0x2A, 0x7A, 0x00, 0x0C, 0x52, 0x1B, 0x32, 0x0D, 0x18, 0x1C, 0x18, 0x64, 0x09, 0x0A, 0x64, 0x19, 0x24, 0x0B, 0x0A, 0x1A, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x11, 0x52, 0x20, 0x32, 0x12, 0x18, 0x21, 0x18, 0x67, 0x09, 0x0F, 0x64, 0x1E, 0x24, 0x7A, 0x00, 0x10, 0x0A, 0x1F, 0x0A, 0x66, 0x2A, 0x25, 0x52, 0x34, 0x32, 0x26, 0x18, 0x35, 0x18, 0x6A, 0x09, 0x23, 0x64, 0x32, 0x24, 0x24, 0x0A, 0x33, 0x0A, 0x68, 0x65, 0x69, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x45, 0x60, 0x0A, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x2B, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0A, 0x7A, 0xA4, 0x5F, 0x65, 0x60, 0x27, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x07, 0x5F, 0x99, 0x60, 0x27, 0x7A, 0xA3, 0x60, 0x07, 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x44, 0x63, 0x2A, 0x66, 0x0A, 0x66, 0x2A, 0x69, 0x0B, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x8B, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x8B, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, 0x66, 0x2B, 0x69, 0x0B, 0x68, 0x05, 0x69, 0x2E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x0E, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x0E, 0x7A, 0xA4, 0x60, 0x2E, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x0A, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0B, 0x65, 0x65, 0x66, 0x2B, 0x69, 0x0E, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0B, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x69, 0x0E, 0x68, 0x65, 0x69, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x44, 0x63, 0x2A, 0x66, 0x0A, 0x7A, 0x00, 0x66, 0x2A, 0x69, 0x0B, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x60, 0x2A, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x8B, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, 0x66, 0x2B, 0x69, 0x0B, 0x68, 0x05, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x60, 0x2E, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x60, 0x2E, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2E, 0x25, 0xF2, 0x34, 0xF2, 0x26, 0xF2, 0x35, 0xF2, 0x7A, 0x00, 0x6A, 0x08, 0x23, 0x27, 0x32, 0x32, 0x24, 0x4F, 0x33, 0x08, 0x69, 0x0E, 0x69, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x5F, 0x44, 0x60, 0x2E, 0x63, 0x0E, 0x62, 0x44, 0x63, 0x2E, 0x66, 0x0B, 0x69, 0x16, 0x68, 0x44, 0x69, 0x36, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x5F, 0x05, 0x60, 0x2E, 0x7A, 0x00, 0x63, 0x0E, 0x62, 0x05, 0x63, 0x2E, 0x69, 0x16, 0x68, 0x05, 0x69, 0x36, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x0E, 0x62, 0x99, 0x63, 0x2B, 0x69, 0x16, 0x68, 0x99, 0x69, 0x33, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x7A, 0x00, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x0B, 0x62, 0x06, 0x63, 0x2B, 0x69, 0x13, 0x68, 0x06, 0x69, 0x33, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x2A, 0x69, 0x13, 0x68, 0xB2, 0x69, 0x32, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x0A, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x69, 0x12, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x07, 0xF2, 0x16, 0xF2, 0x08, 0xF2, 0x17, 0xF2, 0x09, 0x00, 0x18, 0x00, 0x61, 0x08, 0x7A, 0x00, 0x05, 0x27, 0x14, 0x32, 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, 0x60, 0x33, 0x0C, 0xE9, 0x1B, 0xE9, 0x0D, 0x05, 0x1C, 0x04, 0x0E, 0x01, 0x64, 0x06, 0x0A, 0x01, 0x19, 0x01, 0x0B, 0x08, 0x1A, 0x08, 0x63, 0x26, 0x7A, 0x00, 0x1F, 0x16, 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x23, 0x00, 0x32, 0x00, 0x24, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x07, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x7A, 0x00, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x7A, 0x00, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x7A, 0xA4, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x25, 0xE4, 0x34, 0xF5, 0x7A, 0x00, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x5F, 0x05, 0x60, 0x36, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x05, 0x63, 0x2A, 0x7A, 0x00, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0A, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x33, 0x63, 0x06, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x16, 0x5F, 0xB2, 0x7A, 0x00, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x7A, 0x00, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x07, 0x62, 0x99, 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x07, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x60, 0x36, 0x63, 0x0A, 0x63, 0x2E, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x62, 0x99, 0x63, 0x27, 0x7A, 0x00, 0x66, 0x0A, 0x65, 0x8B, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x2B, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0xB2, 0x60, 0x32, 0x7A, 0x00, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x66, 0x0A, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, 0x17, 0x65, 0x09, 0x01, 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, 0x15, 0x00, 0x60, 0x12, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x7A, 0x00, 0x1F, 0x0A, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x7A, 0x00, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA3, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0A, 0x66, 0x0A, 0x69, 0x03, 0x7A, 0xA4, 0x5F, 0x65, 0x60, 0x27, 0x63, 0x06, 0x65, 0x65, 0x66, 0x27, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x07, 0x5F, 0x99, 0x60, 0x27, 0x66, 0x07, 0x65, 0x99, 0x66, 0x27, 0x69, 0x02, 0x7A, 0x00, 0x69, 0x22, 0x7A, 0xA3, 0x60, 0x07, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x66, 0x07, 0x65, 0x05, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0x99, 0x7A, 0x00, 0x63, 0x27, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x07, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, 0x7A, 0x00, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x27, 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x07, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x27, 0x69, 0x02, 0x7A, 0xA3, 0x63, 0x07, 0x63, 0x27, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x69, 0x03, 0x7A, 0xA4, 0x60, 0x0A, 0x66, 0x0A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x2A, 0x63, 0x27, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0B, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x07, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x62, 0x05, 0x63, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x8B, 0x60, 0x2A, 0x63, 0x0A, 0x66, 0x0B, 0x65, 0x8B, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x63, 0x2A, 0x69, 0x23, 0x7A, 0xA3, 0x63, 0x0A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x2A, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x0A, 0x63, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA3, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0A, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x63, 0x0A, 0x66, 0x0E, 0x7A, 0xA3, 0x60, 0x2E, 0x7A, 0x00, 0x63, 0x2A, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x0A, 0x7A, 0xA4, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x2A, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x7A, 0x00, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x0A, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x62, 0xB2, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x06, 0x66, 0x0B, 0x65, 0x65, 0x66, 0x2B, 0x69, 0x03, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x7A, 0x00, 0x69, 0x02, 0x7A, 0xA3, 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0x00, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0x05, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x7A, 0x00, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x0A, 0x60, 0x2A, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x7A, 0x00, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x62, 0x99, 0x63, 0x27, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x07, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x63, 0x27, 0x69, 0x23, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0x00, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x27, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x07, 0x63, 0x27, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x0A, 0x60, 0x2A, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2A, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0B, 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x7A, 0xA2, 0x60, 0x0B, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x0A, 0x66, 0x0B, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x2A, 0x69, 0x23, 0x7A, 0xA4, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, 0x66, 0x0E, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x2A, 0x69, 0x02, 0x7A, 0xA3, 0x60, 0x0E, 0x7A, 0x00, 0x60, 0x2E, 0x63, 0x0A, 0x63, 0x2A, 0x66, 0x0E, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, 0x66, 0x0E, 0x66, 0x2E, 0x69, 0x02, 0x7A, 0xA4, 0x60, 0x0E, 0x5F, 0x44, 0x60, 0x2E, 0x62, 0x44, 0x63, 0x2A, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0E, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x0A, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0B, 0x65, 0x06, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x02, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x63, 0x06, 0x69, 0x02, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0A, 0x66, 0x0A, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x69, 0x02, 0x69, 0x22, 0x7A, 0x6D, 0x69, 0x02, 0x69, 0x22, 0x7A, 0x6D, 0x07, 0xF2, 0x16, 0xF2, 0x08, 0xF2, 0x17, 0xF2, 0x09, 0x00, 0x18, 0x00, 0x61, 0x08, 0x05, 0x27, 0x14, 0x32, 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x7A, 0x00, 0x1F, 0x16, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x7A, 0x00, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x33, 0x63, 0x06, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x16, 0x7A, 0x00, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x7A, 0x00, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x63, 0x27, 0x66, 0x0A, 0x7A, 0x00, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x7A, 0xA3, 0x66, 0x0A, 0x66, 0x2E, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x5F, 0x05, 0x60, 0x36, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x05, 0x63, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0A, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x7A, 0x00, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x66, 0x0B, 0x7A, 0x00, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x12, 0x7A, 0x00, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x7A, 0x00, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x7A, 0x00, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x07, 0x62, 0x99, 0x63, 0x27, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x07, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x7A, 0x00, 0x60, 0x36, 0x63, 0x0A, 0x63, 0x2E, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x8B, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x7A, 0x00, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x2B, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x66, 0x0A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, 0x17, 0x65, 0x09, 0x01, 0x7A, 0x00, 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, 0x15, 0x00, 0x60, 0x12, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x1F, 0x0A, 0x7A, 0x00, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36}; // rudolph.dro - Rudolph, the red-nosed Reindeer (Recording of RUDOLPH.CMF) const unsigned char RODOLPH_DRO[0x1451] = { 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0xE2, 0x36, 0x01, 0x00, 0x40, 0x14, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x20, 0x02, 0x08, 0x04, 0x08, 0x0B, 0x08, 0x0D, 0x08, 0x0F, 0x08, 0x16, 0x08, 0x18, 0x08, 0x1A, 0x08, 0x20, 0x01, 0x21, 0x01, 0x22, 0x01, 0x23, 0x11, 0x24, 0x11, 0x25, 0x11, 0x28, 0x01, 0x29, 0x01, 0x2A, 0x01, 0x2B, 0x11, 0x2C, 0x11, 0x2D, 0x11, 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x33, 0x11, 0x34, 0x11, 0x35, 0x11, 0x40, 0x4F, 0x41, 0x4F, 0x42, 0x4F, 0x48, 0x4F, 0x49, 0x4F, 0x4A, 0x4F, 0x50, 0x4F, 0x51, 0x4F, 0x52, 0x4F, 0x60, 0xF1, 0x61, 0xF1, 0x62, 0xF1, 0x63, 0xD2, 0x64, 0xF2, 0x65, 0xF2, 0x68, 0xF1, 0x69, 0xF1, 0x6A, 0xF1, 0x6B, 0xF2, 0x6C, 0xF2, 0x6D, 0xF2, 0x70, 0xF1, 0x71, 0xF1, 0x72, 0xF1, 0x73, 0xF2, 0x74, 0xF2, 0x75, 0xF2, 0x80, 0x53, 0x81, 0x53, 0x82, 0x53, 0x83, 0x74, 0x84, 0x74, 0x85, 0x74, 0x88, 0x53, 0x89, 0x53, 0x8A, 0x53, 0x8B, 0x74, 0x8C, 0x74, 0x8D, 0x74, 0x90, 0x53, 0x91, 0x53, 0x92, 0x53, 0x93, 0x74, 0x94, 0x74, 0x95, 0x74, 0xA0, 0x02, 0xA1, 0x81, 0xA2, 0x87, 0xBD, 0xE0, 0xB0, 0x36, 0x21, 0x01, 0x24, 0x11, 0x41, 0x4F, 0x61, 0xF1, 0x64, 0xD2, 0x81, 0x53, 0x84, 0x74, 0xE1, 0x00, 0x00, 0x05, 0xE4, 0x00, 0xC1, 0x00, 0x44, 0x00, 0xA1, 0x81, 0xB1, 0x35, 0x22, 0x01, 0x25, 0x11, 0x42, 0x4F, 0x00, 0x05, 0x62, 0xF1, 0x65, 0xD2, 0x82, 0x53, 0x85, 0x74, 0xE2, 0x00, 0xE5, 0x00, 0xC2, 0x00, 0x45, 0x00, 0xA2, 0x87, 0x00, 0x05, 0xB2, 0x32, 0x00, 0xCB, 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0x41, 0xB0, 0x36, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0x02, 0xB0, 0x36, 0x01, 0xBF, 0x01, 0xA1, 0x81, 0xB1, 0x15, 0xA2, 0x87, 0xB2, 0x12, 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0xCA, 0xB0, 0x35, 0x00, 0x05, 0x44, 0x00, 0xA1, 0x57, 0xB1, 0x35, 0x45, 0x00, 0xA2, 0x41, 0xB2, 0x32, 0x00, 0xD3, 0xA0, 0xCA, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x02, 0xB0, 0x36, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0xCA, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0xA1, 0x57, 0xB1, 0x15, 0xA2, 0x41, 0xB2, 0x12, 0xA0, 0xCA, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0xB0, 0x00, 0x05, 0xB0, 0x35, 0x44, 0x00, 0xA1, 0x87, 0xB1, 0x32, 0x45, 0x00, 0xA2, 0x02, 0xB2, 0x32, 0x00, 0xD4, 0xA0, 0xB0, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0xCA, 0xB0, 0x35, 0x00, 0xE4, 0xA0, 0xCA, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0xB0, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA1, 0x87, 0xB1, 0x12, 0xA2, 0x02, 0xB2, 0x12, 0xA0, 0xB0, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x35, 0x44, 0x00, 0xA1, 0x41, 0xB1, 0x32, 0x45, 0x00, 0xA2, 0xCA, 0xB2, 0x31, 0x01, 0x79, 0x03, 0xA0, 0x81, 0xB0, 0x15, 0xA1, 0x41, 0xB1, 0x12, 0xA2, 0xCA, 0xB2, 0x11, 0x43, 0x00, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x44, 0x00, 0xA1, 0x02, 0xB1, 0x32, 0x45, 0x00, 0xA2, 0xB0, 0xB2, 0x31, 0x00, 0xD4, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x81, 0xB0, 0x35, 0x00, 0xD9, 0xA0, 0x81, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x57, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x12, 0xA2, 0xB0, 0xB2, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x44, 0x00, 0xA1, 0xE5, 0xB1, 0x31, 0x45, 0x00, 0xA2, 0x98, 0xB2, 0x31, 0x00, 0xD4, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x00, 0xA0, 0x57, 0xB0, 0x35, 0x00, 0xE4, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x87, 0xB0, 0x32, 0x01, 0xB5, 0x01, 0xA1, 0xE5, 0xB1, 0x11, 0xA2, 0x98, 0xB2, 0x11, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x00, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0x05, 0x44, 0x00, 0xA1, 0xCA, 0xB1, 0x31, 0x45, 0x00, 0xA2, 0x81, 0xB2, 0x31, 0x01, 0xB9, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x00, 0xA0, 0x81, 0xB0, 0x35, 0x01, 0xBE, 0x01, 0xA1, 0xCA, 0xB1, 0x11, 0xA2, 0x81, 0xB2, 0x11, 0xA0, 0x81, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x02, 0x00, 0x05, 0xB0, 0x32, 0x44, 0x00, 0xA1, 0xCA, 0xB1, 0x31, 0x45, 0x00, 0xA2, 0x87, 0xB2, 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA1, 0xCA, 0xB1, 0x11, 0xA2, 0x87, 0xB2, 0x0E, 0x20, 0x32, 0x23, 0x61, 0x00, 0x05, 0x40, 0x9A, 0x60, 0x51, 0x63, 0xA2, 0x80, 0x1B, 0x83, 0x3B, 0xE0, 0x00, 0xE3, 0x00, 0xC0, 0x00, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD8, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x21, 0x31, 0x24, 0x61, 0x41, 0x1C, 0x61, 0x41, 0x00, 0x05, 0x64, 0x92, 0x81, 0x0B, 0x84, 0x3B, 0xE1, 0x00, 0xE4, 0x00, 0xC1, 0x00, 0x44, 0x80, 0xA1, 0xB0, 0x00, 0x05, 0xB1, 0x31, 0x22, 0x31, 0x25, 0x61, 0x42, 0x1C, 0x62, 0x41, 0x65, 0x92, 0x82, 0x0B, 0x85, 0x3B, 0xE2, 0x00, 0x00, 0x05, 0xE5, 0x00, 0xC2, 0x00, 0x45, 0x80, 0xA2, 0x41, 0xB2, 0x2E, 0x28, 0x01, 0x2B, 0x11, 0x48, 0x4F, 0x00, 0x05, 0x68, 0xF1, 0x6B, 0xD2, 0x88, 0x53, 0x8B, 0x74, 0xE8, 0x00, 0xEB, 0x00, 0xC3, 0x00, 0x4B, 0x00, 0xA3, 0xCA, 0x00, 0x05, 0xB3, 0x2D, 0x00, 0xC6, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0xB0, 0xB1, 0x11, 0xA2, 0x41, 0xB2, 0x0E, 0xA3, 0xCA, 0xB3, 0x0D, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0xB0, 0xB3, 0x2D, 0x00, 0xD8, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0xB4, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x00, 0xE4, 0xA1, 0x81, 0xB1, 0x11, 0xA2, 0x02, 0xB2, 0x0E, 0xA3, 0xB0, 0xB3, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0xCA, 0x00, 0x05, 0xB2, 0x2D, 0x4B, 0x00, 0xA3, 0x81, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0x57, 0xB1, 0x11, 0xA2, 0xCA, 0xB2, 0x0D, 0xA3, 0x81, 0xB3, 0x0D, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x87, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0x57, 0xB3, 0x2D, 0x00, 0xCD, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0x9A, 0x02, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0xB0, 0xB2, 0x0D, 0xA3, 0x57, 0xB3, 0x0D, 0xA0, 0x02, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x44, 0x80, 0xA1, 0xB0, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x41, 0xB2, 0x2E, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0xCA, 0xB3, 0x2D, 0x00, 0xD8, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB5, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xE4, 0xA1, 0xB0, 0xB1, 0x11, 0xA2, 0x41, 0xB2, 0x0E, 0xA3, 0xCA, 0xB3, 0x0D, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0xB0, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0xBE, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0x81, 0xB1, 0x11, 0xA2, 0x02, 0xB2, 0x0E, 0xA3, 0xB0, 0xB3, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0xCA, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0x81, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0x57, 0xB1, 0x11, 0xA2, 0xCA, 0xB2, 0x0D, 0xA3, 0x81, 0xB3, 0x0D, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x87, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0x57, 0xB3, 0x2D, 0x00, 0xD8, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0x99, 0x02, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0xB0, 0xB2, 0x0D, 0xA3, 0x57, 0xB3, 0x0D, 0xA0, 0x02, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x29, 0xB2, 0x2C, 0xB1, 0x49, 0xCD, 0x69, 0x91, 0x6C, 0x91, 0x00, 0x05, 0x89, 0x2A, 0x8C, 0x2A, 0xE9, 0x00, 0xEC, 0x00, 0xC4, 0x00, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x41, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0x57, 0xB2, 0x2D, 0x01, 0xAD, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xB9, 0x01, 0xA1, 0x41, 0xB1, 0x0E, 0xA2, 0x57, 0xB2, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x81, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x20, 0xB1, 0x2E, 0x00, 0x05, 0x45, 0x80, 0xA2, 0x87, 0xB2, 0x2A, 0x01, 0xB3, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x81, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0x81, 0xB4, 0x31, 0x01, 0xAE, 0x01, 0xA1, 0x20, 0xB1, 0x0E, 0xA2, 0x87, 0xB2, 0x0A, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x81, 0xB4, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x2A, 0xB2, 0x2D, 0xB1, 0x00, 0x05, 0x4A, 0xCD, 0x6A, 0x91, 0x6D, 0x91, 0x8A, 0x2A, 0x8D, 0x2A, 0xEA, 0x00, 0xED, 0x00, 0xC5, 0x00, 0x4D, 0x80, 0x00, 0x05, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2A, 0x01, 0xB8, 0x01, 0xA1, 0x41, 0xB1, 0x0A, 0x44, 0x80, 0xA1, 0xB0, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA1, 0xB0, 0xB1, 0x0D, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2E, 0x01, 0xB4, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDE, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x00, 0x05, 0x4D, 0x80, 0xA5, 0xB0, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0xB0, 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0A, 0xA0, 0x57, 0xB0, 0x15, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0xB0, 0x00, 0x05, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0xB0, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x01, 0xAF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x00, 0xDE, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xA8, 0x01, 0xA5, 0x57, 0xB5, 0x11, 0x4D, 0x80, 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0xA5, 0x41, 0xB5, 0x0E, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x02, 0xB1, 0x2A, 0x45, 0x80, 0xA2, 0xCA, 0xB2, 0x2D, 0x01, 0x6F, 0x03, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0xA2, 0xCA, 0x00, 0x05, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xAF, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0x00, 0x05, 0xB4, 0x31, 0x01, 0xAE, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA1, 0x57, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE5, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xB9, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, 0xA2, 0x98, 0xB2, 0x2D, 0x01, 0xAF, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA2, 0x98, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBE, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB5, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD3, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE5, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x00, 0xD9, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xDA, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x01, 0xB9, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xB4, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x01, 0xB5, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xE4, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0x98, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0x00, 0x05, 0xB5, 0x2E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xA9, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA4, 0x98, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0x00, 0x05, 0xA0, 0xB0, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x57, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xB4, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0x57, 0xB4, 0x11, 0xA5, 0x41, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x57, 0x00, 0x05, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xB9, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA5, 0x41, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD3, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xE4, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xAE, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB5, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0x00, 0x05, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD3, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xE4, 0xA1, 0x57, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xB9, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, 0xA2, 0x98, 0xB2, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA2, 0x98, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xAE, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBF, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xB4, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0xCA, 0x00, 0x05, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xDF, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xD9, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xE4, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xAF, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x01, 0xBE, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xAF, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x00, 0xCD, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA0, 0x81, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x44, 0x80, 0xA1, 0x57, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB9, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x57, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xAF, 0x01, 0xA4, 0xCA, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x44, 0x80, 0xA1, 0xB0, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB8, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x02, 0xB4, 0x12, 0xA1, 0xB0, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0x00, 0x05, 0xA2, 0xCA, 0xB2, 0x2D, 0x01, 0xB3, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB5, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x31, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA2, 0xCA, 0xB2, 0x0D, 0xA4, 0xCA, 0xB4, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xAE, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA1, 0x81, 0xB1, 0x11, 0xA0, 0xB0, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x6B, 0x00, 0x05, 0xB1, 0x31, 0x01, 0x93, 0x02, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x00, 0xDA, 0xA2, 0xB0, 0xB2, 0x0D, 0xA0, 0x02, 0xB0, 0x12, 0xA1, 0x6B, 0xB1, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x81, 0xB2, 0x2D, 0x01, 0xB9, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x87, 0xB1, 0x2E, 0x01, 0xAE, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA2, 0x81, 0xB2, 0x0D, 0xA1, 0x87, 0xB1, 0x0E, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0x00, 0x05, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0x57, 0xB2, 0x2D, 0x00, 0xD4, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2E, 0x00, 0xE4, 0xA1, 0x41, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xB5, 0x01, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xBF, 0x01, 0xA2, 0x57, 0xB2, 0x0D, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x63, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0x6B, 0x00, 0x05, 0xB2, 0x2D, 0x01, 0xB8, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA1, 0x63, 0xB1, 0x0E, 0xA2, 0x6B, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x87, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB4, 0x01, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0x02, 0xB2, 0x0E, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x02, 0x00, 0x05, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0xA1, 0x02, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0xA1, 0x02, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x20, 0xB1, 0x2E, 0x01, 0xA8, 0x01, 0xA1, 0x20, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0xB0, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0xA1, 0xB0, 0xB1, 0x0D, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xB0, 0x00, 0x05, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xB0, 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xB0, 0xB5, 0x31, 0x01, 0xAF, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xB0, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB2, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0xCA, 0x00, 0x05, 0xB1, 0x2D, 0x45, 0x80, 0xA2, 0x81, 0xB2, 0x2D, 0x01, 0xA9, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0xCA, 0xB1, 0x0D, 0xA2, 0x81, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xDE, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0xCA, 0xB1, 0x2D, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2A, 0x01, 0x94, 0x02, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0xCA, 0xB1, 0x0D, 0xA2, 0x02, 0xB2, 0x0A, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xAE, 0x01, 0xA1, 0x02, 0xB1, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xDE, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xAE, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x01, 0xAE, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA5, 0x81, 0xB5, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA1, 0x57, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0x00, 0x05, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xB8, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, 0xA2, 0x98, 0xB2, 0x2D, 0x01, 0xAF, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA2, 0x98, 0xB2, 0x0D, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0x87, 0xB5, 0x0E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB4, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0xCA, 0x00, 0x05, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xD9, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x01, 0xB9, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xB5, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBE, 0x01, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB3, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0xB4, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0x68, 0x03, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0x79, 0x03, 0xA1, 0x02, 0xB1, 0x0A, 0xA0, 0x81, 0xB0, 0x15, 0xA4, 0x87, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0x41, 0xB4, 0x32, 0x01, 0x2E, 0x05, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x41, 0xB4, 0x12, 0x85, 0x13, 0x84, 0x13, 0x83, 0x13, 0x82, 0x13, 0x00, 0x05, 0x81, 0x13, 0x80, 0x13, 0xBD, 0xE0, 0x90, 0xFF, 0x91, 0xFF, 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF}; // clyde1_1.dro - Clyde's Adventure: Castle 1-3 const unsigned char clyde1_1_dro[0x1DF3] = { 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0x00, 0x00, 0x01, 0x00, 0xBC, 0xB7, 0x01, 0x00, 0xDB, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x40, 0x4F, 0xC0, 0x06, 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, 0xE0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x41, 0x4F, 0xC1, 0x06, 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, 0xE1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x42, 0x4F, 0xC2, 0x06, 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, 0xE2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x43, 0x00, 0x63, 0xF1, 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x44, 0x00, 0x64, 0xF1, 0x84, 0x71, 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, 0x71, 0x25, 0x11, 0xE5, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, 0x51, 0x28, 0x01, 0xE8, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, 0x51, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, 0x51, 0x2A, 0x01, 0xEA, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, 0x11, 0xEB, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, 0x11, 0xEC, 0x00, 0x10, 0x05, 0xBD, 0x00, 0x08, 0x00, 0x4D, 0x00, 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, 0xF5, 0x00, 0xBD, 0x00, 0xBD, 0x00, 0x08, 0x00, 0xA0, 0x00, 0xB0, 0x00, 0xA1, 0x00, 0xB1, 0x00, 0xA2, 0x00, 0xB2, 0x00, 0xA3, 0x00, 0xB3, 0x00, 0xA4, 0x00, 0xB4, 0x00, 0xA5, 0x00, 0xB5, 0x00, 0xA6, 0x00, 0xB6, 0x00, 0xA7, 0x00, 0xB7, 0x00, 0xA8, 0x00, 0xB8, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE2, 0x00, 0xE3, 0x00, 0xE4, 0x00, 0xE5, 0x00, 0xE8, 0x00, 0xE9, 0x00, 0xEA, 0x00, 0xEB, 0x00, 0xEC, 0x00, 0xED, 0x00, 0xF0, 0x00, 0xF1, 0x00, 0xF2, 0x00, 0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0x04, 0x01, 0x20, 0x10, 0x31, 0xBD, 0x00, 0x08, 0x00, 0x40, 0x4F, 0xC0, 0x06, 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, 0xE0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x41, 0x4F, 0xC1, 0x06, 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, 0xE1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x42, 0x4F, 0xC2, 0x06, 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, 0xE2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x43, 0x00, 0x63, 0xF1, 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x44, 0x00, 0x64, 0xF1, 0x84, 0x71, 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, 0x71, 0x25, 0x11, 0xE5, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, 0x51, 0x28, 0x01, 0xE8, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, 0x51, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, 0x51, 0x2A, 0x01, 0xEA, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, 0x11, 0x10, 0x05, 0xEB, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, 0x11, 0xEC, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4D, 0x00, 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, 0xF5, 0x00, 0xBD, 0x00, 0xBD, 0x00, 0x08, 0x00, 0xA0, 0x00, 0xB0, 0x00, 0xA1, 0x00, 0xB1, 0x00, 0xA2, 0x00, 0xB2, 0x00, 0xA3, 0x00, 0xB3, 0x00, 0xA4, 0x00, 0xB4, 0x00, 0xA5, 0x00, 0xB5, 0x00, 0xA6, 0x00, 0xB6, 0x00, 0xA7, 0x00, 0xB7, 0x00, 0xA8, 0x00, 0xB8, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE2, 0x00, 0xE3, 0x00, 0xE4, 0x00, 0xE5, 0x00, 0xE8, 0x00, 0xE9, 0x00, 0xEA, 0x00, 0x10, 0x05, 0xEB, 0x00, 0xEC, 0x00, 0xED, 0x00, 0xF0, 0x00, 0xF1, 0x00, 0xF2, 0x00, 0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0x04, 0x01, 0x20, 0xA6, 0x00, 0xB6, 0x00, 0xA7, 0x00, 0xB7, 0x00, 0xA8, 0x00, 0xB8, 0x00, 0xA8, 0x57, 0xB8, 0x09, 0xA7, 0x03, 0xB7, 0x0A, 0xBD, 0x20, 0x08, 0x00, 0x40, 0x4F, 0xC0, 0x06, 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, 0xE0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x41, 0x4F, 0xC1, 0x06, 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, 0xE1, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x42, 0x4F, 0xC2, 0x06, 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, 0xE2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x43, 0x00, 0x63, 0xF1, 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x44, 0x00, 0x64, 0xF1, 0x84, 0x71, 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, 0x71, 0x25, 0x11, 0xE5, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, 0x51, 0x28, 0x01, 0xE8, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, 0x51, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, 0x51, 0x10, 0x05, 0x2A, 0x01, 0xEA, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, 0x11, 0xEB, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, 0x11, 0xEC, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4D, 0x00, 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, 0xF5, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x50, 0x0B, 0xC6, 0x00, 0x70, 0xA8, 0x90, 0x4C, 0x30, 0x00, 0xF0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x53, 0x00, 0x10, 0x05, 0x73, 0xD6, 0x93, 0x4F, 0x33, 0x00, 0xF3, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF8, 0x94, 0xB5, 0x34, 0x0C, 0xF4, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x52, 0x00, 0xC8, 0x01, 0x72, 0xF7, 0x92, 0xB5, 0x32, 0x04, 0xF2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF5, 0x95, 0xB5, 0x35, 0x01, 0xF5, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x51, 0x00, 0xC7, 0x01, 0x71, 0xF7, 0x91, 0xB5, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x20, 0xBD, 0x20, 0x08, 0x00, 0x41, 0x00, 0xC1, 0x00, 0x61, 0x70, 0x81, 0x04, 0x21, 0x00, 0xE1, 0x01, 0xBD, 0x20, 0x08, 0x00, 0x44, 0x00, 0x64, 0x70, 0x84, 0x04, 0x24, 0x03, 0xE4, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x42, 0x00, 0xC2, 0x01, 0x62, 0x80, 0x82, 0x06, 0x22, 0x0F, 0xE2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x45, 0x00, 0x65, 0x80, 0x85, 0x06, 0x25, 0x0F, 0xE5, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x48, 0x00, 0xC3, 0x06, 0x68, 0x10, 0x88, 0x02, 0x28, 0x10, 0xE8, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0x40, 0x8B, 0x02, 0x2B, 0x01, 0xEB, 0x00, 0x4C, 0x0B, 0x53, 0x0B, 0x54, 0x0B, 0x01, 0x01, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x00, 0x69, 0xA0, 0x89, 0x04, 0x29, 0x00, 0xE9, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xA0, 0x8C, 0x04, 0x2C, 0x00, 0xEC, 0x00, 0x00, 0x54, 0xBD, 0x20, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x30, 0xA4, 0x57, 0xB4, 0x01, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x01, 0x69, 0xD0, 0x89, 0x04, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xD0, 0x8C, 0x04, 0x2C, 0x02, 0xEC, 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x07, 0xC4, 0x05, 0x69, 0x80, 0x89, 0x04, 0x29, 0x01, 0xE9, 0x01, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0x60, 0x8C, 0x02, 0x2C, 0x01, 0xEC, 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x00, 0x69, 0xA0, 0x89, 0x04, 0x29, 0x00, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xA0, 0x8C, 0x04, 0x2C, 0x00, 0xEC, 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0x6C, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x01, 0x69, 0xD0, 0x89, 0x04, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xD0, 0x8C, 0x04, 0x2C, 0x02, 0xEC, 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, 0x31}; ================================================ FILE: VGMPlay/chips/2151intf.c ================================================ /*************************************************************************** 2151intf.c Support interface YM2151(OPM) ***************************************************************************/ #include "mamedef.h" #include #include // for NULL //#include "sndintrf.h" //#include "streams.h" #include "fm.h" #include "2151intf.h" #include "ym2151.h" #include "opm.h" #define EC_MAME 0x00 #ifdef ENABLE_ALL_CORES #define EC_NUKED 0x01 #endif typedef struct _ym2151_state ym2151_state; struct _ym2151_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; UINT8 lastreg; //const ym2151_interface *intf; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ym2151_state YM2151Data[MAX_CHIPS]; /*INLINE ym2151_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2151); return (ym2151_state *)device->token; }*/ //static STREAM_UPDATE( ym2151_update ) void ym2151_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2151_state *info = (ym2151_state *)param; ym2151_state *info = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: ym2151_update_one(info->chip, outputs, samples); break; #ifdef ENABLE_ALL_CORES case EC_NUKED: OPM_GenerateStream(info->chip, outputs, samples); break; #endif } //YM2151UpdateOne(0x00, outputs, samples); } //static STATE_POSTLOAD( ym2151intf_postload ) /*static void ym2151intf_postload(UINT8 ChipID) { //ym2151_state *info = (ym2151_state *)param; ym2151_state *info = &YM2151Data[ChipID]; ym2151_postload(info->chip); }*/ //static DEVICE_START( ym2151 ) int device_start_ym2151(UINT8 ChipID, int clock) { //static const ym2151_interface dummy = { 0 }; //ym2151_state *info = get_safe_token(device); ym2151_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &YM2151Data[ChipID]; rate = clock/64; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const ym2151_interface *)device->static_config : &dummy; //info->intf = &dummy; /* stream setup */ //info->stream = stream_create(device,0,2,rate,info,ym2151_update); switch (EMU_CORE) { case EC_MAME: info->chip = ym2151_init(clock, rate); break; #ifdef ENABLE_ALL_CORES case EC_NUKED: info->chip = malloc(sizeof(opm_t)); OPM_Reset(info->chip, rate, clock); break; #endif } //assert_always(info->chip != NULL, "Error creating YM2151 chip"); //state_save_register_postload(device->machine, ym2151intf_postload, info); //ym2151_set_irq_handler(info->chip,info->intf->irqhandler); //ym2151_set_port_write_handler(info->chip,info->intf->portwritehandler); return rate; } //static DEVICE_STOP( ym2151 ) void device_stop_ym2151(UINT8 ChipID) { //ym2151_state *info = get_safe_token(device); ym2151_state *info = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: ym2151_shutdown(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_NUKED: free(info->chip); break; #endif } //YM2151Shutdown(); } //static DEVICE_RESET( ym2151 ) void device_reset_ym2151(UINT8 ChipID) { //ym2151_state *info = get_safe_token(device); ym2151_state *info = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: ym2151_reset_chip(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_NUKED: OPM_Reset(info->chip, 0, 0); break; #endif } //YM2151ResetChip(0x00); } //READ8_DEVICE_HANDLER( ym2151_r ) UINT8 ym2151_r(UINT8 ChipID, offs_t offset) { //ym2151_state *token = get_safe_token(device); ym2151_state *token = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: if (offset & 1) { //stream_update(token->stream); return ym2151_read_status(token->chip); //return YM2151ReadStatus(0x00); } else return 0xff; /* confirmed on a real YM2151 */ } return 0x00; } //WRITE8_DEVICE_HANDLER( ym2151_w ) void ym2151_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2151_state *token = get_safe_token(device); ym2151_state *token = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: if (offset & 1) { //stream_update(token->stream); ym2151_write_reg(token->chip, token->lastreg, data); //YM2151WriteReg(0x00, token->lastreg, data); } else token->lastreg = data; break; #ifdef ENABLE_ALL_CORES case EC_NUKED: OPM_WriteBuffered(token->chip, offset, data); break; #endif } } /*READ8_DEVICE_HANDLER( ym2151_status_port_r ) { return ym2151_r(device, 1); } WRITE8_DEVICE_HANDLER( ym2151_register_port_w ) { ym2151_w(device, 0, data); } WRITE8_DEVICE_HANDLER( ym2151_data_port_w ) { ym2151_w(device, 1, data); }*/ UINT8 ym2151_status_port_r(UINT8 ChipID, offs_t offset) { return ym2151_r(ChipID, 1); } void ym2151_register_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2151_w(ChipID, 0, data); } void ym2151_data_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2151_w(ChipID, 1, data); } void ym2151_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_MAME; #endif return; } void ym2151_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ym2151_state *info = &YM2151Data[ChipID]; switch (EMU_CORE) { case EC_MAME: ym2151_set_mutemask(info->chip, MuteMask); break; #ifdef ENABLE_ALL_CORES case EC_NUKED: OPM_SetMute(info->chip, MuteMask); break; #endif } } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2151 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2151_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2151 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2151 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2151 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2151"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/2151intf.h ================================================ #pragma once /*typedef struct _ym2151_interface ym2151_interface; struct _ym2151_interface { //void (*irqhandler)(const device_config *device, int irq); void (*irqhandler)(int irq); write8_device_func portwritehandler; };*/ /*READ8_DEVICE_HANDLER( ym2151_r ); WRITE8_DEVICE_HANDLER( ym2151_w ); READ8_DEVICE_HANDLER( ym2151_status_port_r ); WRITE8_DEVICE_HANDLER( ym2151_register_port_w ); WRITE8_DEVICE_HANDLER( ym2151_data_port_w ); DEVICE_GET_INFO( ym2151 ); #define SOUND_YM2151 DEVICE_GET_INFO_NAME( ym2151 )*/ void ym2151_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2151(UINT8 ChipID, int clock); void device_stop_ym2151(UINT8 ChipID); void device_reset_ym2151(UINT8 ChipID); UINT8 ym2151_r(UINT8 ChipID, offs_t offset); void ym2151_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2151_status_port_r(UINT8 ChipID, offs_t offset); void ym2151_register_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2151_data_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2151_set_emu_core(UINT8 Emulator); void ym2151_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/2203intf.c ================================================ #include #include // for free #include // for memset #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #include "2203intf.h" #include "fm.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // AY8910 core from MAME #endif #define EC_EMU2149 0x00 typedef struct _ym2203_state ym2203_state; struct _ym2203_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; void * psg; ym2203_interface intf; //const device_config *device; }; #define CHTYPE_YM2203 0x20 extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 AY_EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x10 static ym2203_state YM2203Data[MAX_CHIPS]; /*INLINE ym2203_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2203); return (ym2203_state *)device->token; }*/ static void psg_set_clock(void *param, int clock) { ym2203_state *info = (ym2203_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_clock_ym(info->psg, clock); break; #endif case EC_EMU2149: PSG_set_clock((PSG*)info->psg, clock); break; } } } static void psg_write(void *param, int address, int data) { ym2203_state *info = (ym2203_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->psg, address, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->psg, address, data); break; } } } static int psg_read(void *param) { ym2203_state *info = (ym2203_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ay8910_read_ym(info->psg); #endif case EC_EMU2149: return PSG_readIO((PSG*)info->psg); } } return 0x00; } static void psg_reset(void *param) { ym2203_state *info = (ym2203_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->psg); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->psg); break; } } } static const ssg_callbacks psgintf = { psg_set_clock, psg_write, psg_read, psg_reset }; /* IRQ Handler */ /*static void IRQHandler(void *param,int irq) { ym2203_state *info = (ym2203_state *)param; if (info->intf.handler != NULL) //(*info->intf->handler)(info->device, irq); (*info->intf.handler)(irq); }*/ /* Timer overflow callback from timer.c */ /*static TIMER_CALLBACK( timer_callback_2203_0 ) { ym2203_state *info = (ym2203_state *)ptr; ym2203_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_2203_1 ) { ym2203_state *info = (ym2203_state *)ptr; ym2203_timer_over(info->chip,1); }*/ /* update request from fm.c */ void ym2203_update_request(void *param) { ym2203_state *info = (ym2203_state *)param; //stream_update(info->stream); ym2203_update_one(info->chip, DUMMYBUF, 0); // We really don't need this. /*if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, DUMMYBUF, 0); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, DUMMYBUF, 0); break; } }*/ } /*static void timer_handler(void *param,int c,int count,int clock) { ym2203_state *info = (ym2203_state *)param; if( count == 0 ) { // Reset FM Timer //timer_enable(info->timer[c], 0); } else { // Start FM Timer //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); //if (!timer_enable(info->timer[c], 1)) // timer_adjust_oneshot(info->timer[c], period, 0); } }*/ //static STREAM_UPDATE( ym2203_stream_update ) void ym2203_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2203_state *info = (ym2203_state *)param; ym2203_state *info = &YM2203Data[ChipID]; ym2203_update_one(info->chip, outputs, samples); } void ym2203_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2203_state *info = (ym2203_state *)param; ym2203_state *info = &YM2203Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, outputs, samples); break; } } else { memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); } } //static STATE_POSTLOAD( ym2203_intf_postload ) /*static void ym2203_intf_postload(UINT8 ChipID) { //ym2203_state *info = (ym2203_state *)param; ym2203_state *info = &YM2203Data[ChipID]; ym2203_postload(info->chip); }*/ //static DEVICE_START( ym2203 ) int device_start_ym2203(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate) { static const ym2203_interface generic_2203 = { { AY8910_LEGACY_OUTPUT, AY8910_DEFAULT_LOADS //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL }, NULL }; //const ym2203_interface *intf = device->static_config ? (const ym2203_interface *)device->static_config : &generic_2203; ym2203_interface* intf; //ym2203_state *info = get_safe_token(device); ym2203_state *info; int rate; int ay_clock; if (ChipID >= MAX_CHIPS) return 0; info = &YM2203Data[ChipID]; rate = clock/72; /* ??? */ if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; info->intf = generic_2203; intf = &info->intf; if (AYFlags) intf->ay8910_intf.flags = AYFlags; //info->device = device; //info->psg = ay8910_start_ym(NULL, SOUND_YM2203, device, device->clock, &intf->ay8910_intf); if (! AYDisable) { ay_clock = clock / 2; *AYrate = ay_clock / 8; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) *AYrate = CHIP_SAMPLE_RATE; switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: // fits in the most common cases // TODO: remove after being able to change the resampler's sampling rate info->psg = ay8910_start_ym(NULL, CHTYPE_YM2203, ay_clock, &intf->ay8910_intf); break; #endif case EC_EMU2149: info->psg = PSG_new(ay_clock, *AYrate); if (info->psg == NULL) return 0; PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode break; } } else { info->psg = NULL; *AYrate = 0; } //assert_always(info->psg != NULL, "Error creating YM2203/AY8910 chip"); /* Timer Handler set */ //info->timer[0] = timer_alloc(device->machine, timer_callback_2203_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_2203_1, info); /* stream system initialize */ //info->stream = stream_create(device,0,1,rate,info,ym2203_stream_update); /* Initialize FM emurator */ //info->chip = ym2203_init(info,clock,rate,timer_handler,IRQHandler,&psgintf); info->chip = ym2203_init(info,clock,rate,NULL,NULL,&psgintf); //assert_always(info->chip != NULL, "Error creating YM2203 chip"); //state_save_register_postload(device->machine, ym2203_intf_postload, info); return rate; } //static DEVICE_STOP( ym2203 ) void device_stop_ym2203(UINT8 ChipID) { //ym2203_state *info = get_safe_token(device); ym2203_state *info = &YM2203Data[ChipID]; ym2203_shutdown(info->chip); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->psg); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->psg); break; } info->psg = NULL; } } //static DEVICE_RESET( ym2203 ) void device_reset_ym2203(UINT8 ChipID) { //ym2203_state *info = get_safe_token(device); ym2203_state *info = &YM2203Data[ChipID]; ym2203_reset_chip(info->chip); // also resets the AY clock //psg_reset(info); // already done as a callback in ym2203_reset_chip } //READ8_DEVICE_HANDLER( ym2203_r ) UINT8 ym2203_r(UINT8 ChipID, offs_t offset) { //ym2203_state *info = get_safe_token(device); ym2203_state *info = &YM2203Data[ChipID]; return ym2203_read(info->chip, offset & 1); } //WRITE8_DEVICE_HANDLER( ym2203_w ) void ym2203_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2203_state *info = get_safe_token(device); ym2203_state *info = &YM2203Data[ChipID]; ym2203_write(info->chip, offset & 1, data); } //READ8_DEVICE_HANDLER( ym2203_status_port_r ) UINT8 ym2203_status_port_r(UINT8 ChipID, offs_t offset) { return ym2203_r(ChipID, 0); } //READ8_DEVICE_HANDLER( ym2203_read_port_r ) UINT8 ym2203_read_port_r(UINT8 ChipID, offs_t offset) { return ym2203_r(ChipID, 1); } //WRITE8_DEVICE_HANDLER( ym2203_control_port_w ) void ym2203_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2203_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym2203_write_port_w ) void ym2203_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2203_w(ChipID, 1, data); } void ym2203_set_ay_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES AY_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else AY_EMU_CORE = EC_EMU2149; #endif return; } void ym2203_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY) { ym2203_state *info = &YM2203Data[ChipID]; ym2203_set_mutemask(info->chip, MuteMaskFM); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); break; #endif case EC_EMU2149: PSG_setMask((PSG*)info->psg, MuteMaskAY); break; } } } void ym2203_set_stereo_mask_ay(UINT8 ChipID, UINT32 StereoMaskAY) { ym2203_state *info = &YM2203Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_stereo_mask_ym(info->psg, StereoMaskAY); break; #endif case EC_EMU2149: PSG_setStereoMask((PSG*)info->psg, StereoMaskAY); break; } } } void ym2203_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr) { ym2203_state *info = &YM2203Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_srchg_cb_ym(info->psg, CallbackFunc, AYDataPtr); break; #endif case EC_EMU2149: break; } } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2203 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2203_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2203 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2203 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2203 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2203"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/2203intf.h ================================================ #pragma once #include "ay8910.h" #include "emu2149.h" void ym2203_update_request(void *param); typedef struct _ym2203_interface ym2203_interface; struct _ym2203_interface { ay8910_interface ay8910_intf; //void (*handler)(const device_config *device, int irq); void (*handler)(int irq); }; /*READ8_DEVICE_HANDLER( ym2203_r ); WRITE8_DEVICE_HANDLER( ym2203_w ); READ8_DEVICE_HANDLER( ym2203_status_port_r ); READ8_DEVICE_HANDLER( ym2203_read_port_r ); WRITE8_DEVICE_HANDLER( ym2203_control_port_w ); WRITE8_DEVICE_HANDLER( ym2203_write_port_w ); DEVICE_GET_INFO( ym2203 ); #define SOUND_YM2203 DEVICE_GET_INFO_NAME( ym2203 )*/ void ym2203_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ym2203_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2203(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate); void device_stop_ym2203(UINT8 ChipID); void device_reset_ym2203(UINT8 ChipID); UINT8 ym2203_r(UINT8 ChipID, offs_t offset); void ym2203_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2203_status_port_r(UINT8 ChipID, offs_t offset); UINT8 ym2203_read_port_r(UINT8 ChipID, offs_t offset); void ym2203_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2203_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2203_set_ay_emu_core(UINT8 Emulator); void ym2203_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY); void ym2203_set_stereo_mask_ay(UINT8 ChipID, UINT32 StereoMaskAY); void ym2203_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr); ================================================ FILE: VGMPlay/chips/2413intf.c ================================================ /**************************************************************** MAME / MESS functions ****************************************************************/ #include "mamedef.h" #include #include // for NULL //#include "sndintrf.h" //#include "streams.h" #ifdef ENABLE_ALL_CORES #include "ym2413.h" #include "opll.h" #endif #include "emu2413.h" #include "2413intf.h" #ifdef ENABLE_ALL_CORES #define EC_NUKED 0x02 // Nuked OPLL #define EC_MAME 0x01 // YM2413 core from MAME #endif #define EC_EMU2413 0x00 // EMU2413 core from in_vgm, value 0 because it's better than MAME /* for stream system */ typedef struct _ym2413_state ym2413_state; struct _ym2413_state { //sound_stream * stream; void * chip; UINT8 Mode; }; static unsigned char vrc7_inst[(16 + 3) * 8] = { #include "vrc7tone.h" }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ym2413_state YM2413Data[MAX_CHIPS]; /*INLINE ym2413_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2413); return (ym2413_state *)device->token; }*/ #ifdef UNUSED_FUNCTION void YM2413DAC_update(int chip,stream_sample_t **inputs, stream_sample_t **_buffer,int length) { INT16 *buffer = _buffer[0]; static int out = 0; if ( ym2413[chip].reg[0x0F] & 0x01 ) { out = ((ym2413[chip].reg[0x10] & 0xF0) << 7); } while (length--) *(buffer++) = out; } #endif static void _emu2413_calc_stereo(OPLL *opll, INT32 **out, int samples) { INT32 *bufL = out[0]; INT32 *bufR = out[1]; INT32 buffers[2]; int i; for (i = 0; i < samples; i++) { OPLL_calcStereo(opll, buffers); bufL[i] = buffers[0]; bufR[i] = buffers[1]; } } static void _emu2413_set_mute_mask(OPLL *opll, UINT32 MuteMask) { unsigned char CurChn; UINT32 ChnMsk; for (CurChn = 0; CurChn < 14; CurChn++) { if (CurChn < 9) { ChnMsk = OPLL_MASK_CH(CurChn); } else { switch (CurChn) { case 9: ChnMsk = OPLL_MASK_BD; break; case 10: ChnMsk = OPLL_MASK_SD; break; case 11: ChnMsk = OPLL_MASK_TOM; break; case 12: ChnMsk = OPLL_MASK_CYM; break; case 13: ChnMsk = OPLL_MASK_HH; break; default: ChnMsk = 0; break; } } if ((MuteMask >> CurChn) & 0x01) opll->mask |= ChnMsk; else opll->mask &= ~ChnMsk; } return; } //static STREAM_UPDATE( ym2413_stream_update ) void ym2413_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { ym2413_state *info = &YM2413Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_update_one(info->chip, outputs, samples); break; case EC_NUKED: OPLL_GenerateStream(info->chip, outputs, samples); break; #endif case EC_EMU2413: _emu2413_calc_stereo(info->chip, outputs, samples); break; } } static void _stream_update(void *param, int interval) { ym2413_state *info = (ym2413_state *)param; /*stream_update(info->stream);*/ switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_update_one(info->chip, DUMMYBUF, 0); break; case EC_NUKED: // OPLL_GenerateStream(info->chip, DUMMYBUF, 0); break; #endif case EC_EMU2413: _emu2413_calc_stereo(info->chip, DUMMYBUF, 0); break; } } //static DEVICE_START( ym2413 ) int device_start_ym2413(UINT8 ChipID, int clock) { //ym2413_state *info = get_safe_token(device); ym2413_state *info; int type; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &YM2413Data[ChipID]; info->Mode = (clock & 0x80000000) >> 31; clock &= 0x7FFFFFFF; rate = clock/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; /* emulator create */ switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->chip = ym2413_init(clock, rate); if (info->chip == NULL) return 0; //assert_always(info->chip != NULL, "Error creating YM2413 chip"); ym2413_set_chip_mode(info->chip, info->Mode); /* stream system initialize */ //info->stream = stream_create(device,0,2,rate,info,ym2413_stream_update); ym2413_set_update_handler(info->chip, _stream_update, info); break; case EC_NUKED: info->chip = malloc(sizeof(opll_t)); //if(chiptype) // OPN2_SetChipType(ym3438_type_discrete); type = info->Mode ? opll_type_ds1001 : opll_type_ym2413; OPLL_Reset(info->chip, type, rate, clock); break; #endif case EC_EMU2413: info->chip = OPLL_new(clock, rate); if (info->chip == NULL) return 0; OPLL_setChipMode(info->chip, info->Mode); if (info->Mode) OPLL_setPatch(info->chip, vrc7_inst); break; } // Note: VRC7 instruments are set in device_reset if necessary. /*#if 0 int i, tst; char name[40]; num = intf->num; tst = YM3812_sh_start (msound); if (tst) return 1; for (i=0;iclock/72, i, YM2413DAC_update); if (ym2413[i].DAC_stream == -1) return 1; } return 0; #endif*/ return rate; } //static DEVICE_STOP( ym2413 ) void device_stop_ym2413(UINT8 ChipID) { //ym2413_state *info = get_safe_token(device); ym2413_state *info = &YM2413Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_shutdown(info->chip); break; case EC_NUKED: free(info->chip); break; #endif case EC_EMU2413: OPLL_delete(info->chip); break; } } //static DEVICE_RESET( ym2413 ) void device_reset_ym2413(UINT8 ChipID) { //ym2413_state *info = get_safe_token(device); ym2413_state *info = &YM2413Data[ChipID]; int type; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_reset_chip(info->chip); if (info->Mode) ym2413_override_patches(info->chip, vrc7_inst); break; case EC_NUKED: type = info->Mode ? opll_type_ds1001 : opll_type_ym2413; OPLL_Reset(info->chip, type, 0, 0); break; #endif case EC_EMU2413: OPLL_reset(info->chip); // EMU2413 doesn't reset the patch data in OPLL_reset //if (info->Mode) // OPLL_setPatch(info->chip, vrc7_inst); break; } } //WRITE8_DEVICE_HANDLER( ym2413_w ) void ym2413_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2413_state *info = get_safe_token(device); ym2413_state *info = &YM2413Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_write(info->chip, offset & 1, data); break; case EC_NUKED: OPLL_WriteBuffered(info->chip, offset, data); break; #endif case EC_EMU2413: OPLL_writeIO(info->chip, offset & 1, data); break; } } //WRITE8_DEVICE_HANDLER( ym2413_register_port_w ) void ym2413_register_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2413_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym2413_data_port_w ) void ym2413_data_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2413_w(ChipID, 1, data); } void ym2413_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x03) ? Emulator : 0x00; #else EMU_CORE = EC_EMU2413; #endif return; } void ym2413_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ym2413_state *info = &YM2413Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym2413_set_mutemask(info->chip, MuteMask); break; case EC_NUKED: OPLL_SetMute(info->chip, MuteMask); break; #endif case EC_EMU2413: _emu2413_set_mute_mask(info->chip, MuteMask); break; } return; } void ym2413_set_panning(UINT8 ChipID, INT16* PanVals) { ym2413_state *info = &YM2413Data[ChipID]; UINT8 CurChn; UINT8 EmuChn; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: break; case EC_NUKED: break; #endif case EC_EMU2413: for (CurChn = 0x00; CurChn < 0x0E; CurChn ++) { // input: 0..8, BD, SD, TOM, CYM, HH // output: 0..8, BD, HH, SD, TOM, CYM if (CurChn < 10) EmuChn = CurChn; else if (CurChn < 13) EmuChn = CurChn + 1; else EmuChn = 10; OPLL_setPanEx(info->chip, EmuChn, PanVals[CurChn]); } break; } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2413 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2413_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2413 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2413 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2413 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2413"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/2413intf.h ================================================ #pragma once /*WRITE8_DEVICE_HANDLER( ym2413_w ); WRITE8_DEVICE_HANDLER( ym2413_register_port_w ); WRITE8_DEVICE_HANDLER( ym2413_data_port_w ); DEVICE_GET_INFO( ym2413 ); #define SOUND_YM2413 DEVICE_GET_INFO_NAME( ym2413 )*/ void ym2413_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2413(UINT8 ChipID, int clock); void device_stop_ym2413(UINT8 ChipID); void device_reset_ym2413(UINT8 ChipID); void ym2413_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2413_register_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2413_data_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2413_set_emu_core(UINT8 Emulator); void ym2413_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void ym2413_set_panning(UINT8 ChipID, INT16* PanVals); ================================================ FILE: VGMPlay/chips/2608intf.c ================================================ /*************************************************************************** 2608intf.c The YM2608 emulator supports up to 2 chips. Each chip has the following connections: - Status Read / Control Write A - Port Read / Data Write A - Control Write B - Data Write B ***************************************************************************/ #include // for free #include // for memset #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #include "2608intf.h" //#include "fm.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // AY8910 core from MAME #endif #define EC_EMU2149 0x00 typedef struct _ym2608_state ym2608_state; struct _ym2608_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; void * psg; ym2608_interface intf; //const device_config *device; }; #define CHTYPE_YM2608 0x21 extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 AY_EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static ym2608_state YM2608Data[MAX_CHIPS]; /*INLINE ym2608_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2608); return (ym2608_state *)device->token; }*/ static void psg_set_clock(void *param, int clock) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_clock_ym(info->psg, clock); break; #endif case EC_EMU2149: PSG_set_clock((PSG*)info->psg, clock); break; } } } static void psg_write(void *param, int address, int data) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->psg, address, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->psg, address, data); break; } } } static int psg_read(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ay8910_read_ym(info->psg); #endif case EC_EMU2149: return PSG_readIO((PSG*)info->psg); } } return 0x00; } static void psg_reset(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->psg); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->psg); break; } } } static const ssg_callbacks psgintf = { psg_set_clock, psg_write, psg_read, psg_reset }; /* IRQ Handler */ /*static void IRQHandler(void *param,int irq) { ym2608_state *info = (ym2608_state *)param; //if(info->intf->handler) info->intf->handler(info->device, irq); if(info->intf.handler) info->intf.handler(irq); }*/ /* Timer overflow callback from timer.c */ /*static TIMER_CALLBACK( timer_callback_2608_0 ) { ym2608_state *info = (ym2608_state *)ptr; ym2608_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_2608_1 ) { ym2608_state *info = (ym2608_state *)ptr; ym2608_timer_over(info->chip,1); }*/ /*static void timer_handler(void *param,int c,int count,int clock) { ym2608_state *info = (ym2608_state *)param; if( count == 0 ) { // Reset FM Timer //timer_enable(info->timer[c], 0); } else { // Start FM Timer //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); //if (!timer_enable(info->timer[c], 1)) // timer_adjust_oneshot(info->timer[c], period, 0); } }*/ /* update request from fm.c */ void ym2608_update_request(void *param) { ym2608_state *info = (ym2608_state *)param; //stream_update(info->stream); ym2608_update_one(info->chip, DUMMYBUF, 0); // Not necessary. //if (info->psg != NULL) // ay8910_update_one(info->psg, DUMMYBUF, 0); } //static STREAM_UPDATE( ym2608_stream_update ) void ym2608_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2608_state *info = (ym2608_state *)param; ym2608_state *info = &YM2608Data[ChipID]; ym2608_update_one(info->chip, outputs, samples); } void ym2608_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2608_state *info = (ym2608_state *)param; ym2608_state *info = &YM2608Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, outputs, samples); break; } } else { memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); } } //static STATE_POSTLOAD( ym2608_intf_postload ) /*static void ym2608_intf_postload(UINT8 ChipID) { //ym2608_state *info = (ym2608_state *)param; ym2608_state *info = &YM2608Data[ChipID]; ym2608_postload(info->chip); }*/ //static DEVICE_START( ym2608 ) int device_start_ym2608(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate) { static const ym2608_interface generic_2608 = { { AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, AY8910_DEFAULT_LOADS //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL }, NULL }; //const ym2608_interface *intf = device->static_config ? (const ym2608_interface *)device->static_config : &generic_2608; ym2608_interface *intf; int rate; int ay_clock; //void *pcmbufa; //int pcmsizea; //ym2608_state *info = get_safe_token(device); ym2608_state *info; if (ChipID >= MAX_CHIPS) return 0; info = &YM2608Data[ChipID]; rate = clock/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; info->intf = generic_2608; intf = &info->intf; if (AYFlags) intf->ay8910_intf.flags = AYFlags; //info->device = device; /* FIXME: Force to use single output */ //info->psg = ay8910_start_ym(NULL, SOUND_YM2608, clock, &intf->ay8910_intf); if (! AYDisable) { ay_clock = clock / 4; *AYrate = ay_clock / 8; switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->psg = ay8910_start_ym(NULL, CHTYPE_YM2608, ay_clock, &intf->ay8910_intf); break; #endif case EC_EMU2149: info->psg = PSG_new(ay_clock, *AYrate); if (info->psg == NULL) return 0; PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode break; } } else { info->psg = NULL; *AYrate = 0; } //assert_always(info->psg != NULL, "Error creating YM2608/AY8910 chip"); /* Timer Handler set */ //info->timer[0] = timer_alloc(device->machine, timer_callback_2608_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_2608_1, info); /* stream system initialize */ //info->stream = stream_create(device,0,2,rate,info,ym2608_stream_update); /* setup adpcm buffers */ //pcmbufa = device->region; //pcmsizea = device->regionbytes; /* initialize YM2608 */ //info->chip = ym2608_init(info,device,device->clock,rate, // pcmbufa,pcmsizea, // timer_handler,IRQHandler,&psgintf); info->chip = ym2608_init(info, clock, rate, NULL, NULL, &psgintf); //assert_always(info->chip != NULL, "Error creating YM2608 chip"); //state_save_register_postload(device->machine, ym2608_intf_postload, info); return rate; } //static DEVICE_STOP( ym2608 ) void device_stop_ym2608(UINT8 ChipID) { //ym2608_state *info = get_safe_token(device); ym2608_state *info = &YM2608Data[ChipID]; ym2608_shutdown(info->chip); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->psg); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->psg); break; } info->psg = NULL; } } //static DEVICE_RESET( ym2608 ) void device_reset_ym2608(UINT8 ChipID) { //ym2608_state *info = get_safe_token(device); ym2608_state *info = &YM2608Data[ChipID]; ym2608_reset_chip(info->chip); // also resets the AY clock //psg_reset(info); // already done as a callback in ym2608_reset_chip } //READ8_DEVICE_HANDLER( ym2608_r ) UINT8 ym2608_r(UINT8 ChipID, offs_t offset) { //ym2608_state *info = get_safe_token(device); ym2608_state *info = &YM2608Data[ChipID]; return ym2608_read(info->chip, offset & 3); } //WRITE8_DEVICE_HANDLER( ym2608_w ) void ym2608_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2608_state *info = get_safe_token(device); ym2608_state *info = &YM2608Data[ChipID]; ym2608_write(info->chip, offset & 3, data); } //READ8_DEVICE_HANDLER( ym2608_read_port_r ) UINT8 ym2608_read_port_r(UINT8 ChipID, offs_t offset) { return ym2608_r(ChipID, 1); } //READ8_DEVICE_HANDLER( ym2608_status_port_a_r ) UINT8 ym2608_status_port_a_r(UINT8 ChipID, offs_t offset) { return ym2608_r(ChipID, 0); } //READ8_DEVICE_HANDLER( ym2608_status_port_b_r ) UINT8 ym2608_status_port_b_r(UINT8 ChipID, offs_t offset) { return ym2608_r(ChipID, 2); } //WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w ) void ym2608_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2608_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w ) void ym2608_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2608_w(ChipID, 2, data); } //WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w ) void ym2608_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2608_w(ChipID, 1, data); } //WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w ) void ym2608_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2608_w(ChipID, 3, data); } void ym2608_set_ay_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES AY_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else AY_EMU_CORE = EC_EMU2149; #endif return; } void ym2608_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ym2608_state* info = &YM2608Data[ChipID]; ym2608_write_pcmrom(info->chip, rom_id, ROMSize, DataStart, DataLength, ROMData); } void ym2608_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY) { ym2608_state* info = &YM2608Data[ChipID]; ym2608_set_mutemask(info->chip, MuteMaskFM); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); break; #endif case EC_EMU2149: PSG_setMask((PSG*)info->psg, MuteMaskAY); break; } } } void ym2608_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr) { ym2608_state* info = &YM2608Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_srchg_cb_ym(info->psg, CallbackFunc, AYDataPtr); break; #endif case EC_EMU2149: break; } } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2608 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2608_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2608 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2608 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2608 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2608"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/2608intf.h ================================================ #pragma once #include "fm.h" #include "ay8910.h" #include "emu2149.h" void ym2608_update_request(void *param); typedef struct _ym2608_interface ym2608_interface; struct _ym2608_interface { ay8910_interface ay8910_intf; //void ( *handler )( const device_config *device, int irq ); /* IRQ handler for the YM2608 */ void ( *handler )( int irq ); /* IRQ handler for the YM2608 */ }; /*READ8_DEVICE_HANDLER( ym2608_r ); WRITE8_DEVICE_HANDLER( ym2608_w ); READ8_DEVICE_HANDLER( ym2608_read_port_r ); READ8_DEVICE_HANDLER( ym2608_status_port_a_r ); READ8_DEVICE_HANDLER( ym2608_status_port_b_r ); WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w ); WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w ); WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w ); WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w ); DEVICE_GET_INFO( ym2608 ); #define SOUND_YM2608 DEVICE_GET_INFO_NAME( ym2608 )*/ void ym2608_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ym2608_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2608(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate); void device_stop_ym2608(UINT8 ChipID); void device_reset_ym2608(UINT8 ChipID); UINT8 ym2608_r(UINT8 ChipID, offs_t offset); void ym2608_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2608_read_port_r(UINT8 ChipID, offs_t offset); UINT8 ym2608_status_port_a_r(UINT8 ChipID, offs_t offset); UINT8 ym2608_status_port_b_r(UINT8 ChipID, offs_t offset); void ym2608_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_set_ay_emu_core(UINT8 Emulator); void ym2608_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2608_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY); void ym2608_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr); ================================================ FILE: VGMPlay/chips/2610intf.c ================================================ /*************************************************************************** 2610intf.c The YM2610 emulator supports up to 2 chips. Each chip has the following connections: - Status Read / Control Write A - Port Read / Data Write A - Control Write B - Data Write B ***************************************************************************/ #include // for free #include // for memset #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #include "2610intf.h" #include "fm.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // AY8910 core from MAME #endif #define EC_EMU2149 0x00 typedef struct _ym2610_state ym2610_state; struct _ym2610_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; void * psg; //const ym2610_interface *intf; //const device_config *device; }; #define CHTYPE_YM2610 0x22 extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 AY_EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static ym2610_state YM2610Data[MAX_CHIPS]; /*INLINE ym2610_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2610 || sound_get_type(device) == SOUND_YM2610B); return (ym2610_state *)device->token; }*/ static void psg_set_clock(void *param, int clock) { ym2610_state *info = (ym2610_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_clock_ym(info->psg, clock); break; #endif case EC_EMU2149: PSG_set_clock((PSG*)info->psg, clock); break; } } } static void psg_write(void *param, int address, int data) { ym2610_state *info = (ym2610_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->psg, address, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->psg, address, data); break; } } } static int psg_read(void *param) { ym2610_state *info = (ym2610_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ay8910_read_ym(info->psg); #endif case EC_EMU2149: return PSG_readIO((PSG*)info->psg); } } return 0x00; } static void psg_reset(void *param) { ym2610_state *info = (ym2610_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->psg); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->psg); break; } } } static const ssg_callbacks psgintf = { psg_set_clock, psg_write, psg_read, psg_reset }; /*------------------------- TM2610 -------------------------------*/ /* IRQ Handler */ /*static void IRQHandler(void *param,int irq) { ym2610_state *info = (ym2610_state *)param; //if(info->intf->handler) info->intf->handler(info->device, irq); //if(info->intf->handler) info->intf->handler(irq); }*/ /* Timer overflow callback from timer.c */ /*static TIMER_CALLBACK( timer_callback_0 ) { ym2610_state *info = (ym2610_state *)ptr; ym2610_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_1 ) { ym2610_state *info = (ym2610_state *)ptr; ym2610_timer_over(info->chip,1); }*/ /*static void timer_handler(void *param,int c,int count,int clock) { ym2610_state *info = (ym2610_state *)param; if( count == 0 ) { // Reset FM Timer //timer_enable(info->timer[c], 0); } else { // Start FM Timer //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); //if (!timer_enable(info->timer[c], 1)) // timer_adjust_oneshot(info->timer[c], period, 0); } }*/ /* update request from fm.c */ void ym2610_update_request(void *param) { ym2610_state *info = (ym2610_state *)param; //stream_update(info->stream); ym2610b_update_one(info->chip, DUMMYBUF, 0); // Not necessary. //if (info->psg != NULL) // ay8910_update_one(info->psg, DUMMYBUF, 0); } //static STREAM_UPDATE( ym2610_stream_update ) void ym2610_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2610_state *info = (ym2610_state *)param; ym2610_state *info = &YM2610Data[ChipID]; ym2610_update_one(info->chip, outputs, samples); } //static STREAM_UPDATE( ym2610b_stream_update ) void ym2610b_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2610_state *info = (ym2610_state *)param; ym2610_state *info = &YM2610Data[ChipID]; ym2610b_update_one(info->chip, outputs, samples); } void ym2610_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2610_state *info = (ym2610_state *)param; ym2610_state *info = &YM2610Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, outputs, samples); break; } } else { memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); } } //static STATE_POSTLOAD( ym2610_intf_postload ) /*static void ym2610_intf_postload(UINT8 ChipID) { //ym2610_state *info = (ym2610_state *)param; ym2610_state *info = &YM2610Data[ChipID]; ym2610_postload(info->chip); }*/ //static DEVICE_START( ym2610 ) int device_start_ym2610(UINT8 ChipID, int clock, UINT8 AYDisable, int* AYrate) { // clock bit 31: 0 - YM2610 // 1 - YM2610B //static const ym2610_interface generic_2610 = { 0 }; static const ay8910_interface generic_ay8910 = { AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, AY8910_DEFAULT_LOADS //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL }; //const ym2610_interface *intf = device->static_config ? (const ym2610_interface *)device->static_config : &generic_2610; //const ym2610_interface *intf = &generic_2610; int rate; int ay_clock; //void *pcmbufa,*pcmbufb; //int pcmsizea,pcmsizeb; //ym2610_state *info = get_safe_token(device); ym2610_state *info; //astring *name = astring_alloc(); //sound_type type = sound_get_type(device); unsigned char ChipType; if (ChipID >= MAX_CHIPS) return 0; info = &YM2610Data[ChipID]; ChipType = (clock & 0x80000000) ? 0x01 : 0x00; clock &= 0x7FFFFFFF; rate = clock/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = intf; //info->device = device; //info->psg = ay8910_start_ym(NULL, sound_get_type(device), device, device->clock, &generic_ay8910); if (! AYDisable) { ay_clock = clock / 4; *AYrate = ay_clock / 8; switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->psg = ay8910_start_ym(NULL, CHTYPE_YM2610 + ChipType, ay_clock, &generic_ay8910); break; #endif case EC_EMU2149: info->psg = PSG_new(ay_clock, *AYrate); if (info->psg == NULL) return 0; PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode break; } } else { info->psg = NULL; *AYrate = 0; } //assert_always(info->psg != NULL, "Error creating YM2610/AY8910 chip"); /* Timer Handler set */ //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); /* stream system initialize */ //info->stream = stream_create(device,0,2,rate,info,(type == SOUND_YM2610) ? ym2610_stream_update : ym2610b_stream_update); /* setup adpcm buffers */ //pcmbufa = device->region; //pcmsizea = device->regionbytes; //astring_printf(name, "%s.deltat", device->tag); //pcmbufb = (void *)(memory_region(device->machine, astring_c(name))); //pcmsizeb = memory_region_length(device->machine, astring_c(name)); //astring_free(name); /*if (pcmbufb == NULL || pcmsizeb == 0) { pcmbufb = pcmbufa; pcmsizeb = pcmsizea; }*/ /**** initialize YM2610 ****/ //info->chip = ym2610_init(info,device,device->clock,rate, // pcmbufa,pcmsizea,pcmbufb,pcmsizeb, // timer_handler,IRQHandler,&psgintf); info->chip = ym2610_init(info, clock & 0x7FFFFFFF, rate, NULL, NULL, &psgintf); //assert_always(info->chip != NULL, "Error creating YM2610 chip"); //state_save_register_postload(device->machine, ym2610_intf_postload, info); return rate; } //static DEVICE_STOP( ym2610 ) void device_stop_ym2610(UINT8 ChipID) { //ym2610_state *info = get_safe_token(device); ym2610_state* info = &YM2610Data[ChipID]; ym2610_shutdown(info->chip); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->psg); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->psg); break; } info->psg = NULL; } } //static DEVICE_RESET( ym2610 ) void device_reset_ym2610(UINT8 ChipID) { //ym2610_state *info = get_safe_token(device); ym2610_state* info = &YM2610Data[ChipID]; ym2610_reset_chip(info->chip); // also resets the AY clock //psg_reset(info); // already done as a callback in ym2610_reset_chip } //READ8_DEVICE_HANDLER( ym2610_r ) UINT8 ym2610_r(UINT8 ChipID, offs_t offset) { //ym2610_state *info = get_safe_token(device); ym2610_state* info = &YM2610Data[ChipID]; return ym2610_read(info->chip, offset & 3); } //WRITE8_DEVICE_HANDLER( ym2610_w ) void ym2610_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2610_state *info = get_safe_token(device); ym2610_state* info = &YM2610Data[ChipID]; ym2610_write(info->chip, offset & 3, data); } //READ8_DEVICE_HANDLER( ym2610_status_port_a_r ) UINT8 ym2610_status_port_a_r(UINT8 ChipID, offs_t offset) { return ym2610_r(ChipID, 0); } //READ8_DEVICE_HANDLER( ym2610_status_port_b_r ) UINT8 ym2610_status_port_b_r(UINT8 ChipID, offs_t offset) { return ym2610_r(ChipID, 2); } //READ8_DEVICE_HANDLER( ym2610_read_port_r ) UINT8 ym2610_read_port_r(UINT8 ChipID, offs_t offset) { return ym2610_r(ChipID, 1); } //WRITE8_DEVICE_HANDLER( ym2610_control_port_a_w ) void ym2610_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2610_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym2610_control_port_b_w ) void ym2610_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2610_w(ChipID, 2, data); } //WRITE8_DEVICE_HANDLER( ym2610_data_port_a_w ) void ym2610_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2610_w(ChipID, 1, data); } //WRITE8_DEVICE_HANDLER( ym2610_data_port_b_w ) void ym2610_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2610_w(ChipID, 3, data); } void ym2610_set_ay_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES AY_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else AY_EMU_CORE = EC_EMU2149; #endif return; } void ym2610_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ym2610_state* info = &YM2610Data[ChipID]; ym2610_write_pcmrom(info->chip, rom_id, ROMSize, DataStart, DataLength, ROMData); } void ym2610_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY) { ym2610_state* info = &YM2610Data[ChipID]; ym2610_set_mutemask(info->chip, MuteMaskFM); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); break; #endif case EC_EMU2149: PSG_setMask((PSG*)info->psg, MuteMaskAY); break; } } } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2610 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2610_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2610 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2610 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2610 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2610"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ /*DEVICE_GET_INFO( ym2610b ) { switch (state) { // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2610B"); break; default: DEVICE_GET_INFO_CALL(ym2610); break; } }*/ ================================================ FILE: VGMPlay/chips/2610intf.h ================================================ #pragma once #include "fm.h" #include "ay8910.h" #include "emu2149.h" void ym2610_update_request(void *param); /*typedef struct _ym2610_interface ym2610_interface; struct _ym2610_interface { //void ( *handler )( const device_config *device, int irq ); // IRQ handler for the YM2610 void ( *handler )( int irq ); // IRQ handler for the YM2610 };*/ /*READ8_DEVICE_HANDLER( ym2610_r ); WRITE8_DEVICE_HANDLER( ym2610_w ); READ8_DEVICE_HANDLER( ym2610_status_port_a_r ); READ8_DEVICE_HANDLER( ym2610_status_port_b_r ); READ8_DEVICE_HANDLER( ym2610_read_port_r ); WRITE8_DEVICE_HANDLER( ym2610_control_port_a_w ); WRITE8_DEVICE_HANDLER( ym2610_control_port_b_w ); WRITE8_DEVICE_HANDLER( ym2610_data_port_a_w ); WRITE8_DEVICE_HANDLER( ym2610_data_port_b_w ); DEVICE_GET_INFO( ym2610 ); DEVICE_GET_INFO( ym2610b ); #define SOUND_YM2610 DEVICE_GET_INFO_NAME( ym2610 ) #define SOUND_YM2610B DEVICE_GET_INFO_NAME( ym2610b )*/ void ym2610_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ym2610b_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ym2610_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2610(UINT8 ChipID, int clock, UINT8 AYDisable, int* AYrate); void device_stop_ym2610(UINT8 ChipID); void device_reset_ym2610(UINT8 ChipID); UINT8 ym2610_r(UINT8 ChipID, offs_t offset); void ym2610_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2610_status_port_a_r(UINT8 ChipID, offs_t offset); UINT8 ym2610_status_port_b_r(UINT8 ChipID, offs_t offset); UINT8 ym2610_read_port_r(UINT8 ChipID, offs_t offset); void ym2610_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2610_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2610_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2610_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2610_set_ay_emu_core(UINT8 Emulator); void ym2610_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2610_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY); ================================================ FILE: VGMPlay/chips/2612intf.c ================================================ /*************************************************************************** 2612intf.c The YM2612 emulator supports up to 2 chips. Each chip has the following connections: - Status Read / Control Write A - Port Read / Data Write A - Control Write B - Data Write B ***************************************************************************/ #include #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" //#include "sound/fm.h" //#include "sound/2612intf.h" #include "fm.h" #include "2612intf.h" #ifdef ENABLE_ALL_CORES #include "ym2612.h" #include "ym3438.h" #endif #define EC_MAME 0x00 // YM2612 core from MAME (now fixed, so it isn't worse than Gens anymore) #ifdef ENABLE_ALL_CORES #define EC_GENS 0x02 // Gens YM2612 core from in_vgm #define EC_NUKED 0x01 // Nuked YM3438/YM2612 core #endif typedef struct _ym2612_state ym2612_state; struct _ym2612_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; //const ym2612_interface *intf; //const device_config *device; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ym2612_state YM2612Data[MAX_CHIPS]; static int* GensBuf[0x02] = {NULL, NULL}; static UINT8 ChipFlags = 0x00; /*INLINE ym2612_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2612 || sound_get_type(device) == SOUND_YM3438); return (ym2612_state *)device->token; }*/ /*------------------------- TM2612 -------------------------------*/ /* IRQ Handler */ /*static void IRQHandler(void *param,int irq) { ym2612_state *info = (ym2612_state *)param; //if(info->intf->handler) info->intf->handler(info->device, irq); //if(info->intf->handler) info->intf->handler(irq); }*/ /* Timer overflow callback from timer.c */ //static TIMER_CALLBACK( timer_callback_2612_0 ) /*void timer_callback_2612_0(void *ptr, int param) { ym2612_state *info = (ym2612_state *)ptr; ym2612_timer_over(info->chip,0); } //static TIMER_CALLBACK( timer_callback_2612_1 ) void timer_callback_2612_1(void *ptr, int param) { ym2612_state *info = (ym2612_state *)ptr; ym2612_timer_over(info->chip,1); }*/ /*static void timer_handler(void *param,int c,int count,int clock) { ym2612_state *info = (ym2612_state *)param; if( count == 0 ) { // Reset FM Timer //timer_enable(info->timer[c], 0); } else { // Start FM Timer //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); //if (!timer_enable(info->timer[c], 1)) // timer_adjust_oneshot(info->timer[c], period, 0); } }*/ /* update request from fm.c */ void ym2612_update_request(void *param) { ym2612_state *info = (ym2612_state *)param; //stream_update(info->stream); switch(EMU_CORE) { case EC_MAME: ym2612_update_one(info->chip, DUMMYBUF, 0); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_Update(info->chip, DUMMYBUF, 0); YM2612_DacAndTimers_Update(info->chip, DUMMYBUF, 0); break; case EC_NUKED: break; #endif } } /***********************************************************/ /* YM2612 */ /***********************************************************/ //static STREAM_UPDATE( ym2612_stream_update ) void ym2612_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym2612_state *info = (ym2612_state *)param; ym2612_state *info = &YM2612Data[ChipID]; #ifdef ENABLE_ALL_CORES int i; #endif switch(EMU_CORE) { case EC_MAME: ym2612_update_one(info->chip, outputs, samples); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_ClearBuffer(GensBuf, samples); YM2612_Update(info->chip, GensBuf, samples); YM2612_DacAndTimers_Update(info->chip, GensBuf, samples); for (i = 0x00; i < samples; i ++) { outputs[0x00][i] = (stream_sample_t)GensBuf[0x00][i]; outputs[0x01][i] = (stream_sample_t)GensBuf[0x01][i]; } break; case EC_NUKED: OPN2_GenerateStream(info->chip, outputs, samples); break; #endif } } //static STATE_POSTLOAD( ym2612_intf_postload ) /*static void ym2612_intf_postload(UINT8 ChipID) { //ym2612_state *info = (ym2612_state *)param; ym2612_state *info = &YM2612Data[ChipID]; ym2612_postload(info->chip); }*/ //static DEVICE_START( ym2612 ) int device_start_ym2612(UINT8 ChipID, int clock) { //static const ym2612_interface dummy = { 0 }; //ym2612_state *info = get_safe_token(device); ym2612_state *info; int rate; int chiptype; if (ChipID >= MAX_CHIPS) return 0; chiptype = clock&0x80000000; clock&=0x3fffffff; info = &YM2612Data[ChipID]; rate = clock/72; if (! (EMU_CORE == EC_MAME && (ChipFlags & 0x04))) // if not ("double rate" required) rate /= 2; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const ym2612_interface *)device->static_config : &dummy; //info->intf = &dummy; //info->device = device; /* FM init */ /* Timer Handler set */ //info->timer[0] = timer_alloc(device->machine, timer_callback_2612_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_2612_1, info); /* stream system initialize */ //info->stream = stream_create(device,0,2,rate,info,ym2612_stream_update); /**** initialize YM2612 ****/ switch(EMU_CORE) { case EC_MAME: //info->chip = ym2612_init(info,clock,rate,timer_handler,IRQHandler); info->chip = ym2612_init(info, clock, rate, NULL, NULL); break; #ifdef ENABLE_ALL_CORES case EC_GENS: if (GensBuf[0x00] == NULL) { GensBuf[0x00] = malloc(sizeof(int) * 0x100); GensBuf[0x01] = GensBuf[0x00] + 0x80; } info->chip = YM2612_Init(clock, rate, 0x00); YM2612_SetMute(info->chip, 0x80); // Disable SSG-EG break; case EC_NUKED: info->chip = malloc(sizeof(ym3438_t)); if(chiptype) OPN2_SetChipType(ym3438_type_discrete); OPN2_Reset(info->chip, rate, clock); #endif } //assert_always(info->chip != NULL, "Error creating YM2612 chip"); //ym2612_postload(info->chip); //state_save_register_postload(device->machine, ym2612_intf_postload, info); //ym2612_intf_postload(); return rate; } //static DEVICE_STOP( ym2612 ) void device_stop_ym2612(UINT8 ChipID) { //ym2612_state *info = get_safe_token(device); ym2612_state *info = &YM2612Data[ChipID]; switch(EMU_CORE) { case EC_MAME: ym2612_shutdown(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_End(info->chip); if (GensBuf[0x00] != NULL) { free(GensBuf[0x00]); GensBuf[0x00] = NULL; GensBuf[0x01] = NULL; } break; case EC_NUKED: free(info->chip); break; #endif } } //static DEVICE_RESET( ym2612 ) void device_reset_ym2612(UINT8 ChipID) { //ym2612_state *info = get_safe_token(device); ym2612_state *info = &YM2612Data[ChipID]; switch(EMU_CORE) { case EC_MAME: ym2612_reset_chip(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_Reset(info->chip); break; case EC_NUKED: OPN2_Reset(info->chip, 0, 0); break; #endif } } //READ8_DEVICE_HANDLER( ym2612_r ) UINT8 ym2612_r(UINT8 ChipID, offs_t offset) { //ym2612_state *info = get_safe_token(device); ym2612_state *info = &YM2612Data[ChipID]; switch(EMU_CORE) { case EC_MAME: return ym2612_read(info->chip, offset & 3); #ifdef ENABLE_ALL_CORES case EC_GENS: return YM2612_Read(info->chip); case EC_NUKED: return OPN2_Read(info->chip, offset); #endif default: return 0x00; } } //WRITE8_DEVICE_HANDLER( ym2612_w ) void ym2612_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym2612_state *info = get_safe_token(device); ym2612_state *info = &YM2612Data[ChipID]; switch(EMU_CORE) { case EC_MAME: ym2612_write(info->chip, offset & 3, data); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_Write(info->chip, (unsigned char)(offset & 0x03), data); break; case EC_NUKED: OPN2_WriteBuffered(info->chip, offset, data); break; #endif } } /*READ8_DEVICE_HANDLER( ym2612_status_port_a_r ) { return ym2612_r(device, 0); } READ8_DEVICE_HANDLER( ym2612_status_port_b_r ) { return ym2612_r(device, 2); } READ8_DEVICE_HANDLER( ym2612_data_port_a_r ) { return ym2612_r(device, 1); } READ8_DEVICE_HANDLER( ym2612_data_port_b_r ) { return ym2612_r(device, 3); } WRITE8_DEVICE_HANDLER( ym2612_control_port_a_w ) { ym2612_w(device, 0, data); } WRITE8_DEVICE_HANDLER( ym2612_control_port_b_w ) { ym2612_w(device, 2, data); } WRITE8_DEVICE_HANDLER( ym2612_data_port_a_w ) { ym2612_w(device, 1, data); } WRITE8_DEVICE_HANDLER( ym2612_data_port_b_w ) { ym2612_w(device, 3, data); }*/ UINT8 ym2612_status_port_a_r(UINT8 ChipID, offs_t offset) { return ym2612_r(ChipID, 0); } UINT8 ym2612_status_port_b_r(UINT8 ChipID, offs_t offset) { return ym2612_r(ChipID, 2); } UINT8 ym2612_data_port_a_r(UINT8 ChipID, offs_t offset) { return ym2612_r(ChipID, 1); } UINT8 ym2612_data_port_b_r(UINT8 ChipID, offs_t offset) { return ym2612_r(ChipID, 3); } void ym2612_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2612_w(ChipID, 0, data); } void ym2612_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2612_w(ChipID, 2, data); } void ym2612_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2612_w(ChipID, 1, data); } void ym2612_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym2612_w(ChipID, 3, data); } void ym2612_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x03) ? Emulator : 0x00; #else EMU_CORE = EC_MAME; #endif return; } void ym2612_set_options(UINT8 Flags) { ChipFlags = Flags; switch(EMU_CORE) { case EC_MAME: ym2612_setoptions(Flags); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_SetOptions(Flags); break; case EC_NUKED: OPN2_SetOptions(Flags); break; #endif } return; } void ym2612_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ym2612_state *info = &YM2612Data[ChipID]; switch(EMU_CORE) { case EC_MAME: ym2612_set_mutemask(info->chip, MuteMask); break; #ifdef ENABLE_ALL_CORES case EC_GENS: YM2612_SetMute(info->chip, (int)MuteMask); break; case EC_NUKED: OPN2_SetMute(info->chip, MuteMask); break; #endif } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2612 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2612_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2612 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2612 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2612 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2612"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEVICE_GET_INFO( ym3438 ) { switch (state) { // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM3438"); break; default: DEVICE_GET_INFO_CALL(ym2612); break; } }*/ ================================================ FILE: VGMPlay/chips/2612intf.h ================================================ #pragma once void ym2612_update_request(void *param); /*typedef struct _ym2612_interface ym2612_interface; struct _ym2612_interface { //void (*handler)(const device_config *device, int irq); void (*handler)(int irq); };*/ /*READ8_DEVICE_HANDLER( ym2612_r ); WRITE8_DEVICE_HANDLER( ym2612_w ); READ8_DEVICE_HANDLER( ym2612_status_port_a_r ); READ8_DEVICE_HANDLER( ym2612_status_port_b_r ); READ8_DEVICE_HANDLER( ym2612_data_port_a_r ); READ8_DEVICE_HANDLER( ym2612_data_port_b_r ); WRITE8_DEVICE_HANDLER( ym2612_control_port_a_w ); WRITE8_DEVICE_HANDLER( ym2612_control_port_b_w ); WRITE8_DEVICE_HANDLER( ym2612_data_port_a_w ); WRITE8_DEVICE_HANDLER( ym2612_data_port_b_w ); DEVICE_GET_INFO( ym2612 ); #define SOUND_YM2612 DEVICE_GET_INFO_NAME( ym2612 )*/ void ym2612_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2612(UINT8 ChipID, int clock); void device_stop_ym2612(UINT8 ChipID); void device_reset_ym2612(UINT8 ChipID); UINT8 ym2612_r(UINT8 ChipID, offs_t offset); void ym2612_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2612_status_port_a_r(UINT8 ChipID, offs_t offset); UINT8 ym2612_status_port_b_r(UINT8 ChipID, offs_t offset); UINT8 ym2612_data_port_a_r(UINT8 ChipID, offs_t offset); UINT8 ym2612_data_port_b_r(UINT8 ChipID, offs_t offset); void ym2612_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2612_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2612_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2612_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2612_set_emu_core(UINT8 Emulator); void ym2612_set_options(UINT8 Flags); void ym2612_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); /*typedef struct _ym3438_interface ym3438_interface; struct _ym3438_interface { //void (*handler)(const device_config *device, int irq); void (*handler)(int irq); }; #define ym3438_r ym2612_r #define ym3438_w ym2612_w #define ym3438_status_port_a_r ym2612_status_port_a_r #define ym3438_status_port_b_r ym2612_status_port_b_r #define ym3438_data_port_a_r ym2612_data_port_a_r #define ym3438_data_port_b_r ym2612_data_port_b_r #define ym3438_control_port_a_w ym2612_control_port_a_w #define ym3438_control_port_b_w ym2612_control_port_b_w #define ym3438_data_port_a_w ym2612_data_port_a_w #define ym3438_data_port_b_w ym2612_data_port_b_w*/ //DEVICE_GET_INFO( ym3438 ); //#define SOUND_YM3438 DEVICE_GET_INFO_NAME( ym3438 ) ================================================ FILE: VGMPlay/chips/262intf.c ================================================ /*************************************************************************** 262intf.c MAME interface for YMF262 (OPL3) emulator ***************************************************************************/ #include "mamedef.h" //#include "attotime.h" //#include "sndintrf.h" //#include "streams.h" #include "262intf.h" #ifdef ENABLE_ALL_CORES #include "ymf262.h" #endif #define OPLTYPE_IS_OPL3 #include "adlibemu.h" #define EC_DBOPL 0x00 // DosBox OPL (AdLibEmu) #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // YMF262 core from MAME #endif typedef struct _ymf262_state ymf262_state; struct _ymf262_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; //const ymf262_interface *intf; //const device_config *device; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ymf262_state YMF262Data[MAX_CHIPS]; /*INLINE ymf262_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YMF262); return (ymf262_state *)device->token; }*/ static void IRQHandler_262(void *param,int irq) { ymf262_state *info = (ymf262_state *)param; //if (info->intf->handler) (info->intf->handler)(info->device, irq); } /*static TIMER_CALLBACK( timer_callback_262_0 ) { ymf262_state *info = (ymf262_state *)ptr; ymf262_timer_over(info->chip, 0); } static TIMER_CALLBACK( timer_callback_262_1 ) { ymf262_state *info = (ymf262_state *)ptr; ymf262_timer_over(info->chip, 1); }*/ //static void timer_handler_262(void *param,int timer, attotime period) static void timer_handler_262(void *param,int timer, int period) { ymf262_state *info = (ymf262_state *)param; if( period == 0 ) { /* Reset FM Timer */ //timer_enable(info->timer[timer], 0); } else { /* Start FM Timer */ //timer_adjust_oneshot(info->timer[timer], period, 0); } } //static STREAM_UPDATE( ymf262_stream_update ) void ymf262_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ymf262_state *info = (ymf262_state *)param; ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_update_one(info->chip, outputs, samples); break; #endif case EC_DBOPL: adlib_OPL3_getsample(info->chip, outputs, samples); break; } } static void _stream_update(void *param/*, int interval*/) { ymf262_state *info = (ymf262_state *)param; //stream_update(info->stream); switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_update_one(info->chip, DUMMYBUF, 0); break; #endif case EC_DBOPL: adlib_OPL3_getsample(info->chip, DUMMYBUF, 0); break; } } //static DEVICE_START( ymf262 ) int device_start_ymf262(UINT8 ChipID, int clock) { //static const ymf262_interface dummy = { 0 }; //ymf262_state *info = get_safe_token(device); ymf262_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &YMF262Data[ChipID]; rate = clock/288; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const ymf262_interface *)device->static_config : &dummy; //info->intf = &dummy; //info->device = device; /* stream system initialize */ switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->chip = ymf262_init(clock,rate); //assert_always(info->chip != NULL, "Error creating YMF262 chip"); //info->stream = stream_create(device,0,4,rate,info,ymf262_stream_update); /* YMF262 setup */ ymf262_set_timer_handler (info->chip, timer_handler_262, info); ymf262_set_irq_handler (info->chip, IRQHandler_262, info); ymf262_set_update_handler(info->chip, _stream_update, info); //info->timer[0] = timer_alloc(device->machine, timer_callback_262_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_262_1, info); break; #endif case EC_DBOPL: info->chip = adlib_OPL3_init(clock, rate, _stream_update, info); break; } return rate; } //static DEVICE_STOP( ymf262 ) void device_stop_ymf262(UINT8 ChipID) { //ymf262_state *info = get_safe_token(device); ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_shutdown(info->chip); break; #endif case EC_DBOPL: adlib_OPL3_stop(info->chip); break; } } /* reset */ //static DEVICE_RESET( ymf262 ) void device_reset_ymf262(UINT8 ChipID) { //ymf262_state *info = get_safe_token(device); ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_reset_chip(info->chip); break; #endif case EC_DBOPL: adlib_OPL3_reset(info->chip); break; } } //READ8_DEVICE_HANDLER( ymf262_r ) UINT8 ymf262_r(UINT8 ChipID, offs_t offset) { //ymf262_state *info = get_safe_token(device); ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ymf262_read(info->chip, offset & 3); #endif case EC_DBOPL: return adlib_OPL3_reg_read(info->chip, offset & 0x03); default: return 0x00; } } //WRITE8_DEVICE_HANDLER( ymf262_w ) void ymf262_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ymf262_state *info = get_safe_token(device); ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_write(info->chip, offset & 3, data); break; #endif case EC_DBOPL: adlib_OPL3_writeIO(info->chip, offset & 3, data); break; } } //READ8_DEVICE_HANDLER ( ymf262_status_r ) UINT8 ymf262_status_r(UINT8 ChipID, offs_t offset) { return ymf262_r(ChipID, 0); } //WRITE8_DEVICE_HANDLER( ymf262_register_a_w ) void ymf262_register_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ymf262_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ymf262_register_b_w ) void ymf262_register_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ymf262_w(ChipID, 2, data); } //WRITE8_DEVICE_HANDLER( ymf262_data_a_w ) void ymf262_data_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { ymf262_w(ChipID, 1, data); } //WRITE8_DEVICE_HANDLER( ymf262_data_b_w ) void ymf262_data_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { ymf262_w(ChipID, 3, data); } void ymf262_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_DBOPL; #endif return; } void ymf262_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ymf262_state *info = &YMF262Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ymf262_set_mutemask(info->chip, MuteMask); break; #endif case EC_DBOPL: adlib_OPL3_set_mute_mask(info->chip, MuteMask); break; } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ymf262 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ymf262_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymf262 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ymf262 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ymf262 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YMF262"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/262intf.h ================================================ #pragma once /*typedef struct _ymf262_interface ymf262_interface; struct _ymf262_interface { //void (*handler)(const device_config *device, int irq); void (*handler)(int irq); };*/ /*READ8_DEVICE_HANDLER( ymf262_r ); WRITE8_DEVICE_HANDLER( ymf262_w ); READ8_DEVICE_HANDLER ( ymf262_status_r ); WRITE8_DEVICE_HANDLER( ymf262_register_a_w ); WRITE8_DEVICE_HANDLER( ymf262_register_b_w ); WRITE8_DEVICE_HANDLER( ymf262_data_a_w ); WRITE8_DEVICE_HANDLER( ymf262_data_b_w ); DEVICE_GET_INFO( ymf262 ); #define SOUND_YMF262 DEVICE_GET_INFO_NAME( ymf262 )*/ void ymf262_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ymf262(UINT8 ChipID, int clock); void device_stop_ymf262(UINT8 ChipID); void device_reset_ymf262(UINT8 ChipID); UINT8 ymf262_r(UINT8 ChipID, offs_t offset); void ymf262_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ymf262_status_r(UINT8 ChipID, offs_t offset); void ymf262_register_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf262_register_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf262_data_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf262_data_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf262_set_emu_core(UINT8 Emulator); void ymf262_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/3526intf.c ================================================ /****************************************************************************** * FILE * Yamaha 3812 emulator interface - MAME VERSION * * CREATED BY * Ernesto Corvi * * UPDATE LOG * JB 28-04-2002 Fixed simultaneous usage of all three different chip types. * Used real sample rate when resample filter is active. * AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. * CHS 1999-01-09 Fixes new ym3812 emulation interface. * CHS 1998-10-23 Mame streaming sound chip update * EC 1998 Created Interface * * NOTES * ******************************************************************************/ #include "mamedef.h" //#include "attotime.h" //#include "sndintrf.h" //#include "streams.h" //#include "cpuintrf.h" #include "3526intf.h" #include "fmopl.h" typedef struct _ym3526_state ym3526_state; struct _ym3526_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; //const ym3526_interface *intf; //const device_config *device; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static ym3526_state YM3526Data[MAX_CHIPS]; /*INLINE ym3526_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM3526); return (ym3526_state *)device->token; }*/ /* IRQ Handler */ static void IRQHandler(void *param,int irq) { ym3526_state *info = (ym3526_state *)param; //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); } /* Timer overflow callback from timer.c */ /*static TIMER_CALLBACK( timer_callback_0 ) { ym3526_state *info = (ym3526_state *)ptr; ym3526_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_1 ) { ym3526_state *info = (ym3526_state *)ptr; ym3526_timer_over(info->chip,1); }*/ /* TimerHandler from fm.c */ //static void TimerHandler(void *param,int c,attotime period) static void TimerHandler(void *param,int c,int period) { ym3526_state *info = (ym3526_state *)param; //if( attotime_compare(period, attotime_zero) == 0 ) if( period == 0 ) { /* Reset FM Timer */ //timer_enable(info->timer[c], 0); } else { /* Start FM Timer */ //timer_adjust_oneshot(info->timer[c], period, 0); } } //static STREAM_UPDATE( ym3526_stream_update ) void ym3526_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym3526_state *info = (ym3526_state *)param; ym3526_state *info = &YM3526Data[ChipID]; ym3526_update_one(info->chip, outputs, samples); } static void _stream_update(void *param/*, int interval*/) { ym3526_state *info = (ym3526_state *)param; //stream_update(info->stream); ym3526_update_one(info->chip, DUMMYBUF, 0); } //static DEVICE_START( ym3526 ) int device_start_ym3526(UINT8 ChipID, int clock) { //static const ym3526_interface dummy = { 0 }; //ym3526_state *info = get_safe_token(device); ym3526_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &YM3526Data[ChipID]; rate = clock/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const ym3526_interface *)device->static_config : &dummy; //info->intf = &dummy; //info->device = device; /* stream system initialize */ info->chip = ym3526_init(clock,rate); //assert_always(info->chip != NULL, "Error creating YM3526 chip"); //info->stream = stream_create(device,0,1,rate,info,ym3526_stream_update); /* YM3526 setup */ ym3526_set_timer_handler (info->chip, TimerHandler, info); ym3526_set_irq_handler (info->chip, IRQHandler, info); ym3526_set_update_handler(info->chip, _stream_update, info); //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); return rate; } //static DEVICE_STOP( ym3526 ) void device_stop_ym3526(UINT8 ChipID) { //ym3526_state *info = get_safe_token(device); ym3526_state *info = &YM3526Data[ChipID]; ym3526_shutdown(info->chip); } //static DEVICE_RESET( ym3526 ) void device_reset_ym3526(UINT8 ChipID) { //ym3526_state *info = get_safe_token(device); ym3526_state *info = &YM3526Data[ChipID]; ym3526_reset_chip(info->chip); } //READ8_DEVICE_HANDLER( ym3526_r ) UINT8 ym3526_r(UINT8 ChipID, offs_t offset) { //ym3526_state *info = get_safe_token(device); ym3526_state *info = &YM3526Data[ChipID]; return ym3526_read(info->chip, offset & 1); } //WRITE8_DEVICE_HANDLER( ym3526_w ) void ym3526_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym3526_state *info = get_safe_token(device); ym3526_state *info = &YM3526Data[ChipID]; ym3526_write(info->chip, offset & 1, data); } //READ8_DEVICE_HANDLER( ym3526_status_port_r ) UINT8 ym3526_status_port_r(UINT8 ChipID, offs_t offset) { return ym3526_r(ChipID, 0); } //READ8_DEVICE_HANDLER( ym3526_read_port_r ) UINT8 ym3526_read_port_r(UINT8 ChipID, offs_t offset) { return ym3526_r(ChipID, 1); } //WRITE8_DEVICE_HANDLER( ym3526_control_port_w ) void ym3526_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym3526_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym3526_write_port_w ) void ym3526_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym3526_w(ChipID, 1, data); } void ym3526_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ym3526_state *info = &YM3526Data[ChipID]; opl_set_mute_mask(info->chip, MuteMask); } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym3526 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym3526_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym3526 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym3526 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym3526 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM3526"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/3526intf.h ================================================ #pragma once /*typedef struct _ym3526_interface ym3526_interface; struct _ym3526_interface { //void (*handler)(const device_config *device, int linestate); void (*handler)(int linestate); };*/ /*READ8_DEVICE_HANDLER( ym3526_r ); WRITE8_DEVICE_HANDLER( ym3526_w ); READ8_DEVICE_HANDLER( ym3526_status_port_r ); READ8_DEVICE_HANDLER( ym3526_read_port_r ); WRITE8_DEVICE_HANDLER( ym3526_control_port_w ); WRITE8_DEVICE_HANDLER( ym3526_write_port_w ); DEVICE_GET_INFO( ym3526 ); #define SOUND_YM3526 DEVICE_GET_INFO_NAME( ym3526 )*/ void ym3526_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym3526(UINT8 ChipID, int clock); void device_stop_ym3526(UINT8 ChipID); void device_reset_ym3526(UINT8 ChipID); UINT8 ym3526_r(UINT8 ChipID, offs_t offset); void ym3526_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym3526_status_port_r(UINT8 ChipID, offs_t offset); UINT8 ym3526_read_port_r(UINT8 ChipID, offs_t offset); void ym3526_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym3526_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym3526_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/3812intf.c ================================================ /****************************************************************************** * FILE * Yamaha 3812 emulator interface - MAME VERSION * * CREATED BY * Ernesto Corvi * * UPDATE LOG * JB 28-04-2002 Fixed simultaneous usage of all three different chip types. * Used real sample rate when resample filter is active. * AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. * CHS 1999-01-09 Fixes new ym3812 emulation interface. * CHS 1998-10-23 Mame streaming sound chip update * EC 1998 Created Interface * * NOTES * ******************************************************************************/ #include // for NULL #include "mamedef.h" //#include "attotime.h" //#include "sndintrf.h" //#include "streams.h" //#include "cpuintrf.h" #include "3812intf.h" #ifdef ENABLE_ALL_CORES #include "fmopl.h" #endif #define OPLTYPE_IS_OPL2 #include "adlibemu.h" #define EC_DBOPL 0x00 // DosBox OPL (AdLibEmu) #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // YM3826 core from MAME #endif typedef struct _ym3812_state ym3812_state; struct _ym3812_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; //const ym3812_interface *intf; //const device_config *device; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ym3812_state YM3812Data[MAX_CHIPS]; /*INLINE ym3812_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM3812); return (ym3812_state *)device->token; }*/ static void IRQHandler(void *param,int irq) { ym3812_state *info = (ym3812_state *)param; //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); } /*static TIMER_CALLBACK( timer_callback_0 ) { ym3812_state *info = (ym3812_state *)ptr; ym3812_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_1 ) { ym3812_state *info = (ym3812_state *)ptr; ym3812_timer_over(info->chip,1); }*/ //static void TimerHandler(void *param,int c,attotime period) static void TimerHandler(void *param,int c,int period) { ym3812_state *info = (ym3812_state *)param; //if( attotime_compare(period, attotime_zero) == 0 ) if( period == 0 ) { /* Reset FM Timer */ //timer_enable(info->timer[c], 0); } else { /* Start FM Timer */ //timer_adjust_oneshot(info->timer[c], period, 0); } } //static STREAM_UPDATE( ym3812_stream_update ) void ym3812_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ym3812_state *info = (ym3812_state *)param; ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym3812_update_one(info->chip, outputs, samples); break; #endif case EC_DBOPL: adlib_OPL2_getsample(info->chip, outputs, samples); break; } } static void _stream_update(void * param/*, int interval*/) { ym3812_state *info = (ym3812_state *)param; //stream_update(info->stream); switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym3812_update_one(info->chip, DUMMYBUF, 0); break; #endif case EC_DBOPL: adlib_OPL2_getsample(info->chip, DUMMYBUF, 0); break; } } //static DEVICE_START( ym3812 ) int device_start_ym3812(UINT8 ChipID, int clock) { //static const ym3812_interface dummy = { 0 }; //ym3812_state *info = get_safe_token(device); ym3812_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &YM3812Data[ChipID]; rate = (clock & 0x7FFFFFFF)/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const ym3812_interface *)device->static_config : &dummy; //info->intf = &dummy; //info->device = device; /* stream system initialize */ switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->chip = ym3812_init(clock & 0x7FFFFFFF,rate); //assert_always(info->chip != NULL, "Error creating YM3812 chip"); //info->stream = stream_create(device,0,1,rate,info,ym3812_stream_update); /* YM3812 setup */ ym3812_set_timer_handler (info->chip, TimerHandler, info); ym3812_set_irq_handler (info->chip, IRQHandler, info); ym3812_set_update_handler(info->chip, _stream_update, info); //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); break; #endif case EC_DBOPL: info->chip = adlib_OPL2_init(clock & 0x7FFFFFFF, rate, _stream_update, info); break; } return rate; } //static DEVICE_STOP( ym3812 ) void device_stop_ym3812(UINT8 ChipID) { //ym3812_state *info = get_safe_token(device); ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym3812_shutdown(info->chip); break; #endif case EC_DBOPL: adlib_OPL2_stop(info->chip); break; } } //static DEVICE_RESET( ym3812 ) void device_reset_ym3812(UINT8 ChipID) { //ym3812_state *info = get_safe_token(device); ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym3812_reset_chip(info->chip); break; #endif case EC_DBOPL: adlib_OPL2_reset(info->chip); break; } } //READ8_DEVICE_HANDLER( ym3812_r ) UINT8 ym3812_r(UINT8 ChipID, offs_t offset) { //ym3812_state *info = get_safe_token(device); ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ym3812_read(info->chip, offset & 1); #endif case EC_DBOPL: return adlib_OPL2_reg_read(info->chip, offset & 0x01); default: return 0x00; } } //WRITE8_DEVICE_HANDLER( ym3812_w ) void ym3812_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ym3812_state *info = get_safe_token(device); ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ym3812_write(info->chip, offset & 1, data); break; #endif case EC_DBOPL: adlib_OPL2_writeIO(info->chip, offset & 1, data); break; } } //READ8_DEVICE_HANDLER( ym3812_status_port_r ) UINT8 ym3812_status_port_r(UINT8 ChipID, offs_t offset) { return ym3812_r(ChipID, 0); } //READ8_DEVICE_HANDLER( ym3812_read_port_r ) UINT8 ym3812_read_port_r(UINT8 ChipID, offs_t offset) { return ym3812_r(ChipID, 1); } //WRITE8_DEVICE_HANDLER( ym3812_control_port_w ) void ym3812_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym3812_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( ym3812_write_port_w ) void ym3812_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { ym3812_w(ChipID, 1, data); } void ym3812_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_DBOPL; #endif return; } void ym3812_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ym3812_state *info = &YM3812Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: opl_set_mute_mask(info->chip, MuteMask); break; #endif case EC_DBOPL: adlib_OPL2_set_mute_mask(info->chip, MuteMask); break; } return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym3812 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym3812_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym3812 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym3812 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym3812 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM3812"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/3812intf.h ================================================ #pragma once /*typedef struct _ym3812_interface ym3812_interface; struct _ym3812_interface { //void (*handler)(const device_config *device, int linestate); void (*handler)(int linestate); };*/ /*READ8_DEVICE_HANDLER( ym3812_r ); WRITE8_DEVICE_HANDLER( ym3812_w ); READ8_DEVICE_HANDLER( ym3812_status_port_r ); READ8_DEVICE_HANDLER( ym3812_read_port_r ); WRITE8_DEVICE_HANDLER( ym3812_control_port_w ); WRITE8_DEVICE_HANDLER( ym3812_write_port_w ); DEVICE_GET_INFO( ym3812 ); #define SOUND_YM3812 DEVICE_GET_INFO_NAME( ym3812 )*/ void ym3812_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym3812(UINT8 ChipID, int clock); void device_stop_ym3812(UINT8 ChipID); void device_reset_ym3812(UINT8 ChipID); UINT8 ym3812_r(UINT8 ChipID, offs_t offset); void ym3812_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym3812_status_port_r(UINT8 ChipID, offs_t offset); UINT8 ym3812_read_port_r(UINT8 ChipID, offs_t offset); void ym3812_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym3812_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym3812_set_emu_core(UINT8 Emulator); void ym3812_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/8950intf.c ================================================ /****************************************************************************** * FILE * Yamaha 3812 emulator interface - MAME VERSION * * CREATED BY * Ernesto Corvi * * UPDATE LOG * JB 28-04-2002 Fixed simultaneous usage of all three different chip types. * Used real sample rate when resample filter is active. * AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. * CHS 1999-01-09 Fixes new ym3812 emulation interface. * CHS 1998-10-23 Mame streaming sound chip update * EC 1998 Created Interface * * NOTES * ******************************************************************************/ #include // for NULL #include "mamedef.h" //#include "attotime.h" //#include "sndintrf.h" //#include "streams.h" //#include "cpuintrf.h" #include "8950intf.h" //#include "fm.h" #include "fmopl.h" typedef struct _y8950_state y8950_state; struct _y8950_state { //sound_stream * stream; //emu_timer * timer[2]; void * chip; //const y8950_interface *intf; //const device_config *device; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static y8950_state Y8950Data[MAX_CHIPS]; /*INLINE y8950_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_Y8950); return (y8950_state *)device->token; }*/ static void IRQHandler(void *param,int irq) { y8950_state *info = (y8950_state *)param; //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); } /*static TIMER_CALLBACK( timer_callback_0 ) { y8950_state *info = (y8950_state *)ptr; y8950_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_1 ) { y8950_state *info = (y8950_state *)ptr; y8950_timer_over(info->chip,1); }*/ //static void TimerHandler(void *param,int c,attotime period) static void TimerHandler(void *param,int c,int period) { y8950_state *info = (y8950_state *)param; //if( attotime_compare(period, attotime_zero) == 0 ) if( period == 0 ) { /* Reset FM Timer */ //timer_enable(info->timer[c], 0); } else { /* Start FM Timer */ //timer_adjust_oneshot(info->timer[c], period, 0); } } static unsigned char Y8950PortHandler_r(void *param) { y8950_state *info = (y8950_state *)param; /*if (info->intf->portread) return info->intf->portread(0);*/ return 0; } static void Y8950PortHandler_w(void *param,unsigned char data) { y8950_state *info = (y8950_state *)param; /*if (info->intf->portwrite) info->intf->portwrite(0,data);*/ } static unsigned char Y8950KeyboardHandler_r(void *param) { y8950_state *info = (y8950_state *)param; /*if (info->intf->keyboardread) return info->intf->keyboardread(0);*/ return 0; } static void Y8950KeyboardHandler_w(void *param,unsigned char data) { y8950_state *info = (y8950_state *)param; /*if (info->intf->keyboardwrite) info->intf->keyboardwrite(0,data);*/ } //static STREAM_UPDATE( y8950_stream_update ) void y8950_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //y8950_state *info = (y8950_state *)param; y8950_state *info = &Y8950Data[ChipID]; y8950_update_one(info->chip, outputs, samples); } static void _stream_update(void *param/*, int interval*/) { y8950_state *info = (y8950_state *)param; //stream_update(info->stream); y8950_update_one(info->chip, DUMMYBUF, 0); } //static DEVICE_START( y8950 ) int device_start_y8950(UINT8 ChipID, int clock) { //static const y8950_interface dummy = { 0 }; //y8950_state *info = get_safe_token(device); y8950_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &Y8950Data[ChipID]; rate = clock/72; if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; //info->intf = device->static_config ? (const y8950_interface *)device->static_config : &dummy; //info->intf = &dummy; //info->device = device; /* stream system initialize */ info->chip = y8950_init(clock,rate); //assert_always(info->chip != NULL, "Error creating Y8950 chip"); /* ADPCM ROM data */ //y8950_set_delta_t_memory(info->chip, device->region, device->regionbytes); y8950_set_delta_t_memory(info->chip, NULL, 0x00); //info->stream = stream_create(device,0,1,rate,info,y8950_stream_update); /* port and keyboard handler */ y8950_set_port_handler(info->chip, Y8950PortHandler_w, Y8950PortHandler_r, info); y8950_set_keyboard_handler(info->chip, Y8950KeyboardHandler_w, Y8950KeyboardHandler_r, info); /* Y8950 setup */ y8950_set_timer_handler (info->chip, TimerHandler, info); y8950_set_irq_handler (info->chip, IRQHandler, info); y8950_set_update_handler(info->chip, _stream_update, info); //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); return rate; } //static DEVICE_STOP( y8950 ) void device_stop_y8950(UINT8 ChipID) { //y8950_state *info = get_safe_token(device); y8950_state *info = &Y8950Data[ChipID]; y8950_shutdown(info->chip); } //static DEVICE_RESET( y8950 ) void device_reset_y8950(UINT8 ChipID) { //y8950_state *info = get_safe_token(device); y8950_state *info = &Y8950Data[ChipID]; y8950_reset_chip(info->chip); } //READ8_DEVICE_HANDLER( y8950_r ) UINT8 y8950_r(UINT8 ChipID, offs_t offset) { //y8950_state *info = get_safe_token(device); y8950_state *info = &Y8950Data[ChipID]; return y8950_read(info->chip, offset & 1); } //WRITE8_DEVICE_HANDLER( y8950_w ) void y8950_w(UINT8 ChipID, offs_t offset, UINT8 data) { //y8950_state *info = get_safe_token(device); y8950_state *info = &Y8950Data[ChipID]; y8950_write(info->chip, offset & 1, data); } //READ8_DEVICE_HANDLER( y8950_status_port_r ) UINT8 y8950_status_port_r(UINT8 ChipID, offs_t offset) { return y8950_r(ChipID, 0); } //READ8_DEVICE_HANDLER( y8950_read_port_r ) UINT8 y8950_read_port_r(UINT8 ChipID, offs_t offset) { return y8950_r(ChipID, 1); } //WRITE8_DEVICE_HANDLER( y8950_control_port_w ) void y8950_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { y8950_w(ChipID, 0, data); } //WRITE8_DEVICE_HANDLER( y8950_write_port_w ) void y8950_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { y8950_w(ChipID, 1, data); } void y8950_write_data_pcmrom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { y8950_state* info = &Y8950Data[ChipID]; y8950_write_pcmrom(info->chip, ROMSize, DataStart, DataLength, ROMData); return; } void y8950_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { y8950_state *info = &Y8950Data[ChipID]; opl_set_mute_mask(info->chip, MuteMask); } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( y8950 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(y8950_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( y8950 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( y8950 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( y8950 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "Y8950"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/8950intf.h ================================================ #pragma once /*typedef struct _y8950_interface y8950_interface; struct _y8950_interface { //void (*handler)(const device_config *device, int linestate); void (*handler)(int linestate); read8_device_func keyboardread; write8_device_func keyboardwrite; read8_device_func portread; write8_device_func portwrite; };*/ /*READ8_DEVICE_HANDLER( y8950_r ); WRITE8_DEVICE_HANDLER( y8950_w ); READ8_DEVICE_HANDLER( y8950_status_port_r ); READ8_DEVICE_HANDLER( y8950_read_port_r ); WRITE8_DEVICE_HANDLER( y8950_control_port_w ); WRITE8_DEVICE_HANDLER( y8950_write_port_w ); DEVICE_GET_INFO( y8950 ); #define SOUND_Y8950 DEVICE_GET_INFO_NAME( y8950 )*/ void y8950_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_y8950(UINT8 ChipID, int clock); void device_stop_y8950(UINT8 ChipID); void device_reset_y8950(UINT8 ChipID); UINT8 y8950_r(UINT8 ChipID, offs_t offset); void y8950_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 y8950_status_port_r(UINT8 ChipID, offs_t offset); UINT8 y8950_read_port_r(UINT8 ChipID, offs_t offset); void y8950_control_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void y8950_write_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void y8950_write_data_pcmrom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void y8950_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/ChipIncl.h ================================================ // includes all chip-headers #include "sn764intf.h" #include "2413intf.h" #include "2612intf.h" #include "2151intf.h" #include "segapcm.h" #include "rf5c68.h" #include "2203intf.h" #include "2608intf.h" #include "2610intf.h" #include "3812intf.h" #include "3526intf.h" #include "8950intf.h" #include "262intf.h" #include "ymz280b.h" #include "ymf271.h" #include "ymf278b.h" #include "scd_pcm.h" #include "pwm.h" #include "ay_intf.h" #include "dac_control.h" #include "gb.h" #include "nes_intf.h" #include "multipcm.h" #include "upd7759.h" #include "okim6258.h" #include "okim6295.h" #include "k051649.h" #include "k054539.h" #include "c6280intf.h" #include "c140.h" #include "k053260.h" #include "pokey.h" #include "qsound_intf.h" #include "scsp.h" #include "ws_audio.h" #include "vsu.h" #include "saa1099.h" #include "es5503.h" #include "es5506.h" #include "x1_010.h" #include "c352.h" #include "iremga20.h" ================================================ FILE: VGMPlay/chips/Ootake_PSG.c ================================================ /****************************************************************************** Ootake ・キューの参照処理をシンプルにした。テンポの安定性および音質の向上。 ・オーバーサンプリングしないようにした。(筆者の主観もあるが、PSGの場合、響きの 美しさが損なわれてしまうケースが多いため。速度的にもアップ) ・ノイズの音質・音量を実機並みに調整した。v0.72 ・ノイズの周波数に0x1Fが書き込まれたときは、0x1Eと同じ周波数で音量を半分にして 鳴らすようにした。v0.68 ・現状は再生サンプルレートは44.1KHz固定とした。(CD-DA再生時の速度アップのため) ・DDA音の発声が終了したときにいきなり波形を0にせず、フェードアウトさせるように し、ノイズを軽減した。v0.57 ・DDAモード(サンプリング発声)のときの波形データのノイズが多く含まれている部分 をカットしして、音質を上げた。音量も調節した。v0.59 ・ノイズ音の音質・音量を調整して、実機の雰囲気に近づけた。v0.68 ・waveIndexの初期化とDDAモード時の動作を見直して実機の動作に近づけた。v0.63 ・waveIndexの初期化時にwaveテーブルも初期化するようにした。ファイヤープロレス リング、F1トリプルバトルなどの音が実機に近づいた。v0.65 ・waveの波形の正負を実機同様にした。v0.74 ・waveの最小値が-14になるようにし音質を整えた。v0.74 ・クリティカルセクションは必要ない(書き込みが同時に行われるわけではない)ような ので、省略し高速化した。v1.09 ・キュー処理(ApuQueue.c)をここに統合して高速化した。v1.10 ・低音領域のボリュームを上げて実機並みの聞こえやすさに近づけた。v1.46 ・LFO処理のの実装。"はにいいんざすかい"のOPや、フラッシュハイダースの効果音が 実機の音に近づいた。v1.59 Copyright(C)2006-2012 Kitao Nakamura. 改造版・後継版を公開なさるときは必ずソースコードを添付してください。 その際に事後でかまいませんので、ひとことお知らせいただけると幸いです。 商的な利用は禁じます。 あとは「GNU General Public License(一般公衆利用許諾契約書)」に準じます。 ******************************************************************************* [PSG.c] PSGを実装します。 Implements the PSG. Copyright (C) 2004 Ki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ******************************************************************************/ #include #include #include // for memset #include #include "mamedef.h" #include "Ootake_PSG.h" //#include "MainBoard.h" //Kitao追加 //#include "App.h" //Kitao追加 //#include "PRINTF.h" //Kitao追加 //#define PRINTF printf #define N_CHANNEL 6 //#define SAMPLE_RATE 44100.0 //Kitao更新。現状は速度優先でサンプルレートを44100固定とした。 #define OVERSAMPLE_RATE 1.0 //Kitao更新。PSGはオーバーサンプリングすると響きの美しさが損なわれてしまうのでオーバーサンプリングしないようにした。速度的にもアップ。 #define PSG_DECLINE (21.8500*6.0) //21.8500。Kitao追加。PSG音量の減少値。*6.0は各チャンネル足したぶんを割る意味。大きいほど音は減る。CDDAが100%のときにちょうど良いぐらいの音量に合わせよう。v2.19,v2.37,v2.39,v2.62更新 #define VOL_TABLE_DECLINE -1.05809999010 //-1.05809999010で雀探物語2OK。Kitao追加。音量テーブルの減少値。マイナスが大きいほど小さい音が聞こえづらくなる。マイナスが小さすぎると平面的な音になる。v2.19,v2.37,v2.39,v2.40,v2.62,v2.65更新 // ※PSG_DECLINEの値を変更した場合、減退率のベスト値も変更する必要がある。雀探物語2(マイナスが小さいとPSGが目立ちすぎてADPCMが聴きづらい),大魔界村(マイナスが大きいと音篭り),ソルジャーブレイドで、PSG_DECLINE=(14.4701*6.0)で減退率-1.0498779900db前後が飛び抜けていい響き(うちの環境で主観)。 // モトローダー(マイナスやや大き目がいい),1941(マイナス小さめがいい)なども微妙な値変更で大きく変わる。 #define NOISE_TABLE_VALUE -18 : -1 //キレと聴きやすさで-18:-1をベストとした。最大値が大きい(+に近い)と重い音に。2つの値が離れていると重い音に。フォーメーションサッカー,大魔界村のエンディングのドラムなどで調整。v1.46,v2.40,v2.62更新 // ※VOL_TABLE_DECLINEによってこの値の最適値も変化する。 #define SAMPLE_FADE_DECLINE 0.305998999951 //0.30599899951。Kitao追加。サンプリング音の消音時の音の減退量。ソルジャーブレイド,将棋初心者無用の音声で調整。基本的にこの値が小さいほうがノイズが減る(逆のケースもある)。v2.40 // サンプリングドラムの音色が決まるので大事な値。値が大きすぎるとファイナルソルジャーやソルジャーブレイド,モトローダーなどでドラムがしょぼくなる。 //#define RESMPL_RATE PSG_FRQ / OVERSAMPLE_RATE / SAMPLE_RATE // the lack of () is intentional /*----------------------------------------------------------------------------- [DEV NOTE] MAL --- 0 - 15 (15 で -0[dB], 1減るごとに -3.0 [dB]) AL --- 0 - 31 (31 で -0[dB], 1減るごとに -1.5 [dB]) LAL/RAL --- 0 - 15 (15 で -0[dB], 1減るごとに -3.0 [dB]) 次のように解釈しなおす。 MAL*2 --- 0 - 30 (30 で -0[dB], 1減るごとに -1.5 [dB]) AL --- 0 - 31 (31 で -0[dB], 1減るごとに -1.5 [dB]) LAL/RAL*2 --- 0 - 30 (30 で -0[dB], 1減るごとに -1.5 [dB]) dB = 20 * log10(OUT/IN) dB / 20 = log10(OUT/IN) OUT/IN = 10^(dB/20) IN(最大出力) を 1.0 とすると、 OUT = 10^(dB/20) -91 <= -(MAL*2 + AL + LAL(RAL)*2) <= 0 だから、最も小さい音は、 -91 * 1.5 [dB] = -136.5 [dB] = 10^(-136.5/20) ~= 1.496236e-7 [倍] となる。 1e-7 オーダーの値は、固定小数点で表現しようとすると、小数部だけで 24 ビット以上必要で、なおかつ16ビットの音声を扱うためには +16ビット だから 24+16 = 40ビット以上必要になる。よって、32 ビットの処理系で PCEの音声を固定小数点で表現するのはつらい。そこで、波形の計算は float で行なうことにする。 float から出力形式に変換するのはAPUの仕事とする。 [2004.4.28] やっぱり Sint32 で実装することにした(微小な値は無視する)。 CPUとPSGは同じICにパッケージしてあるのだが、 実際にはPSGはCPUの1/2のクロックで動作すると考えて良いようだ。 よって、PSGの動作周波数 Fpsg は、 Fpsg = 21.47727 [MHz] / 3 / 2 = 3.579545 [MHz] となる。 たとえば32サンプルを1周期とする波形が再生されるとき、 この周波数の周期でサンプルを1つずつ拾い出すと、 M = 3579545 / 32 = 111860.78125 [Hz] というマジックナンバーが得られる(ファミコンと同じ)。 ただし、再生周波数が固定では曲の演奏ができないので、 FRQ なる周波数パラメータを用いて再生周波数を変化させる。 FRQ はPSGのレジスタに書き込まれる12ビット長のパラメータで、 ↑で得られたマジックナンバーの「割る数」になっている。 上の32サンプルを1周期とする波形が再生されるとき、 この波形の周波数 F は、FRQ を用いて、 F = M / FRQ [Hz] (FRQ != 0) となる。 PCの再生サンプリング周波数が Fpc [Hz] だとすると、 1周期32サンプルの波形の再生周波数 F2 は F2 = Fpc / 32 [Hz]。 よって、PCの1サンプルに対して、PCEの1サンプルを拾い出す カウンタの進み幅 I は I = F / F2 = 32 * F / Fpc = Fpsg / FRQ / Fpc [単位なし] となる。 [NOISE CHANNEL] 擬似ノイズの生成にはM系列(maximum length sequence)が用いられる。 M系列のビット長は未調査につき不明。 ここでは仮に15ビットとして実装を行なう。 出力は1ビットで、D0 がゼロのときは負の値、1のときは正の値とする。 PCの1サンプルに対して、PCEの1サンプルを拾い出す カウンタの進み幅 I は、 I = Fpsg / 64 / FRQ / Fpc (FRQ != 0) となる。 [再生クオリティ向上について] 2004.6.22 エミュレータでは、PSGのレジスタにデータが書き込まれるまで、 次に発声すべき音がわからない。レジスタにデータが書き込まれたときに、 サウンドバッファを更新したいのだけど、あいにく現在の実装では、 サウンドバッファの更新は別スレッドで行なわれていて、 エミュレーションスレッドから任意の時間に更新することができない。 これまでの再生では、サウンドバッファの更新時のレジスタ設定のみが 有効だったが、これだと例えばサウンドバッファ更新の合間に一瞬だけ 出力された音などが無視されてしまう。これは特にDDAモードやノイズが リズムパートとして使用される上で問題になる。 レジスタに書き込まれた値をきちんと音声出力に反映させるには、 過去に書き込まれたレジスタの値(いつ、どのレジスタに、何が書き込まれたか) を保存しておいて、サウンドバッファ更新時にこれを参照する方法が 考えられる。どのくらい過去までレジスタの値を保存しておくかは、 サウンドバッファの長さにもよると思われるが、とりあえずは試行錯誤で 決めることにする。 PSGレジスタへの書き込み動作はエミュレーションスレッドで 行なわれ、サウンドバッファ更新はその専用スレッドで行なわれる。 これだと、エミュレーションスレッドがレジスタのキューに書き込みを 行なっている最中に、サウンドバッファ更新スレッドがキューから 読み出しを行なってしまい、アクセスが衝突する。この問題を解決するには、 1.サウンドバッファの更新を別スレッドで行なわない 2.キューのアクセス部分を排他処理にする の2とおりが考えられる。とりあえず2の方法をとることにする。 ---------------------------------------------------------------------------*/ typedef struct { Uint32 frq; BOOL bOn; BOOL bDDA; Uint32 volume; Uint32 volumeL; Uint32 volumeR; Sint32 outVolumeL; Sint32 outVolumeR; Sint32 wave[32]; Uint32 waveIndex; Sint32 ddaSample; Uint32 phase; Uint32 deltaPhase; BOOL bNoiseOn; Uint32 noiseFrq; Uint32 deltaNoisePhase; } PSG; typedef struct { double SAMPLE_RATE; double PSG_FRQ; double RESMPL_RATE; PSG Psg[8]; // 6, 7 is unused Sint32 DdaFadeOutL[8]; //Kitao追加 Sint32 DdaFadeOutR[8]; //Kitao追加 Uint32 Channel; // 0 - 5; Uint32 MainVolumeL; // 0 - 15 Uint32 MainVolumeR; // 0 - 15 Uint32 LfoFrq; BOOL bLfoOn; //v1.59から非使用。過去verのステートロードのために残してある。 Uint32 LfoCtrl; Uint32 LfoShift; //v1.59から非使用。過去verのステートロードのために残してある。 Sint32 PsgVolumeEffect; // = 0;//Kitao追加 double Volume; // = 0;//Kitao追加 double VOL; // = 0.0;//Kitao追加。v1.08 // BOOL _bPsgMute[8] = {FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE};//Kitao追加。v1.29 BOOL bPsgMute[8]; Uint8 Port[16]; // for debug purpose BOOL bWaveCrash; //Kitao追加。DDA再生中にWaveデータが書き換えられたらTRUE BOOL bHoneyInTheSky; //はにいいんざすかいパッチ用。v2.60 } huc6280_state; static Sint32 _VolumeTable[92]; static Sint32 _NoiseTable[32768]; //static BOOL _bPsgInit = FALSE; static BOOL _bTblInit = FALSE; //Kitao更新。v1.10。キュー処理をここに統合して高速化。 /* APU専用キューの仕様 レジスタに書き込みが行なわれるごとに、 キューにその内容を追加する。 サウンドバッファ更新時に経過時間をみて、 過去に書き込まれたレジスタ内容を 書き込まれた順にキューから取り出し、 PSGレジスタを更新する。 (なおPSGレジスタは全て write only とみなす) ↑要確認 キューに追加するときには write index を用い、 取り出すときには read index を用いる。 // 追加 queue[write index++] = written data // 取り出し data = queue[read index++] キューから値を取り出したときに read index が write index と一致したときは queue underflow。 →とりあえずなにもしない。 キューに値を追加したときに write index が read index と一致したときは queue overflow。 →とりあえずリセットすることにする。 */ /*#define APUQUEUE_SIZE 65536*2 // must be power of 2 v1.61更新。65536だとA列車3をオーバークロックしてプレイしたときに足りなかった。 typedef struct //Kitao更新。clockは非使用とした。v1.61からステートセーブのサイズを減らすために変数上からもカット。 { Uint8 reg; // 0-15 Uint8 data; // written data } ApuQueue; typedef struct //v1.60以前のステートロードのため残してある。 { Uint32 clock; // cpu cycles elapsed since previous write Kitao更新。clockは現在非使用。 Uint8 reg; // 0-15 Uint8 data; // written data } OldApuQueue; static ApuQueue _Queue[APUQUEUE_SIZE]; static Uint32 _QueueWriteIndex; static Uint32 _QueueReadIndex;*/ //ボリュームテーブルの作成 //Kitao更新。低音量の音が実機より聞こえづらいので、減退率をVOL_TABLE_DECLINE[db](試行錯誤したベスト値)とし、ノーマライズ処理をするようにした。v1.46 // おそらく、実機もアンプを通って出力される際にノーマライズ処理されている。 static void create_volume_table() { int i; double v; _VolumeTable[0] = 0; //Kitao追加 for (i = 1; i <= 91; i++) { v = 91 - i; _VolumeTable[i] = (Sint32)(32768.0 * pow(10.0, v * VOL_TABLE_DECLINE / 20.0)); //VOL_TABLE_DECLINE。小さくしすぎると音が平面的な傾向に。ソルジャーブレイドで調整。v1.46。 } } //ノイズテーブルの作成 static void create_noise_table() { Sint32 i; Uint32 bit0; Uint32 bit1; Uint32 bit14; Uint32 reg = 0x100; for (i = 0; i < 32768; i++) { bit0 = reg & 1; bit1 = (reg & 2) >> 1; bit14 = (bit0 ^ bit1); reg >>= 1; reg |= (bit14 << 14); _NoiseTable[i] = (bit0) ? NOISE_TABLE_VALUE; //Kitao更新。ノイズのボリュームと音質を調整した。 } } /*----------------------------------------------------------------------------- [write_reg] PSGポートの書き込みに対する動作を記述します。 -----------------------------------------------------------------------------*/ //static inline void INLINE void write_reg( huc6280_state* info, Uint8 reg, Uint8 data) { Uint32 i; Uint32 frq;//Kitao追加 PSG* PSGChn; info->Port[reg & 15] = data; switch (reg & 15) { case 0: // register select info->Channel = data & 7; break; case 1: // main volume info->MainVolumeL = (data >> 4) & 0x0F; info->MainVolumeR = data & 0x0F; /* LMAL, RMAL は全チャネルの音量に影響する */ for (i = 0; i < N_CHANNEL; i++) { PSGChn = &info->Psg[i]; PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; } break; case 2: // frequency low PSGChn = &info->Psg[info->Channel]; PSGChn->frq &= ~0xFF; PSGChn->frq |= data; //Kitao更新。update_frequencyは、速度アップのためサブルーチンにせず直接実行するようにした。 frq = (PSGChn->frq - 1) & 0xFFF; if (frq) PSGChn->deltaPhase = (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)frq +0.5); //Kitao更新。速度アップのためfrq以外は定数計算にした。精度向上のため、先に値の小さいOVERSAMPLE_RATEのほうで割るようにした。+0.5は四捨五入で精度アップ。プチノイズ軽減のため。 else PSGChn->deltaPhase = 0; break; case 3: // frequency high PSGChn = &info->Psg[info->Channel]; PSGChn->frq &= ~0xF00; PSGChn->frq |= (data & 0x0F) << 8; //Kitao更新。update_frequencyは、速度アップのためサブルーチンにせず直接実行するようにした。 frq = (PSGChn->frq - 1) & 0xFFF; if (frq) PSGChn->deltaPhase = (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)frq +0.5); //Kitao更新。速度アップのためfrq以外は定数計算にした。精度向上のため、先に値の小さいOVERSAMPLE_RATEのほうで割るようにした。+0.5は四捨五入で精度アップ。プチノイズ軽減のため。 else PSGChn->deltaPhase = 0; break; case 4: // ON, DDA, AL PSGChn = &info->Psg[info->Channel]; if (info->bHoneyInTheSky) //はにいいんざすかいのポーズ時に、微妙なボリューム調整タイミングの問題でプチノイズが載ってしまうので、現状はパッチ処理で対応。v2.60更新 { if ((PSGChn->bOn)&&(data == 0)) //発声中にdataが0の場合、LRボリュームも0にリセット。はにいいんざすかいのポーズ時のノイズが解消。(data & 0x1F)だけが0のときにリセットすると、サイレントデバッガーズ等でNG。発声してない時にリセットするとアトミックロボでNG。v2.55 { //PRINTF("test %X %X %X %X",info->Channel,PSGChn->bOn,info->MainVolumeL,info->MainVolumeR); if ((info->MainVolumeL & 1) == 0) //メインボリュームのbit0が0のときだけ処理(はにいいんざすかいでイレギュラーな0xE。他のゲームは0xF。※ヘビーユニットも0xEだった)。これがないとミズバク大冒険で音が出ない。実機の仕組みと同じかどうかは未確認。v2.53追加 PSGChn->volumeL = 0; if ((info->MainVolumeR & 1) == 0) //右チャンネルも同様とする PSGChn->volumeR = 0; } } PSGChn->bOn = ((data & 0x80) != 0); if ((PSGChn->bDDA)&&((data & 0x40)==0)) //DDAからWAVEへ切り替わるとき or DDAから消音するとき { //Kitao追加。DDAはいきなり消音すると目立つノイズが載るのでフェードアウトする。 info->DdaFadeOutL[info->Channel] = (Sint32)((double)(PSGChn->ddaSample * PSGChn->outVolumeL) * ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); //元の音量。v2.65更新 info->DdaFadeOutR[info->Channel] = (Sint32)((double)(PSGChn->ddaSample * PSGChn->outVolumeR) * ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); } PSGChn->bDDA = ((data & 0x40) != 0); //Kitao追加。dataのbit7,6が01のときにWaveインデックスをリセットする。 //DDAモード時にWaveデータを書き込んでいた場合はここでWaveデータを修復(初期化)する。ファイヤープロレスリング。 if ((data & 0xC0) == 0x40) { PSGChn->waveIndex = 0; if (info->bWaveCrash) { for (i=0; i<32; i++) PSGChn->wave[i] = -14; //Waveデータを最小値で初期化 info->bWaveCrash = FALSE; } } PSGChn->volume = data & 0x1F; PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; break; case 5: // LAL, RAL PSGChn = &info->Psg[info->Channel]; PSGChn->volumeL = (data >> 4) & 0xF; PSGChn->volumeR = data & 0xF; PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; break; case 6: // wave data //Kitao更新。DDAモードのときもWaveデータを更新するようにした。v0.63。ファイヤープロレスリング PSGChn = &info->Psg[info->Channel]; data &= 0x1F; info->bWaveCrash = FALSE; //Kitao追加 if (!PSGChn->bOn) //Kitao追加。音を鳴らしていないときだけWaveデータを更新する。v0.65。F1トリプルバトルのエンジン音。 { PSGChn->wave[PSGChn->waveIndex++] = 17 - data; //17。Kitao更新。一番心地よく響く値に。ミズバク大冒険,モトローダー,ドラゴンスピリット等で調整。 PSGChn->waveIndex &= 0x1F; } if (PSGChn->bDDA) { //Kitao更新。ノイズ軽減のため6より下側の値はカットするようにした。v0.59 if (data < 6) //サイバーナイトで6に決定 data = 6; //ノイズが多いので小さな値はカット PSGChn->ddaSample = 11 - data; //サイバーナイトで11に決定。ドラムの音色が最適。v0.74 if (!PSGChn->bOn) //DDAモード時にWaveデータを書き換えた場合 info->bWaveCrash = TRUE; } break; case 7: // noise on, noise frq if (info->Channel >= 4) { PSGChn = &info->Psg[info->Channel]; PSGChn->bNoiseOn = ((data & 0x80) != 0); PSGChn->noiseFrq = 0x1F - (data & 0x1F); if (PSGChn->noiseFrq == 0) PSGChn->deltaNoisePhase = (Uint32)((double)(2048.0 * info->RESMPL_RATE) +0.5); //Kitao更新 else PSGChn->deltaNoisePhase = (Uint32)((double)(2048.0 * info->RESMPL_RATE) / (double)PSGChn->noiseFrq +0.5); //Kitao更新 } break; case 8: // LFO frequency info->LfoFrq = data; //Kitaoテスト用 //PRINTF("LFO Frq = %X",info->LfoFrq); break; case 9: // LFO control Kitao更新。シンプルに実装してみた。実機で同じ動作かは未確認。はにいいんざすかいの音が似るように実装。v1.59 if (data & 0x80) //bit7を立てて呼ぶと恐らくリセット { info->Psg[1].phase = 0; //LfoFrqは初期化しない。はにいいんざすかい。 //Kitaoテスト用 //PRINTF("LFO control = %X",data); } info->LfoCtrl = data & 7; //ドロップロックほらホラで5が使われる。v1.61更新 if (info->LfoCtrl & 4) info->LfoCtrl = 0; //ドロップロックほらホラ。実機で聴いた感じはLFOオフと同じ音のようなのでbit2が立っていた(負の数扱い?)ら0と同じこととする。 //Kitaoテスト用 //PRINTF("LFO control = %X, Frq =%X",data,info->LfoFrq); break; default: // invalid write break; } return; } //Kitao追加 static void set_VOL(huc6280_state* info) { //Sint32 v; if (info->PsgVolumeEffect == 0) //info->VOL = 0.0; //ミュート info->VOL = 1.0 / 128.0; else if (info->PsgVolumeEffect == 3) info->VOL = info->Volume / (double)(OVERSAMPLE_RATE * 4.0/3.0); // 3/4。v1.29追加 else info->VOL = info->Volume / (double)(OVERSAMPLE_RATE * info->PsgVolumeEffect); //Kitao追加。_PsgVolumeEffect=ボリューム調節効果。 /*if (!APP_GetCDGame()) //Huカードゲームのときだけ、ボリューム101~120を有効化。v2.62 { v = APP_GetWindowsVolume(); if (v > 100) _VOL *= ((double)(v-100) * 3.0 + 100.0) / 100.0; //101~120は通常の3.0倍の音量変化。3.0倍のVol120でソルジャーブレイド最適。ビックリマンワールドOK。3.1倍以上だと音が薄くなる&音割れの心配もあり。 }*/ } /*----------------------------------------------------------------------------- [Mix] PSGの出力をミックスします。 -----------------------------------------------------------------------------*/ void PSG_Mix( // Sint16* pDst, // 出力先バッファ //Kitao更新。PSG専用バッファにしたためSint16に。 void* chip, Sint32** pDst, Sint32 nSample) // 書き出すサンプル数 { huc6280_state* info = (huc6280_state*)chip; PSG* PSGChn; Sint32 i; Sint32 j; Sint32 sample; //Kitao追加 Sint32 lfo; Sint32 sampleAllL; //Kitao追加。6chぶんのサンプルを足していくためのバッファ。精度を維持するために必要。6chぶん合計が終わった後に、これをSint16に変換して書き込むようにした。 Sint32 sampleAllR; //Kitao追加。上のRチャンネル用 Sint32 smp; //Kitao追加。DDA音量,ノイズ音量計算用 Sint32* bufL = pDst[0]; Sint32* bufR = pDst[1]; // if (!_bPsgInit) // return; for (j=0; jPsg[i]; if ((PSGChn->bOn)&&((i != 1)||(info->LfoCtrl == 0))&&(!info->bPsgMute[i])) //Kitao更新 { if (PSGChn->bDDA) { smp = PSGChn->ddaSample * PSGChn->outVolumeL; sampleAllL += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音の音量を実機並みに調整。v2.39,v2.40,v2.62,v2.65再調整した。 smp = PSGChn->ddaSample * PSGChn->outVolumeR; sampleAllR += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音の音量を実機並みに調整。v2.39,v2.40,v2.62,v2.65再調整した。 } else if (PSGChn->bNoiseOn) { sample = _NoiseTable[PSGChn->phase >> 17]; if (PSGChn->noiseFrq == 0) //Kitao追加。noiseFrq=0(dataに0x1Fが書き込まれた)のときは音量が通常の半分とした。(ファイヤープロレスリング3、パックランド、桃太郎活劇、がんばれゴルフボーイズなど) { smp = sample * PSGChn->outVolumeL; sampleAllL += (smp >> 1) + (smp >> 12) + (smp >> 14); //(1/2 + 1/4096 + (1/32768 + 1/32768)) smp = sample * PSGChn->outVolumeR; sampleAllR += (smp >> 1) + (smp >> 12) + (smp >> 14); } else //通常 { smp = sample * PSGChn->outVolumeL; sampleAllL += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズの音量を実機並みに調整(1 + 1/2048 + 1/16384 + 1/32768)。この"+1/32768"で絶妙(主観。大魔界村,ソルジャーブレイドなど)になる。v2.62更新 smp = sample * PSGChn->outVolumeR; sampleAllR += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズの音量を実機並みに調整 } PSGChn->phase += PSGChn->deltaNoisePhase; //Kitao更新 } else if (PSGChn->deltaPhase) { //Kitao更新。オーバーサンプリングしないようにした。 sample = PSGChn->wave[PSGChn->phase >> 27]; if (PSGChn->frq < 128) sample -= sample >> 2; //低周波域の音量を制限。ブラッドギアのスタート時などで実機と同様の音に。ソルジャーブレイドなども実機に近くなった。v2.03 sampleAllL += sample * PSGChn->outVolumeL; //Kitao更新 sampleAllR += sample * PSGChn->outVolumeR; //Kitao更新 //Kitao更新。Lfoオンが有効になるようにし、Lfoの掛かり具合を実機に近づけた。v1.59 if ((i==0)&&(info->LfoCtrl>0)) { //_LfoCtrlが1のときに0回シフト(そのまま)で、はにいいんざすかいが実機の音に近い。 //_LfoCtrlが3のときに4回シフトで、フラッシュハイダースが実機の音に近い。 lfo = info->Psg[1].wave[info->Psg[1].phase >> 27] << ((info->LfoCtrl-1) << 1); //v1.60更新 info->Psg[0].phase += (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)(info->Psg[0].frq + lfo) +0.5); info->Psg[1].phase += (Uint32)((double)(65536.0 * 256.0 * 8.0 *info-> RESMPL_RATE) / (double)(info->Psg[1].frq * info->LfoFrq) +0.5); //v1.60更新 } else PSGChn->phase += PSGChn->deltaPhase; } } //Kitao追加。DDA消音時はノイズ軽減のためフェードアウトで消音する。 // ベラボーマン(「わしがばくだはかせじゃ」から数秒後)やパワーテニス(タイトル曲終了から数秒後。点数コール),将棋初心者無用(音声)等で効果あり。 if (info->DdaFadeOutL[i] > 0) --info->DdaFadeOutL[i]; else if (info->DdaFadeOutL[i] < 0) ++info->DdaFadeOutL[i]; if (info->DdaFadeOutR[i] > 0) --info->DdaFadeOutR[i]; else if (info->DdaFadeOutR[i] < 0) ++info->DdaFadeOutR[i]; sampleAllL += info->DdaFadeOutL[i]; sampleAllR += info->DdaFadeOutR[i]; } //Kitao更新。6ch合わさったところで、ボリューム調整してバッファに書き込む。 sampleAllL = (Sint32)((double)sampleAllL * info->VOL); //if ((sampleAllL>32767)||(sampleAllL<-32768)) PRINTF("PSG Sachitta!");//test用 // if (sampleAllL> 32767) sampleAllL= 32767; //Volをアップしたのでサチレーションチェックが必要。v2.39 // if (sampleAllL<-32768) sampleAllL=-32768; // パックランドでUFO等にやられたときぐらいで、通常のゲームでは起こらない。音量の大きなビックリマンワールドもOK。パックランドも通常はOKでサチレーションしたときでもわずかなので音質的に大丈夫。 // なので音質的には、PSGを2つのDirectXチャンネルに分けて鳴らすべき(処理は重くなる)だが、現状はパックランドでもサチレーション処理だけで音質的に問題なし(速度優先)とする。 sampleAllR = (Sint32)((double)sampleAllR * info->VOL); //if ((sampleAllR>32767)||(sampleAllR<-32768)) PRINTF("PSG Satitta!");//test用 // if (sampleAllR> 32767) sampleAllR= 32767; //Volをアップしたのでサチレーションチェックが必要。v2.39 // if (sampleAllR<-32768) sampleAllR=-32768; // *bufL++ = sampleAllL; *bufR++ = sampleAllR; //キューを参照しPSGレジスタを更新する。Kitao更新。高速化のためサブルーチンにせずここで処理。キューの参照はシンプルにした(テンポの安定性向上)。 /*while (_QueueReadIndex != _QueueWriteIndex) //v1.10更新。キュー処理をここへ統合し高速化。 { write_reg(_Queue[_QueueReadIndex].reg, _Queue[_QueueReadIndex].data); _QueueReadIndex++; //Kitao更新 _QueueReadIndex &= APUQUEUE_SIZE-1; //Kitao更新 }*/ } } //Kitao更新 static void psg_reset(huc6280_state* info) { int i,j; memset(info->Psg, 0, sizeof(info->Psg)); memset(info->DdaFadeOutL, 0, sizeof(info->DdaFadeOutL)); //Kitao追加 memset(info->DdaFadeOutR, 0, sizeof(info->DdaFadeOutR)); //Kitao追加 info->MainVolumeL = 0; info->MainVolumeR = 0; info->LfoFrq = 0; info->LfoCtrl = 0; info->Channel = 0; //Kitao追加。v2.65 info->bWaveCrash = FALSE; //Kitao追加 //Kitao更新。v0.65.waveデータを初期化。 for (i=0; iPsg[i].wave[j] = -14; //最小値で初期化。ファイプロ,フォーメーションサッカー'90,F1トリプルバトルで必要。 for (j=0; j<32; j++) info->Psg[3].wave[j] = 17; //ch3は最大値で初期化。F1トリプルバトル。v2.65 //Kitao更新。v1.10。キュー処理をここに統合 // _QueueWriteIndex = 0; // _QueueReadIndex = 0; } static void PSG_SetVolume(huc6280_state* info); /*----------------------------------------------------------------------------- [Init] PSGを初期化します。 -----------------------------------------------------------------------------*/ //Sint32 void* PSG_Init( Sint32 clock, Sint32 sampleRate) { huc6280_state* info; info = (huc6280_state*)malloc(sizeof(huc6280_state)); if (info == NULL) return NULL; info->PSG_FRQ = clock & 0x7FFFFFFF; PSG_SetHoneyInTheSky(info, (clock >> 31) & 0x01); // PSG_SetHoneyInTheSky(0x01); info->PsgVolumeEffect = 0; info->Volume = 0; info->VOL = 0.0; //PSG_SetVolume(APP_GetPsgVolume());//Kitao追加 PSG_SetVolume(info); psg_reset(info); if (! _bTblInit) { create_volume_table(); create_noise_table(); _bTblInit = TRUE; } //PSG_SetSampleRate(sampleRate); info->SAMPLE_RATE = sampleRate; info->RESMPL_RATE = info->PSG_FRQ / OVERSAMPLE_RATE / info->SAMPLE_RATE; // _bPsgInit = TRUE; return info; } /*----------------------------------------------------------------------------- [SetSampleRate] -----------------------------------------------------------------------------*/ /*void PSG_SetSampleRate( Uint32 sampleRate) { //_SampleRate = sampleRate; }*/ /*----------------------------------------------------------------------------- [Deinit] PSGを破棄します。 -----------------------------------------------------------------------------*/ void PSG_Deinit(void* chip) { huc6280_state* info = (huc6280_state*)chip; /*memset(info->Psg, 0, sizeof(_Psg)); memset(info->DdaFadeOutL, 0, sizeof(_DdaFadeOutL)); //Kitao追加 memset(info->DdaFadeOutR, 0, sizeof(_DdaFadeOutR)); //Kitao追加 info->MainVolumeL = 0; info->MainVolumeR = 0; info->LfoFrq = 0; info->LfoCtrl = 0; info->bWaveCrash = FALSE; //Kitao追加 // _bPsgInit = FALSE;*/ free(info); } /*----------------------------------------------------------------------------- [Read] PSGポートの読み出しに対する動作を記述します。 -----------------------------------------------------------------------------*/ Uint8 PSG_Read( void* chip, Uint32 regNum) { huc6280_state* info = (huc6280_state*)chip; if (regNum == 0) return (Uint8)info->Channel; return info->Port[regNum & 15]; } /*----------------------------------------------------------------------------- [Write] PSGポートの書き込みに対する動作を記述します。 -----------------------------------------------------------------------------*/ void PSG_Write( void* chip, Uint32 regNum, Uint8 data) { huc6280_state* info = (huc6280_state*)chip; //v1.10更新。キュー処理をここに統合して高速化。 //Kitao更新。clockは考慮せずにシンプルにして高速化した。 /* if (((_QueueWriteIndex + 1) & (APUQUEUE_SIZE-1)) == _QueueReadIndex) { PRINTF("PSG Queue Over!"); // キューが満タン return; } _Queue[_QueueWriteIndex].reg = (Uint8)(regNum & 15); _Queue[_QueueWriteIndex].data = data; _QueueWriteIndex++; //Kitao更新 _QueueWriteIndex &= APUQUEUE_SIZE-1; //Kitao更新 */ write_reg(chip, regNum, data); } /*Sint32 PSG_AdvanceClock( Sint32 clock) { return 0; }*/ //Kitao追加。PSGのボリュームも個別に設定可能にした。 /*static void PSG_SetVolume( Uint32 volume) // 0 - 65535*/ static void PSG_SetVolume(huc6280_state* info) { /*if (volume < 0) volume = 0; if (volume > 65535) volume = 65535;*/ //_Volume = (double)volume / 65535.0 / PSG_DECLINE; info->Volume = 1.0 / PSG_DECLINE; set_VOL(info); } //Kitao追加。ボリュームミュート、ハーフなどをできるようにした。 /*static void PSG_SetVolumeEffect( Uint32 volumeEffect) { _PsgVolumeEffect = (Sint32)volumeEffect; //※数値が大きいほどボリュームは小さくなる set_VOL(); }*/ //Kitao追加 void PSG_ResetVolumeReg(void* chip) { huc6280_state* info = (huc6280_state*)chip; int i; info->MainVolumeL = 0; info->MainVolumeR = 0; for (i = 0; i < N_CHANNEL; i++) { info->Psg[i].volume = 0; info->Psg[i].outVolumeL = 0; info->Psg[i].outVolumeR = 0; info->DdaFadeOutL[i] = 0; info->DdaFadeOutR[i] = 0; } } //Kitao追加 void PSG_SetMutePsgChannel( void* chip, Sint32 num, BOOL bMute) { huc6280_state* info = (huc6280_state*)chip; info->bPsgMute[num] = bMute; if (bMute) { info->DdaFadeOutL[num] = 0; info->DdaFadeOutR[num] = 0; } } void PSG_SetMuteMask(void* chip, Uint32 MuteMask) { Uint8 CurChn; for (CurChn = 0x00; CurChn < N_CHANNEL; CurChn ++) PSG_SetMutePsgChannel(chip, CurChn, (MuteMask >> CurChn) & 0x01); return; } //Kitao追加 BOOL PSG_GetMutePsgChannel( void* chip, Sint32 num) { huc6280_state* info = (huc6280_state*)chip; return info->bPsgMute[num]; } //Kitao追加。v2.60 void PSG_SetHoneyInTheSky( void* chip, BOOL bHoneyInTheSky) { huc6280_state* info = (huc6280_state*)chip; info->bHoneyInTheSky = bHoneyInTheSky; } /*// save variable #define SAVE_V(V) if (fwrite(&V, sizeof(V), 1, p) != 1) return FALSE #define LOAD_V(V) if (fread(&V, sizeof(V), 1, p) != 1) return FALSE // save array #define SAVE_A(A) if (fwrite(A, sizeof(A), 1, p) != 1) return FALSE #define LOAD_A(A) if (fread(A, sizeof(A), 1, p) != 1) return FALSE*/ /*----------------------------------------------------------------------------- [SaveState] 状態をファイルに保存します。 -----------------------------------------------------------------------------*/ /*BOOL PSG_SaveState( FILE* p) { BOOL bFlashHiders = FALSE; //Kitao更新。現在非使用。旧バージョンのステートセーブとの互換のため if (p == NULL) return FALSE; SAVE_A(_Psg); SAVE_V(_Channel); SAVE_V(_MainVolumeL); SAVE_V(_MainVolumeR); SAVE_V(_LfoFrq); SAVE_V(_bLfoOn); //v1.59から非使用に。 SAVE_V(_LfoCtrl); SAVE_V(_LfoShift); //v1.59から非使用に。 SAVE_V(_bWaveCrash); //Kitao追加。v0.65 SAVE_V(bFlashHiders); //Kitao追加。v0.62 //v1.10追加。キュー処理をここへ統合。 SAVE_A(_Queue); //v1.61からサイズが2倍になった。 SAVE_V(_QueueWriteIndex); SAVE_V(_QueueReadIndex); return TRUE; }*/ /*----------------------------------------------------------------------------- [LoadState] 状態をファイルから読み込みます。 -----------------------------------------------------------------------------*/ /*BOOL PSG_LoadState( FILE* p) { Uint32 i; double clockCounter; //Kitao更新。現在非使用。旧バージョンのステートセーブとの互換のため BOOL bInit; //Kitao更新。現在非使用。旧バージョンのステートセーブとの互換のため Sint32 totalClockAdvanced; //Kitao更新。現在非使用。旧バージョンのステートセーブとの互換のため BOOL bFlashHiders; //Kitao更新。現在非使用。旧バージョンのステートセーブとの互換のため OldApuQueue oldQueue[65536]; //v1.60以前のステートを読み込み用。 if (p == NULL) return FALSE; LOAD_A(_Psg); LOAD_V(_Channel); LOAD_V(_MainVolumeL); LOAD_V(_MainVolumeR); LOAD_V(_LfoFrq); LOAD_V(_bLfoOn); //v1.59から非使用に。 LOAD_V(_LfoCtrl); if (MAINBOARD_GetStateVersion() >= 3) //Kitao追加。v0.57以降のセーブファイルなら LOAD_V(_LfoShift); //v1.59から非使用に。 if (MAINBOARD_GetStateVersion() >= 9) //Kitao追加。v0.65以降のセーブファイルなら { LOAD_V(_bWaveCrash); } else _bWaveCrash = FALSE; if (MAINBOARD_GetStateVersion() >= 7) //Kitao追加。v0.62以降のセーブファイルなら LOAD_V(bFlashHiders); //v1.10追加。キュー処理をここへ統合。v1.61更新 if (MAINBOARD_GetStateVersion() >= 34) //v1.61beta以降のセーブファイルなら { LOAD_A(_Queue); //v1.61からサイズが2倍&clock部分を削除した。 LOAD_V(_QueueWriteIndex); LOAD_V(_QueueReadIndex); } else //v1.60以前のキュー(旧)バージョンのステートの場合、新バージョンの方に合うように変換。 { LOAD_A(oldQueue); LOAD_V(_QueueWriteIndex); LOAD_V(_QueueReadIndex); if (_QueueWriteIndex >= _QueueReadIndex) { for (i=_QueueReadIndex; i<=_QueueWriteIndex; i++) { _Queue[i].reg = oldQueue[i].reg; _Queue[i].data = oldQueue[i].data; } } else //Writeの位置がReadの位置よりも前(65536地点をまたいでデータが存在しているとき)の場合 { for (i=_QueueReadIndex; i<=65535; i++) { _Queue[i].reg = oldQueue[i].reg; _Queue[i].data = oldQueue[i].data; } for (i=0; i<=_QueueWriteIndex; i++) { _Queue[65536+i].reg = oldQueue[i].reg; _Queue[65536+i].data = oldQueue[i].data; } _QueueWriteIndex += 65536; } } if (MAINBOARD_GetStateVersion() < 26) //Kitao追加。v1.11より前のセーブファイルなら { LOAD_V(clockCounter); //現在非使用。v0.95 LOAD_V(bInit); //現在非使用。v1.10 LOAD_V(totalClockAdvanced); //現在非使用。v0.95 } return TRUE; } #undef SAVE_V #undef SAVE_A #undef LOAD_V #undef LOAD_A*/ ================================================ FILE: VGMPlay/chips/Ootake_PSG.h ================================================ /*----------------------------------------------------------------------------- [PSG.h] PSGを記述するのに必要な定義および関数のプロトタイプ宣言を行ないます. Copyright (C) 2004 Ki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA **---------------------------------------------------------------------------*/ //#include //#include "TypeDefs.h" typedef UINT8 Uint8; typedef INT16 Sint16; typedef INT32 Sint32; typedef UINT32 Uint32; typedef UINT8 BOOL; #define TRUE 1 #define FALSE 0 //#define PSG_FRQ 3579545.0 /*----------------------------------------------------------------------------- ** 関数のプロトタイプ宣言を行ないます. **---------------------------------------------------------------------------*/ //Sint32 void* PSG_Init( Sint32 clock, Sint32 sampleRate); void PSG_Deinit(void* chip); void PSG_Mix( // Sint16* pDst, // 出力先バッファ void* chip, Sint32** pDst, Sint32 nSample); // 書き出すサンプル数 /*void PSG_SetSampleRate( Uint32 sampleRate); void PSGDEBUG_ShowRegs();*/ Uint8 PSG_Read(void* chip, Uint32 regNum); void PSG_Write( void* chip, Uint32 regNum, Uint8 data); /*Sint32 PSG_AdvanceClock(Sint32 clock); BOOL PSG_SaveState( FILE* p); BOOL PSG_LoadState( FILE* p);*/ //Kitao追加。PSGのボリュームも個別に設定可能にした。 /*void PSG_SetVolume( Uint32 volume); // 0 - 65535 //Kitao追加。ボリュームミュート、ハーフなどをできるようにした。 void PSG_SetVolumeEffect( Uint32 volumeEffect);*/ //Kitao追加 void PSG_ResetVolumeReg(void* chip); //Kitao追加 void PSG_SetMutePsgChannel( void* chip, Sint32 num, BOOL bMute); void PSG_SetMuteMask(void* chip, Uint32 MuteMask); //Kitao追加 BOOL PSG_GetMutePsgChannel( void* chip, Sint32 num); //Kitao追加。v2.60 void PSG_SetHoneyInTheSky( void* chip, BOOL bHoneyInTheSky); ================================================ FILE: VGMPlay/chips/adlibemu.h ================================================ #if defined(OPLTYPE_IS_OPL2) #define ADLIBEMU(name) adlib_OPL2_##name #elif defined(OPLTYPE_IS_OPL3) #define ADLIBEMU(name) adlib_OPL3_##name #endif typedef void (*ADL_UPDATEHANDLER)(void *param); void* ADLIBEMU(init)(UINT32 clock, UINT32 samplerate, ADL_UPDATEHANDLER UpdateHandler, void* param); void ADLIBEMU(stop)(void *chip); void ADLIBEMU(reset)(void *chip); void ADLIBEMU(writeIO)(void *chip, UINT32 addr, UINT8 val); void ADLIBEMU(getsample)(void *chip, INT32 ** sndptr, INT32 numsamples); UINT32 ADLIBEMU(reg_read)(void *chip, UINT32 port); void ADLIBEMU(write_index)(void *chip, UINT32 port, UINT8 val); void ADLIBEMU(set_mute_mask)(void *chip, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/adlibemu_opl2.c ================================================ #include "mamedef.h" #define OPLTYPE_IS_OPL2 #include "adlibemu.h" #include "opl.c" ================================================ FILE: VGMPlay/chips/adlibemu_opl3.c ================================================ #include "mamedef.h" #define OPLTYPE_IS_OPL3 #include "adlibemu.h" #include "opl.c" ================================================ FILE: VGMPlay/chips/ay8910.c ================================================ /*************************************************************************** ay8910.c Emulation of the AY-3-8910 / YM2149 sound chip. Based on various code snippets by Ville Hallik, Michael Cuddy, Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. Mostly rewritten by couriersud in 2008 Public documentation: - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/ Die pictures of the AY8910 - US Patent 4933980 Games using ADSR: gyruss A list with more games using ADSR can be found here: http://mametesters.org/view.php?id=3043 TODO: * The AY8930 has an extended mode which is currently not emulated. * The YMZ284 only has one 1 output channel (mixed chan A,B,C). This should be forced. * YM2610 & YM2608 will need a separate flag in their config structures to distinguish between legacy and discrete mode. The rewrite also introduces a generic model for the DAC. This model is not perfect, but allows channel mixing based on a parametrized approach. This model also allows to factor in different loads on individual channels. If a better model is developped in the future or better measurements are available, the driver should be easy to change. The model is described later. In order to not break hundreds of existing drivers by default the flag AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff). In the case of channel mixing, output range is 0...3 * 7fff. The main difference between the AY-3-8910 and the YM2149 is, that the AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by registers 8 to 10 is "channel off". The YM2149 mentions, that the generated signal has a 2V DC component. This is confirmed by measurements. The approach taken here is to assume the 2V DC offset for all outputs for the YM2149. For the AY-3-8910, an offset is used if envelope is active for a channel. This is backed by oscilloscope pictures from the datasheet. If a fixed volume is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings I found on the web for gyruss indicate, that the AY-3-8910 offset should be around 0.2V. This will also make sound levels more compatible with user observations for scramble. The Model: 5V 5V | | / | Volume Level x >---| Z > Z Pullup Resistor RU | Z Z | Rx Z | Z | | | '-----+--------> >---+----> Output signal | | Z Z Pulldown RD Z Z Load RL Z Z | | GND GND Each Volume level x will select a different resistor Rx. Measurements from fpgaarcade.com where used to calibrate channel mixing for the YM2149. This was done using a least square approach using a fixed RL of 1K Ohm. For the AY measurements cited in e.g. openmsx as "Hacker Kay" for a single channel were taken. These were normalized to 0 ... 65535 and consequently adapted to an offset of 0.2V and a VPP of 1.3V. These measurements are in line e.g. with the formula used by pcmenc for the volume: vol(i) = exp(i/2-7.5). The following is documentation from the code moved here and amended to reflect the changes done: Careful studies of the chip output prove that the chip counts up from 0 until the counter becomes greater or equal to the period. This is an important difference when the program is rapidly changing the period to modulate the sound. This is worthwhile noting, since the datasheets say, that the chip counts down. Also, note that period = 0 is the same as period = 1. This is mentioned in the YM2203 data sheets. However, this does NOT apply to the Envelope period. In that case, period = 0 is half as period = 1. Envelope shapes: C AtAlH 0 0 x x \___ 0 1 x x /___ 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 /___ The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it has twice the steps, happening twice as fast. ***************************************************************************/ #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" //#include "cpuintrf.h" //#include "cpuexec.h" #include #include // for memset #include #include "ay8910.h" /************************************* * * Defines * *************************************/ #define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */ //#define MAX_OUTPUT 0x7fff #define MAX_OUTPUT 0x4000 #define NUM_CHANNELS 3 /* register id's */ #define AY_AFINE (0) #define AY_ACOARSE (1) #define AY_BFINE (2) #define AY_BCOARSE (3) #define AY_CFINE (4) #define AY_CCOARSE (5) #define AY_NOISEPER (6) #define AY_ENABLE (7) #define AY_AVOL (8) #define AY_BVOL (9) #define AY_CVOL (10) #define AY_EFINE (11) #define AY_ECOARSE (12) #define AY_ESHAPE (13) #define AY_PORTA (14) #define AY_PORTB (15) #define NOISE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (3 + _chan)) & 1) #define TONE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (_chan)) & 1) #define TONE_PERIOD(_psg, _chan) ( (_psg)->regs[(_chan) << 1] | (((_psg)->regs[((_chan) << 1) | 1] & 0x0f) << 8) ) #define NOISE_PERIOD(_psg) ( (_psg)->regs[AY_NOISEPER] & 0x1f) #define TONE_VOLUME(_psg, _chan) ( (_psg)->regs[AY_AVOL + (_chan)] & 0x0f) //#define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->device->type() == AY8914) ? 3 : 1)) #define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->chip_type == CHTYPE_AY8914) ? 3 : 1)) #define ENVELOPE_PERIOD(_psg) (((_psg)->regs[AY_EFINE] | ((_psg)->regs[AY_ECOARSE]<<8))) #define NOISE_OUTPUT(_psg) ((_psg)->rng & 1) #define CHTYPE_AY8910 0x00 #define CHTYPE_AY8912 0x01 #define CHTYPE_AY8913 0x02 #define CHTYPE_AY8930 0x03 #define CHTYPE_AY8914 0x04 #define CHTYPE_YM2149 0x10 #define CHTYPE_YM3439 0x11 #define CHTYPE_YMZ284 0x12 #define CHTYPE_YMZ294 0x13 #define CHTYPE_YM2203 0x20 #define CHTYPE_YM2608 0x21 #define CHTYPE_YM2610 0x22 #define CHTYPE_YM2610B 0x23 /************************************* * * Type definitions * *************************************/ typedef struct _ay_ym_param ay_ym_param; struct _ay_ym_param { double r_up; double r_down; int res_count; double res[32]; }; typedef struct _ay8910_context ay8910_context; struct _ay8910_context { //const device_config *device; int streams; int ready; //sound_stream *channel; const ay8910_interface *intf; INT32 register_latch; UINT8 regs[16]; INT32 last_enable; INT32 count[NUM_CHANNELS]; UINT8 output[NUM_CHANNELS]; UINT8 prescale_noise; INT32 count_noise; INT32 count_env; INT8 env_step; UINT32 env_volume; UINT8 hold,alternate,attack,holding; INT32 rng; UINT8 env_step_mask; /* init parameters ... */ int step; int zero_is_off; UINT8 vol_enabled[NUM_CHANNELS]; const ay_ym_param *par; const ay_ym_param *par_env; INT32 vol_table[NUM_CHANNELS][16]; INT32 env_table[NUM_CHANNELS][32]; INT32 vol3d_table[8*32*32*32]; //devcb_resolved_read8 portAread; //devcb_resolved_read8 portBread; //devcb_resolved_write8 portAwrite; //devcb_resolved_write8 portBwrite; UINT8 StereoMask[NUM_CHANNELS]; UINT32 MuteMsk[NUM_CHANNELS]; UINT8 chip_type; UINT8 IsDisabled; SRATE_CALLBACK SmpRateFunc; void* SmpRateData; }; //#define MAX_CHIPS 0x02 //static ay8910_context AY8910Data[MAX_CHIPS]; #define MAX_UPDATE_LEN 0x10 // in samples static stream_sample_t AYBuf[NUM_CHANNELS][MAX_UPDATE_LEN]; /*INLINE ay8910_context *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_AY8910 || sound_get_type(device) == SOUND_AY8912 || sound_get_type(device) == SOUND_AY8913 || sound_get_type(device) == SOUND_AY8930 || sound_get_type(device) == SOUND_YM2149 || sound_get_type(device) == SOUND_YM3439 || sound_get_type(device) == SOUND_YMZ284 || sound_get_type(device) == SOUND_YMZ294); return (ay8910_context *)device->token; }*/ /************************************* * * Static * *************************************/ static const ay_ym_param ym2149_param = { 630, 801, 16, { 73770, 37586, 27458, 21451, 15864, 12371, 8922, 6796, 4763, 3521, 2403, 1737, 1123, 762, 438, 251 }, }; static const ay_ym_param ym2149_param_env = { 630, 801, 32, { 103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451, 18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796, 5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737, 1397, 1123, 925, 762, 578, 438, 332, 251 }, }; #if 0 /* RL = 1000, Hacker Kay normalized, 2.1V to 3.2V */ static const ay_ym_param ay8910_param = { 664, 913, 16, { 85785, 34227, 26986, 20398, 14886, 10588, 7810, 4856, 4120, 2512, 1737, 1335, 1005, 747, 586, 451 }, }; /* * RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V * These values correspond with guesses based on Gyruss schematics * They work well with scramble as well. */ static const ay_ym_param ay8910_param = { 930, 454, 16, { 85066, 34179, 27027, 20603, 15046, 10724, 7922, 4935, 4189, 2557, 1772, 1363, 1028, 766, 602, 464 }, }; /* * RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V * These values correspond with guesses based on Gyruss schematics * They work well with scramble as well. */ static const ay_ym_param ay8910_param = { 1371, 313, 16, { 93399, 33289, 25808, 19285, 13940, 9846, 7237, 4493, 3814, 2337, 1629, 1263, 962, 727, 580, 458 }, }; /* * RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V */ static const ay_ym_param ay8910_param = { 5806, 300, 16, { 118996, 42698, 33105, 24770, 17925, 12678, 9331, 5807, 4936, 3038, 2129, 1658, 1271, 969, 781, 623 } }; #endif /* * RL = 2000, Based on Matthew Westcott's measurements from Dec 2001. * ------------------------------------------------------------------- * * http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e * After what Russell mentioned a couple of weeks back about the lack of * publicised measurements of AY chip volumes - I've finally got round to * making these readings, and I'm placing them in the public domain - so * anyone's welcome to use them in emulators or anything else. * To make the readings, I set up the chip to produce a constant voltage on * channel C (setting bits 2 and 5 of register 6), and varied the amplitude * (the low 4 bits of register 10). The voltages were measured between the * channel C output (pin 1) and ground (pin 6). * * Level Voltage * 0 1.147 * 1 1.162 * 2 1.169 * 3 1.178 * 4 1.192 * 5 1.213 * 6 1.238 * 7 1.299 * 8 1.336 * 9 1.457 * 10 1.573 * 11 1.707 * 12 1.882 * 13 2.06 * 14 2.32 * 15 2.58 * ------------------------------------------------------------------- * * The ZX spectrum output circuit was modelled in SwitcherCAD and * the resistor values below create the voltage levels above. * RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm. */ static const ay_ym_param ay8910_param = { 800000, 8000000, 16, { 15950, 15350, 15090, 14760, 14275, 13620, 12890, 11370, 10600, 8590, 7190, 5985, 4820, 3945, 3017, 2345 } }; /************************************* * * Inline * *************************************/ INLINE void build_3D_table(double rl, const ay_ym_param *par, const ay_ym_param *par_env, int normalize, double factor, int zero_is_off, INT32 *tab) { int j, j1, j2, j3, e, indx; double rt, rw, n; double min = 10.0, max = 0.0; double *temp; double spdup; temp = (double *)malloc(8*32*32*32*sizeof(*temp)); for (e=0; e < 8; e++) for (j1=0; j1 < 32; j1++) for (j2=0; j2 < 32; j2++) for (j3=0; j3 < 32; j3++) { if (zero_is_off) { n = (j1 != 0 || (e & 0x01)) ? 1 : 0; n += (j2 != 0 || (e & 0x02)) ? 1 : 0; n += (j3 != 0 || (e & 0x04)) ? 1 : 0; } else n = 3.0; rt = n / par->r_up + 3.0 / par->r_down + 1.0 / rl; rw = n / par->r_up; /*rw += 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); rt += 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); rw += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); rt += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); rw += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]); rt += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]);*/ /* this should speed up the initialsation a bit (it calculates 262144 values) */ spdup = 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); spdup += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); spdup += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]); if (spdup > 1.0) { // Bugfix for spdup being 1.#INF indx = (e << 15) | (j3<<10) | (j2<<5) | j1; temp[indx] = 0.0; continue; } rw += spdup; rt += spdup; indx = (e << 15) | (j3<<10) | (j2<<5) | j1; temp[indx] = rw / rt; if (temp[indx] < min) min = temp[indx]; if (temp[indx] > max) max = temp[indx]; } if (normalize) { for (j=0; j < 32*32*32*8; j++) //tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min))) * factor; tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) * 0.5) * factor; } else { for (j=0; j < 32*32*32*8; j++) tab[j] = MAX_OUTPUT * temp[j]; } /* for (e=0;e<16;e++) printf("%d %d\n",e<<10, tab[e<<10]); */ free(temp); } INLINE void build_single_table(double rl, const ay_ym_param *par, int normalize, INT32 *tab, int zero_is_off) { int j; double rt, rw = 0; double temp[32], min=10.0, max=0.0; for (j=0; j < par->res_count; j++) { rt = 1.0 / par->r_down + 1.0 / rl; rw = 1.0 / par->res[j]; rt += 1.0 / par->res[j]; if (!(zero_is_off && j == 0)) { rw += 1.0 / par->r_up; rt += 1.0 / par->r_up; } temp[j] = rw / rt; if (temp[j] < min) min = temp[j]; if (temp[j] > max) max = temp[j]; } if (normalize) { for (j=0; j < par->res_count; j++) /* The following line generates values that cause clicks when starting/pausing/stopping because there're off (the center is at zero, not the base). That's quite bad for a player. tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) - 0.25) * 0.5; */ tab[j] = MAX_OUTPUT * ((temp[j] - min)/(max-min)) / NUM_CHANNELS; } else { for (j=0; j < par->res_count; j++) tab[j] = MAX_OUTPUT * temp[j] / NUM_CHANNELS; } } INLINE UINT16 mix_3D(ay8910_context *psg) { int indx = 0, chan; for (chan = 0; chan < NUM_CHANNELS; chan++) if (TONE_ENVELOPE(psg, chan) != 0) { //if (psg->device->type() == AY8914) // AY8914 Has a two bit tone_envelope field if (psg->chip_type == CHTYPE_AY8914) // AY8914 Has a two bit tone_envelope field { indx |= (1 << (chan+15)) | ( psg->vol_enabled[chan] ? ((psg->env_volume >> (3-TONE_ENVELOPE(psg, chan))) << (chan*5)) : 0); } else { indx |= (1 << (chan+15)) | ( psg->vol_enabled[chan] ? psg->env_volume << (chan*5) : 0); } } else { indx |= (psg->vol_enabled[chan] ? TONE_VOLUME(psg, chan) << (chan*5) : 0); } return psg->vol3d_table[indx]; } /************************************* * * Static functions * *************************************/ static void ay8910_write_reg(ay8910_context *psg, int r, int v) { //if (r >= 11 && r <= 13 ) printf("%d %x %02x\n", PSG->index, r, v); psg->regs[r] = v; switch( r ) { case AY_AFINE: case AY_ACOARSE: case AY_BFINE: case AY_BCOARSE: case AY_CFINE: case AY_CCOARSE: case AY_NOISEPER: case AY_AVOL: case AY_BVOL: case AY_CVOL: case AY_EFINE: /* No action required */ break; case AY_ECOARSE: #ifdef MAME_DEBUG if ( (v & 0x0f) > 0) popmessage("Write to ECoarse register detected - please inform www.mametesters.org"); #endif /* No action required */ break; case AY_ENABLE: /*if ((psg->last_enable == -1) || ((psg->last_enable & 0x40) != (psg->regs[AY_ENABLE] & 0x40))) { // write out 0xff if port set to input devcb_call_write8(&psg->portAwrite, 0, (psg->regs[AY_ENABLE] & 0x40) ? psg->regs[AY_PORTA] : 0xff); } if ((psg->last_enable == -1) || ((psg->last_enable & 0x80) != (psg->regs[AY_ENABLE] & 0x80))) { // write out 0xff if port set to input devcb_call_write8(&psg->portBwrite, 0, (psg->regs[AY_ENABLE] & 0x80) ? psg->regs[AY_PORTB] : 0xff); }*/ if (~v & 0x3F) // one of the channels gets enabled -> Enable emulation psg->IsDisabled = 0x00; psg->last_enable = psg->regs[AY_ENABLE]; break; case AY_ESHAPE: #ifdef MAME_DEBUG if ( (v & 0x0f) > 0) popmessage("Write to EShape register detected - please inform www.mametesters.org"); #endif psg->attack = (psg->regs[AY_ESHAPE] & 0x04) ? psg->env_step_mask : 0x00; if ((psg->regs[AY_ESHAPE] & 0x08) == 0) { /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ psg->hold = 1; psg->alternate = psg->attack; } else { psg->hold = psg->regs[AY_ESHAPE] & 0x01; psg->alternate = psg->regs[AY_ESHAPE] & 0x02; } psg->env_step = psg->env_step_mask; psg->holding = 0; psg->env_volume = (psg->env_step ^ psg->attack); break; case AY_PORTA: /*if (psg->regs[AY_ENABLE] & 0x40) { if (psg->portAwrite.write) devcb_call_write8(&psg->portAwrite, 0, psg->regs[AY_PORTA]); else logerror("warning - write %02x to 8910 '%s' Port A\n",psg->regs[AY_PORTA],psg->device->tag); } else { logerror("warning: write to 8910 '%s' Port A set as input - ignored\n",psg->device->tag); }*/ break; case AY_PORTB: /*if (psg->regs[AY_ENABLE] & 0x80) { if (psg->portBwrite.write) devcb_call_write8(&psg->portBwrite, 0, psg->regs[AY_PORTB]); else logerror("warning - write %02x to 8910 '%s' Port B\n",psg->regs[AY_PORTB],psg->device->tag); } else { logerror("warning: write to 8910 '%s' Port B set as input - ignored\n",psg->device->tag); }*/ break; } } //static STREAM_UPDATE( ay8910_update ) /*void ay8910_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { ay8910_context *psg = &AY8910Data[ChipID]; memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); ay8910_update_one(psg, outputs, samples); return; }*/ void ay8910_update_one(void *param, stream_sample_t **outputs, int samples) { ay8910_context *psg = (ay8910_context *)param; stream_sample_t *buf[NUM_CHANNELS]; int chan; int cursmpl; int buf_smpls; stream_sample_t *bufL = outputs[0]; stream_sample_t *bufR = outputs[1]; //stream_sample_t bufSmpl; memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); // Speed hack for OPN chips (YM2203, YM26xx), that have an often unused AY8910 if (psg->IsDisabled) return; buf_smpls = samples; //buf[0] = outputs[0]; buf[0] = AYBuf[0]; buf[1] = NULL; buf[2] = NULL; //if (psg->streams == NUM_CHANNELS) //{ //buf[1] = outputs[1]; //buf[2] = outputs[2]; buf[1] = AYBuf[1]; buf[2] = AYBuf[2]; //} /* hack to prevent us from hanging when starting filtered outputs */ //if (!psg->ready) //{ for (chan = 0; chan < NUM_CHANNELS; chan++) if (buf[chan] != NULL) memset(buf[chan], 0, samples * sizeof(*buf[chan])); //} /* The 8910 has three outputs, each output is the mix of one of the three */ /* tone generators and of the (single) noise generator. The two are mixed */ /* BEFORE going into the DAC. The formula to mix each channel is: */ /* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */ /* Note that this means that if both tone and noise are disabled, the output */ /* is 1, not 0, and can be modulated changing the volume. */ if (buf_smpls > MAX_UPDATE_LEN) buf_smpls = MAX_UPDATE_LEN; /* buffering loop */ while (buf_smpls) { for (chan = 0; chan < NUM_CHANNELS; chan++) { psg->count[chan]++; if (psg->count[chan] >= TONE_PERIOD(psg, chan)) { psg->output[chan] ^= 1; psg->count[chan] = 0; } } psg->count_noise++; if (psg->count_noise >= NOISE_PERIOD(psg)) { /* toggle the prescaler output. Noise is no different to * channels. */ psg->count_noise = 0; psg->prescale_noise ^= 1; if ( psg->prescale_noise) { /* The Random Number Generator of the 8910 is a 17-bit shift */ /* register. The input to the shift register is bit0 XOR bit3 */ /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ psg->rng ^= (((psg->rng & 1) ^ ((psg->rng >> 3) & 1)) << 17); psg->rng >>= 1; } } for (chan = 0; chan < NUM_CHANNELS; chan++) { psg->vol_enabled[chan] = (psg->output[chan] | TONE_ENABLEQ(psg, chan)) & (NOISE_OUTPUT(psg) | NOISE_ENABLEQ(psg, chan)); } /* update envelope */ if (psg->holding == 0) { psg->count_env++; if (psg->count_env >= ENVELOPE_PERIOD(psg) * psg->step ) { psg->count_env = 0; psg->env_step--; /* check envelope current position */ if (psg->env_step < 0) { if (psg->hold) { if (psg->alternate) psg->attack ^= psg->env_step_mask; psg->holding = 1; psg->env_step = 0; } else { /* if CountEnv has looped an odd number of times (usually 1), */ /* invert the output. */ if (psg->alternate && (psg->env_step & (psg->env_step_mask + 1))) psg->attack ^= psg->env_step_mask; psg->env_step &= psg->env_step_mask; } } } } psg->env_volume = (psg->env_step ^ psg->attack); //if (psg->streams == NUM_CHANNELS) //{ for (chan = 0; chan < NUM_CHANNELS; chan++) if (TONE_ENVELOPE(psg, chan) != 0) { /* Envolope has no "off" state */ //if (psg->device->type() == AY8914) // AY8914 Has a two bit tone_envelope field if (psg->chip_type == CHTYPE_AY8914) // AY8914 Has a two bit tone_envelope field { *(buf[chan]++) = psg->env_table[chan][psg->vol_enabled[chan] ? psg->env_volume >> (3-TONE_ENVELOPE(psg,chan)) : 0]; } else { *(buf[chan]++) = psg->env_table[chan][psg->vol_enabled[chan] ? psg->env_volume : 0]; } } else { *(buf[chan]++) = psg->vol_table[chan][psg->vol_enabled[chan] ? TONE_VOLUME(psg, chan) : 0]; } /*} else { *(buf[0]++) = mix_3D(psg); #if 0 *(buf[0]) = ( vol_enabled[0] * psg->vol_table[psg->Vol[0]] + vol_enabled[1] * psg->vol_table[psg->Vol[1]] + vol_enabled[2] * psg->vol_table[psg->Vol[2]]) / psg->step; #endif }*/ buf_smpls--; } buf_smpls = samples; if (buf_smpls > MAX_UPDATE_LEN) buf_smpls = MAX_UPDATE_LEN; for (cursmpl = 0; cursmpl < buf_smpls; cursmpl ++) { /*bufSmpl = AYBuf[0][cursmpl] & psg->MuteMsk[0]; if (psg->streams == NUM_CHANNELS) { bufSmpl += AYBuf[1][cursmpl] & psg->MuteMsk[1]; bufSmpl += AYBuf[2][cursmpl] & psg->MuteMsk[2]; } bufL[cursmpl] += bufSmpl; bufR[cursmpl] += bufSmpl;*/ for (chan = 0; chan < NUM_CHANNELS; chan ++) { if (psg->MuteMsk[chan]) { if (psg->StereoMask[chan] & 0x01) bufL[cursmpl] += AYBuf[chan][cursmpl]; if (psg->StereoMask[chan] & 0x02) bufR[cursmpl] += AYBuf[chan][cursmpl]; } } } } static void build_mixer_table(ay8910_context *psg) { int normalize = 0; int chan; if ((psg->intf->flags & AY8910_LEGACY_OUTPUT) != 0) { #ifdef _DEBUG //logerror("AY-3-8910/YM2149 using legacy output levels!\n"); #endif //normalize = 1; } normalize = 1; /* skip unnecessary things to speed up the AY8910 init 1-channel AY uses the 3D table, 3-channel AY uses envelope and volume but building the 3D table still takes too long */ //if (psg->streams == NUM_CHANNELS) { for (chan=0; chan < NUM_CHANNELS; chan++) { build_single_table(psg->intf->res_load[chan], psg->par, normalize, psg->vol_table[chan], psg->zero_is_off); build_single_table(psg->intf->res_load[chan], psg->par_env, normalize, psg->env_table[chan], 0); } } //else //{ // /* // * The previous implementation added all three channels up instead of averaging them. // * The factor of 3 will force the same levels if normalizing is used. // */ // build_3D_table(psg->intf->res_load[0], psg->par, psg->par_env, normalize, 3, psg->zero_is_off, psg->vol3d_table); //} } /*static void ay8910_statesave(ay8910_context *psg, const device_config *device) { state_save_register_device_item(device, 0, psg->register_latch); state_save_register_device_item_array(device, 0, psg->regs); state_save_register_device_item(device, 0, psg->last_enable); state_save_register_device_item_array(device, 0, psg->count); state_save_register_device_item(device, 0, psg->count_noise); state_save_register_device_item(device, 0, psg->count_env); state_save_register_device_item(device, 0, psg->env_volume); state_save_register_device_item_array(device, 0, psg->output); state_save_register_device_item(device, 0, psg->output_noise); state_save_register_device_item(device, 0, psg->env_step); state_save_register_device_item(device, 0, psg->hold); state_save_register_device_item(device, 0, psg->alternate); state_save_register_device_item(device, 0, psg->attack); state_save_register_device_item(device, 0, psg->holding); state_save_register_device_item(device, 0, psg->rng); }*/ /************************************* * * Public functions * * used by e.g. YM2203, YM2210 ... * *************************************/ //void *ay8910_start_ym(void *infoptr, sound_type chip_type, const device_config *device, int clock, const ay8910_interface *intf) void *ay8910_start_ym(void *infoptr, unsigned char chip_type, int clock, const ay8910_interface *intf) { ay8910_context *info = (ay8910_context *)infoptr; int master_clock = clock; UINT8 CurChn; if (info == NULL) { //info = auto_alloc_clear(device->machine, ay8910_context); info = (ay8910_context*)malloc(sizeof(ay8910_context)); memset(info, 0x00, sizeof(ay8910_context)); } //info->device = device; info->intf = intf; info->SmpRateFunc = NULL; //devcb_resolve_read8(&info->portAread, &intf->portAread, device); //devcb_resolve_read8(&info->portBread, &intf->portBread, device); //devcb_resolve_write8(&info->portAwrite, &intf->portAwrite, device); //devcb_resolve_write8(&info->portBwrite, &intf->portBwrite, device); if ((info->intf->flags & AY8910_SINGLE_OUTPUT) != 0) { #ifdef _DEBUG //logerror("AY-3-8910/YM2149 using single output!\n"); #endif //info->streams = 1; info->streams = 3; // set to 3 to greatly speed up loading times } else info->streams = 3; info->chip_type = chip_type; //if (chip_type == CHTYPE_AY8910 || chip_type == CHTYPE_AY8914 || chip_type == CHTYPE_AY8930) if ((chip_type & 0xF0) == 0x00) // CHTYPE_AY89xx variants { info->step = 2; info->par = &ay8910_param; info->par_env = &ay8910_param; info->zero_is_off = 0; /* FIXME: Remove after verification that off=vol(0) */ info->env_step_mask = 0x0F; } else //if ((chip_type & 0xF0) == 0x10) // CHTYPE_YMxxxx variants (also YM2203/2608/2610) { info->step = 1; info->par = &ym2149_param; info->par_env = &ym2149_param_env; info->zero_is_off = 0; info->env_step_mask = 0x1F; /* YM2149 master clock divider? */ if (info->intf->flags & YM2149_PIN26_LOW) master_clock /= 2; } if (intf->flags & AY8910_ZX_STEREO) { // ABC Stereo info->StereoMask[0] = 0x01; info->StereoMask[1] = 0x03; info->StereoMask[2] = 0x02; } else { info->StereoMask[0] = 0x03; info->StereoMask[1] = 0x03; info->StereoMask[2] = 0x03; } build_mixer_table(info); /* The envelope is pacing twice as fast for the YM2149 as for the AY-3-8910, */ /* This handled by the step parameter. Consequently we use a divider of 8 here. */ //info->channel = stream_create(device, 0, info->streams, device->clock / 8, info, ay8910_update); ay8910_set_clock_ym(info,clock); //ay8910_statesave(info, device); for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) info->MuteMsk[CurChn] = 0; return info; } void ay8910_stop_ym(void *chip) { free(chip); } void ay8910_reset_ym(void *chip) { ay8910_context *psg = (ay8910_context *)chip; int i; psg->register_latch = 0; psg->rng = 1; psg->output[0] = 0; psg->output[1] = 0; psg->output[2] = 0; psg->count[0] = 0; psg->count[1] = 0; psg->count[2] = 0; psg->count_noise = 0; psg->count_env = 0; psg->prescale_noise = 0; psg->last_enable = -1; /* force a write */ for (i = 0;i < AY_PORTA;i++) ay8910_write_reg(psg,i,0); psg->ready = 1; #if ENABLE_REGISTER_TEST ay8910_write_reg(psg, AY_AFINE, 0); ay8910_write_reg(psg, AY_ACOARSE, 1); ay8910_write_reg(psg, AY_BFINE, 0); ay8910_write_reg(psg, AY_BCOARSE, 2); ay8910_write_reg(psg, AY_CFINE, 0); ay8910_write_reg(psg, AY_CCOARSE, 4); //#define AY_NOISEPER (6) ay8910_write_reg(psg, AY_ENABLE, ~7); ay8910_write_reg(psg, AY_AVOL, 10); ay8910_write_reg(psg, AY_BVOL, 10); ay8910_write_reg(psg, AY_CVOL, 10); //#define AY_EFINE (11) //#define AY_ECOARSE (12) //#define AY_ESHAPE (13) #endif if (psg->chip_type & 0x20) psg->IsDisabled = 0x01; } /*void ay8910_set_volume(UINT8 ChipID,int channel,int volume) { //ay8910_context *psg = get_safe_token(device); int ch; for (ch = 0; ch < psg->streams; ch++) if (channel == ch || psg->streams == 1 || channel == ALL_8910_CHANNELS) stream_set_output_gain(psg->channel, ch, volume / 100.0); }*/ void ay8910_set_clock_ym(void *chip, int clock) { ay8910_context *psg = (ay8910_context *)chip; if ((psg->chip_type & 0xF0) == 0x10 && (psg->intf->flags & YM2149_PIN26_LOW)) clock /= 2; //stream_set_sample_rate(psg->channel, clock / 8 ); if (psg->SmpRateFunc != NULL) psg->SmpRateFunc(psg->SmpRateData, clock / 8); return; } void ay8910_write_ym(void *chip, int addr, int data) { ay8910_context *psg = (ay8910_context *)chip; if (addr & 1) { /* Data port */ int r = psg->register_latch; if (r > 15) return; if (r == AY_ESHAPE || psg->regs[r] != data) { /* update the output buffer before changing the register */ //stream_update(psg->channel); } ay8910_write_reg(psg,r,data); } else { /* Register port */ psg->register_latch = data & 0x0f; } } int ay8910_read_ym(void *chip) { ay8910_context *psg = (ay8910_context *)chip; int r = psg->register_latch; if (r > 15) return 0; /* There are no state dependent register in the AY8910! */ /* stream_update(psg->channel); */ switch (r) { case AY_PORTA: //if ((psg->regs[AY_ENABLE] & 0x40) != 0) // logerror("warning: read from 8910 '%s' Port A set as output\n",psg->device->tag); /* even if the port is set as output, we still need to return the external data. Some games, like kidniki, need this to work. */ /*if (psg->portAread.read) psg->regs[AY_PORTA] = devcb_call_read8(&psg->portAread, 0); else logerror("%s: warning - read 8910 '%s' Port A\n",cpuexec_describe_context(psg->device->machine),psg->device->tag);*/ break; case AY_PORTB: //if ((psg->regs[AY_ENABLE] & 0x80) != 0) // logerror("warning: read from 8910 '%s' Port B set as output\n",psg->device->tag); /*if (psg->portBread.read) psg->regs[AY_PORTB] = devcb_call_read8(&psg->portBread, 0); else logerror("%s: warning - read 8910 '%s' Port B\n",cpuexec_describe_context(psg->device->machine),psg->device->tag);*/ break; } /* Depending on chip type, unused bits in registers may or may not be accessible. Untested chips are assumed to regard them as 'ram' Tested and confirmed on hardware: - AY-3-8910: inaccessible bits (see masks below) read back as 0 - YM2149: no anomaly */ if (! (psg->chip_type & 0x10)) { const UINT8 mask[0x10]={ 0xff,0x0f,0xff,0x0f,0xff,0x0f,0x1f,0xff,0x1f,0x1f,0x1f,0xff,0xff,0x0f,0xff,0xff }; return psg->regs[r] & mask[r]; } else return psg->regs[r]; } /************************************* * * Sound Interface * *************************************/ //static DEVICE_START( ay8910 ) /*int device_start_ay8910(UINT8 ChipID, int clock, unsigned char chip_type, unsigned char Flags) { static const ay8910_interface generic_ay8910 = { AY8910_LEGACY_OUTPUT, AY8910_DEFAULT_LOADS }; //const ay8910_interface *intf = (device->static_config ? (const ay8910_interface *)device->static_config : &generic_ay8910); ay8910_interface intf = generic_ay8910; ay8910_context *psg; if (ChipID >= MAX_CHIPS) return 0; psg = &AY8910Data[ChipID]; intf.flags = Flags; ay8910_start_ym(psg, chip_type, clock, &intf); return clock / 8; }*/ int ay8910_start(void **chip, int clock, UINT8 chip_type, UINT8 Flags) { static const ay8910_interface generic_ay8910 = { AY8910_LEGACY_OUTPUT, AY8910_DEFAULT_LOADS }; ay8910_interface intf = generic_ay8910; ay8910_context *psg = (ay8910_context*)chip; psg = (ay8910_context*)malloc(sizeof(ay8910_context)); if(psg == NULL) return 0; memset(psg, 0x00, sizeof(ay8910_context)); *chip = psg; intf.flags = Flags; ay8910_start_ym(psg, chip_type, clock, &intf); if (Flags & YM2149_PIN26_LOW) return clock / 16; else return clock / 8; } /*static DEVICE_START( ym2149 ) { static const ay8910_interface generic_ay8910 = { AY8910_LEGACY_OUTPUT, AY8910_DEFAULT_LOADS, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL }; const ay8910_interface *intf = (device->static_config ? (const ay8910_interface *)device->static_config : &generic_ay8910); ay8910_start_ym(get_safe_token(device), SOUND_YM2149, device, device->clock, intf); }*/ //static DEVICE_STOP( ay8910 ) /*void device_stop_ay8910(UINT8 ChipID) { ay8910_context *psg = &AY8910Data[ChipID]; ay8910_stop_ym(psg); } //static DEVICE_RESET( ay8910 ) void device_reset_ay8910(UINT8 ChipID) { ay8910_context *psg = &AY8910Data[ChipID]; ay8910_reset_ym(psg); }*/ /*DEVICE_GET_INFO( ay8910 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ay8910_context); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ay8910 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ay8910 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8910A"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "PSG"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEVICE_GET_INFO( ay8912 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8912A"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ay8913 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8913A"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ay8930 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "AY8930"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ym2149 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "YM2149"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ym3439 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "YM3439"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ymz284 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "YMZ284"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } } DEVICE_GET_INFO( ymz294 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "YMZ294"); break; default: DEVICE_GET_INFO_CALL(ay8910); break; } }*/ /************************************* * * Read/Write Handlers * *************************************/ //READ8_DEVICE_HANDLER( ay8910_r ) /*UINT8 ay8910_r(UINT8 ChipID, offs_t offset) { ay8910_context *psg = &AY8910Data[ChipID]; return ay8910_read_ym(psg); } //WRITE8_DEVICE_HANDLER( ay8910_data_address_w ) void ay8910_data_address_w(UINT8 ChipID, offs_t offset, UINT8 data) { ay8910_context *psg = &AY8910Data[ChipID]; // note that directly connecting BC1 to A0 puts data on 0 and address on 1 ay8910_write_ym(psg, ~offset & 1, data); } //WRITE8_DEVICE_HANDLER( ay8910_address_data_w ) void ay8910_address_data_w(UINT8 ChipID, offs_t offset, UINT8 data) { ay8910_context *psg = &AY8910Data[ChipID]; ay8910_write_ym(psg, offset & 1, data); } //WRITE8_DEVICE_HANDLER( ay8910_address_w ) void ay8910_address_w(UINT8 ChipID, offs_t offset, UINT8 data) { #if ENABLE_REGISTER_TEST return; #endif ay8910_data_address_w(ChipID, 1, data); } //WRITE8_DEVICE_HANDLER( ay8910_data_w ) void ay8910_data_w(UINT8 ChipID, offs_t offset, UINT8 data) { #if ENABLE_REGISTER_TEST return; #endif ay8910_data_address_w(ChipID, 0, data); }*/ void ay8910_set_mute_mask_ym(void *chip, UINT32 MuteMask) { ay8910_context *psg = (ay8910_context *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) psg->MuteMsk[CurChn] = (MuteMask & (1 << CurChn)) ? 0 : ~0; return; } void ay8910_set_stereo_mask_ym(void *chip, UINT32 StereoMask) { ay8910_context *psg = (ay8910_context *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) { psg->StereoMask[CurChn] = StereoMask &3; StereoMask >>= 2; } return; } /*void ay8910_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ay8910_context *psg = &AY8910Data[ChipID]; ay8910_set_mute_mask_ym(psg, MuteMask); }*/ void ay8910_set_srchg_cb_ym(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr) { ay8910_context *info = (ay8910_context *)chip; // set Sample Rate Change Callback routine info->SmpRateFunc = CallbackFunc; info->SmpRateData = DataPtr; return; } ================================================ FILE: VGMPlay/chips/ay8910.h ================================================ #pragma once //#include "devcb.h" #define DEVCB_TYPE_NULL (0) #define DEVCB_NULL { DEVCB_TYPE_NULL } /* AY-3-8910A: 2 I/O ports AY-3-8912A: 1 I/O port AY-3-8913A: 0 I/O port AY8930: upper compatible with 8910. In extended mode, it has higher resolution and duty ratio setting YM2149: higher resolution YM3439: same as 2149 YMZ284: 0 I/O port, different clock divider YMZ294: 0 I/O port */ #define ALL_8910_CHANNELS -1 /* Internal resistance at Volume level 7. */ #define AY8910_INTERNAL_RESISTANCE (356) #define YM2149_INTERNAL_RESISTANCE (353) /* * Default values for resistor loads. * The macro should be used in AY8910interface if * the real values are unknown. */ #define AY8910_DEFAULT_LOADS {1000, 1000, 1000} /* * The following is used by all drivers not reviewed yet. * This will like the old behaviour, output between * 0 and 7FFF */ #define AY8910_LEGACY_OUTPUT (1) /* * Specifing the next define will simulate the special * cross channel mixing if outputs are tied together. * The driver will only provide one stream in this case. */ #define AY8910_SINGLE_OUTPUT (2) /* * The follwoing define is the default behaviour. * Output level 0 is 0V and 7ffff corresponds to 5V. * Use this to specify that a discrete mixing stage * follows. */ #define AY8910_DISCRETE_OUTPUT (4) /* * The follwoing define causes the driver to output * raw volume levels, i.e. 0 .. 15 and 0..31. * This is intended to be used in a subsequent * mixing modul (i.e. mpatrol ties 6 channels from * AY-3-8910 together). Do not use it now. */ /* TODO: implement mixing module */ #define AY8910_RAW_OUTPUT (8) #define AY8910_ZX_STEREO 0x80 /* * This define specifies the initial state of YM2149 * pin 26 (SEL pin). By default it is set to high, * compatible with AY8910. */ /* TODO: make it controllable while it's running (used by any hw???) */ #define YM2149_PIN26_HIGH (0x00) /* or N/C */ #define YM2149_PIN26_LOW (0x10) typedef struct _ay8910_interface ay8910_interface; struct _ay8910_interface { int flags; /* Flags */ int res_load[3]; /* Load on channel in ohms */ //devcb_read8 portAread; //devcb_read8 portBread; //devcb_write8 portAwrite; //devcb_write8 portBwrite; }; //void ay8910_set_volume(UINT8 ChipID,int channel,int volume); /*READ8_DEVICE_HANDLER( ay8910_r ); WITE8_DEVICE_HANDLER( ay8910_address_w ); WRITE8_DEVICE_HANDLER( ay8910_data_w );*/ /*UINT8 ay8910_r(UINT8 ChipID, offs_t offset); void ay8910_address_w(UINT8 ChipID, offs_t offset, UINT8 data); void ay8910_data_w(UINT8 ChipID, offs_t offset, UINT8 data);*/ /* use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' */ //WRITE8_DEVICE_HANDLER( ay8910_data_address_w ); //void ay8910_data_address_w(UINT8 ChipID, offs_t offset, UINT8 data); /* use this when BC1 == !A0; here, BC1=0 selects 'latch address' and BC1=1 selects 'data' */ //WRITE8_DEVICE_HANDLER( ay8910_address_data_w ); //void ay8910_address_data_w(UINT8 ChipID, offs_t offset, UINT8 data); /*********** An interface for SSG of YM2203 ***********/ //void *ay8910_start_ym(void *infoptr, sound_type chip_type, const device_config *device, int clock, const ay8910_interface *intf); void *ay8910_start_ym(void *infoptr, unsigned char chip_type, int clock, const ay8910_interface *intf); void ay8910_stop_ym(void *chip); void ay8910_reset_ym(void *chip); void ay8910_set_clock_ym(void *chip, int clock); void ay8910_write_ym(void *chip, int addr, int data); int ay8910_read_ym(void *chip); //void ay8910_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ay8910_update_one(void *param, stream_sample_t **outputs, int samples); int ay8910_start(void **chip, int clock, UINT8 chip_type, UINT8 Flags); /*int device_start_ay8910(UINT8 ChipID, int clock, unsigned char chip_type, unsigned char Flags); void device_stop_ay8910(UINT8 ChipID); void device_reset_ay8910(UINT8 ChipID);*/ void ay8910_set_mute_mask_ym(void *chip, UINT32 MuteMask); void ay8910_set_stereo_mask_ym(void *chip, UINT32 StereoMask); //void ay8910_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void ay8910_set_srchg_cb_ym(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); /*DEVICE_GET_INFO( ay8910 ); DEVICE_GET_INFO( ay8912 ); DEVICE_GET_INFO( ay8913 ); DEVICE_GET_INFO( ay8930 ); DEVICE_GET_INFO( ym2149 ); DEVICE_GET_INFO( ym3439 ); DEVICE_GET_INFO( ymz284 ); DEVICE_GET_INFO( ymz294 ); #define SOUND_AY8910 DEVICE_GET_INFO_NAME( ay8910 ) #define SOUND_AY8912 DEVICE_GET_INFO_NAME( ay8912 ) #define SOUND_AY8913 DEVICE_GET_INFO_NAME( ay8913 ) #define SOUND_AY8930 DEVICE_GET_INFO_NAME( ay8930 ) #define SOUND_YM2149 DEVICE_GET_INFO_NAME( ym2149 ) #define SOUND_YM3439 DEVICE_GET_INFO_NAME( ym3439 ) #define SOUND_YMZ284 DEVICE_GET_INFO_NAME( ymz284 ) #define SOUND_YMZ294 DEVICE_GET_INFO_NAME( ymz294 )*/ ================================================ FILE: VGMPlay/chips/ay8910_opl.c ================================================ #include // for NULL #include "mamedef.h" #include "math.h" void OPL_RegMapper(UINT16 Reg, UINT8 Data); #define NUM_CHANNELS 3 /* register id's */ #define AY_AFINE (0) #define AY_ACOARSE (1) #define AY_BFINE (2) #define AY_BCOARSE (3) #define AY_CFINE (4) #define AY_CCOARSE (5) #define AY_NOISEPER (6) #define AY_ENABLE (7) #define AY_AVOL (8) #define AY_BVOL (9) #define AY_CVOL (10) #define AY_EFINE (11) #define AY_ECOARSE (12) #define AY_ESHAPE (13) #define AY_PORTA (14) #define AY_PORTB (15) #define NOISE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (3 + _chan)) & 1) #define TONE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (_chan)) & 1) #define TONE_PERIOD(_psg, _chan) ( (_psg)->regs[(_chan) << 1] | (((_psg)->regs[((_chan) << 1) | 1] & 0x0f) << 8) ) #define NOISE_PERIOD(_psg) ( (_psg)->regs[AY_NOISEPER] & 0x1f) #define TONE_VOLUME(_psg, _chan) ( (_psg)->regs[AY_AVOL + (_chan)] & 0x0f) //#define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->device->type() == AY8914) ? 3 : 1)) #define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->chip_type == CHTYPE_AY8914) ? 3 : 1)) #define ENVELOPE_PERIOD(_psg) (((_psg)->regs[AY_EFINE] | ((_psg)->regs[AY_ECOARSE]<<8))) typedef struct _ay_ym_param ay_ym_param; struct _ay_ym_param { double r_up; double r_down; int res_count; double res[32]; }; typedef struct _ay8910_context_opl ay8910_context_opl; struct _ay8910_context_opl { UINT32 clock; UINT8 regs[0x10]; //INT32 count[NUM_CHANNELS]; //UINT8 output[NUM_CHANNELS]; //UINT8 output_noise; //INT32 count_noise; //INT32 count_env; //INT8 env_step; //UINT32 env_volume; //UINT8 hold,alternate,attack,holding; //INT32 rng; //UINT8 env_step_mask; /* init parameters ... */ //int step; //int zero_is_off; //UINT8 vol_enabled[NUM_CHANNELS]; const ay_ym_param *par; const ay_ym_param *par_env; INT32 vol_table[16]; UINT8 Vol2OPL[16]; //INT32 env_table[32]; //INT32 vol3d_table[8*32*32*32]; //devcb_resolved_read8 portAread; //devcb_resolved_read8 portBread; //devcb_resolved_write8 portAwrite; //devcb_resolved_write8 portBwrite; //UINT32 MuteMsk[NUM_CHANNELS]; UINT8 chip_type; UINT8 LastVol[3]; UINT16 LastFreq[4]; UINT16 OPLFreq[4]; UINT8 OPLVol[3]; UINT8 Enabled; }; static const UINT8 REG_LIST[0x0B] = {0x20, 0x40, 0x60, 0x80, 0xE0, 0x23, 0x43, 0x63, 0x83, 0xE3, 0xC0}; // writing Reg 83 Data #6 makes a smooth fading, if you break playing // actually the release time could be everything - it wouldn't change anything static const UINT8 SQUARE_FM_INS_OPL[0x0B] = {0x02, 0x18, 0xFF, 0x00, 0x02, 0x01, 0x00, 0xF0, 0xF6, 0x00, 0x00}; // OPL2/OPL3 static const UINT8 SQUARE_FM_INS_OPL3[0x0B] = {0x01, 0x3F, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xF0, 0xF6, 0x06, 0x01}; // OPL3 only // OPL3 has a SquareWave Waveform (OPL2 has only SineWaves) //static const UINT8 NOISE_FM_INS[0x0A] = // {0x0F, 0x00, 0xF0, 0xFF, 0x00, 0x01, 0x00, 0xF0, 0xF6, 0x00}; /* Volume Table (4-bit AY8910 -> 6-bit OPL Conversion) AY8910 OPL Idx Vol Idx Vol ?? ?? ?? ?? */ /*static const UINT8 VOL_OPL[0x10] = {0x3F, 0x38, 0x34, 0x20, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, 0x14, 0x10, 0x0C, 0x08, 0x04, 0x00};*/ extern UINT8 OPL_MODE; #define MAX_CHIPS 0x02 static ay8910_context_opl AY8910Data[MAX_CHIPS]; static const ay_ym_param ym2149_param = { 630, 801, 16, { 73770, 37586, 27458, 21451, 15864, 12371, 8922, 6796, 4763, 3521, 2403, 1737, 1123, 762, 438, 251 }, }; static const ay_ym_param ym2149_param_env = { 630, 801, 32, { 103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451, 18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796, 5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737, 1397, 1123, 925, 762, 578, 438, 332, 251 }, }; static const ay_ym_param ay8910_param = { 800000, 8000000, 16, { 15950, 15350, 15090, 14760, 14275, 13620, 12890, 11370, 10600, 8590, 7190, 5985, 4820, 3945, 3017, 2345 } }; static void SendVolume(ay8910_context_opl* chip, UINT8 Channel) { UINT8 Volume; //if (Channel >= 0x04) // Channel &= 0x03; Volume = TONE_VOLUME(chip, Channel); if (Volume == chip->LastVol[Channel]) return; else chip->LastVol[Channel] = Volume; if (! (Volume & 0x10)) { chip->OPLVol[Channel] = chip->Vol2OPL[Volume]; } else { // TODO: Handle hardware envelope shapes correctly chip->OPLVol[Channel] = 0x00; } if (chip->Enabled & (1 << Channel)) OPL_RegMapper(0x43 + Channel, 0x00 | 0x3F); else // 0 - enable OPL_RegMapper(0x43 + Channel, 0x00 | chip->OPLVol[Channel]); return; } static void SendFrequency(ay8910_context_opl* chip, UINT8 Channel) { const double OPL_CHIP_RATE = 3579545.0 / 72.0; UINT16 Period; double FreqVal; INT16 FNum; INT8 BlockVal; //Channel &= 0x03; Period = TONE_PERIOD(chip, Channel); if (Period == chip->LastFreq[Channel]) return; else chip->LastFreq[Channel] = Period; if (Period) FreqVal = (chip->clock / 16.0) / Period; else FreqVal = 0.0; if (FreqVal > OPL_CHIP_RATE) FreqVal = 0.0; BlockVal = (UINT8)(0x05 + (log(FreqVal) - log(440.0)) / log(2.0)); if (BlockVal < 0x00) { BlockVal = 0x00; FNum = 0x000; } else if (BlockVal > 0x07) { BlockVal = 0x07; FNum = 0x3FF; } else { FNum = (UINT16)(FreqVal * (1 << (20 - BlockVal)) / OPL_CHIP_RATE + 0.5); if (FNum < 0x000) FNum = 0x000; else if (FNum > 0x3FF) FNum = 0x3FF; } chip->OPLFreq[Channel] = (BlockVal << 10) | (FNum << 0); OPL_RegMapper(0xA0 | Channel, chip->OPLFreq[Channel] & 0x00FF); /*if (chip->Enabled & (1 << Channel)) OPL_RegMapper(0xB0 | Channel, 0x00 | (chip->OPLFreq[Channel] >> 8)); else // 0 - enable*/ OPL_RegMapper(0xB0 | Channel, 0x20 | (chip->OPLFreq[Channel] >> 8)); return; } void ay8910_write_opl(UINT8 ChipID, UINT8 r, UINT8 v) { ay8910_context_opl* chip = &AY8910Data[ChipID]; UINT8 CurChn; UINT8 EnDiff; chip->regs[r] = v; switch(r) { case AY_AFINE: case AY_ACOARSE: case AY_BFINE: case AY_BCOARSE: case AY_CFINE: case AY_CCOARSE: SendFrequency(chip, r >> 1); break; case AY_NOISEPER: break; case AY_AVOL: case AY_BVOL: case AY_CVOL: SendVolume(chip, r - AY_AVOL); break; case AY_EFINE: case AY_ECOARSE: break; case AY_ENABLE: EnDiff = chip->Enabled ^ v; for (CurChn = 0x00; CurChn < 0x03; CurChn ++) { if (EnDiff & (1 << CurChn)) { // using the Key On bit sounds horrible, if it's disabled and enabled a lot // (see Final Fantasy MSX: Chaos Temple /*if (v & (1 << CurChn)) OPL_RegMapper(0xB0 | CurChn, 0x00 | (chip->OPLFreq[CurChn] >> 8)); else // 0 - enable OPL_RegMapper(0xB0 | CurChn, 0x20 | (chip->OPLFreq[CurChn] >> 8));*/ if (v & (1 << CurChn)) OPL_RegMapper(0x43 + CurChn, 0x00 | 0x3F); else // 0 - enable OPL_RegMapper(0x43 + CurChn, 0x00 | chip->OPLVol[CurChn]); } } chip->Enabled = v; break; case AY_ESHAPE: break; case AY_PORTA: case AY_PORTB: break; } return; } INLINE void build_single_table(double rl, const ay_ym_param *par, INT32 *tab, UINT8* OPLtab) { int j; double rt, rw = 0; double temp[32], min=10.0, max=0.0; for (j=0; j < par->res_count; j++) { rt = 1.0 / par->r_down + 1.0 / rl; rw = 1.0 / par->res[j]; rt += 1.0 / par->res[j]; rw += 1.0 / par->r_up; rt += 1.0 / par->r_up; temp[j] = rw / rt; if (temp[j] < min) min = temp[j]; if (temp[j] > max) max = temp[j]; } for (j = 0; j < par->res_count; j ++) { tab[j] = (INT32)(0x10000 * temp[j]); rt = (temp[j] - min) / (max - min); rt = -8.0 * log(temp[j]) / log(2.0); OPLtab[j] = (UINT8)rt; } return; } static void ay8910_start_opl(int clock, ay8910_context_opl* chip, UINT8 chip_type) { const UINT8* SQUARE_FM_INS; UINT8 i; UINT8 reg; chip->clock = clock; chip->chip_type = chip_type; if ((chip_type & 0xF0) == 0x00) // CHTYPE_AY89xx variants { // chip->step = 2; chip->par = &ay8910_param; chip->par_env = &ay8910_param; //chip->zero_is_off = 0; // chip->env_step_mask = 0x0F; } else //if ((chip_type & 0xF0) == 0x10) // CHTYPE_YMxxxx variants (also YM2203/2608/2610) { // chip->step = 1; chip->par = &ym2149_param; chip->par_env = &ym2149_param_env; //chip->zero_is_off = 0; // chip->env_step_mask = 0x1F; /*// YM2149 master clock divider? if (info->intf->flags & YM2149_PIN26_LOW) master_clock /= 2;*/ } for (i = 0; i < 3; i ++) { build_single_table(1000, chip->par, chip->vol_table, chip->Vol2OPL); //build_single_table(1000, chip->par_env, psg->env_table, NULL); } for (i = 0x00; i < 0x10; i ++) chip->regs[i] = 0x00; if (OPL_MODE == 0x03) SQUARE_FM_INS = SQUARE_FM_INS_OPL3; else SQUARE_FM_INS = SQUARE_FM_INS_OPL; // Init Instruments for (i = 0; i < 3; i++) { for (reg = 0x00; reg < 0x0A; reg ++) OPL_RegMapper(REG_LIST[reg] + i, SQUARE_FM_INS[reg]); OPL_RegMapper(REG_LIST[0x0A] + i, 0x30 | SQUARE_FM_INS[0x0A]); } chip->Enabled = 0xFF; for (i = 0; i < 3; i ++) { chip->LastVol[i] = 0xFF; chip->LastFreq[i] = 0xFFFF; chip->OPLFreq[i] = 0x0000; SendVolume(chip, i); //SendFrequency(chip, i); OPL_RegMapper(0xB0 | i, 0x00); } return; } void start_ay8910_opl(UINT8 ChipID, int clock, UINT8 chip_type) { ay8910_context_opl* chip; if (ChipID >= MAX_CHIPS) return; chip = &AY8910Data[ChipID]; ay8910_start_opl(clock & 0x7FFFFFFF, chip, chip_type); return; } ================================================ FILE: VGMPlay/chips/ay_intf.c ================================================ /**************************************************************** MAME / MESS functions ****************************************************************/ #include // for free #include // for memset #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #include "ay8910.h" // must be always included (for YM2149_PIN26_LOW) #include "emu2149.h" #include "ay_intf.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // AY8910 core from MAME #endif #define EC_EMU2149 0x00 // EMU2149 from NSFPlay /* for stream system */ typedef struct _ayxx_state ayxx_state; struct _ayxx_state { void *chip; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static ayxx_state AYxxData[MAX_CHIPS]; void ayxx_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->chip, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->chip, outputs, samples); break; } } int device_start_ayxx(UINT8 ChipID, int clock, UINT8 chip_type, UINT8 Flags) { ayxx_state *info; int rate; if (ChipID >= MAX_CHIPS) return 0; info = &AYxxData[ChipID]; if (Flags & YM2149_PIN26_LOW) rate = clock / 16; else rate = clock / 8; if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: rate = ay8910_start(&info->chip, clock, chip_type, Flags); break; #endif case EC_EMU2149: if (Flags & YM2149_PIN26_LOW) clock /= 2; info->chip = PSG_new(clock, rate); if (info->chip == NULL) return 0; PSG_setVolumeMode((PSG*)info->chip, (chip_type & 0x10) ? 1 : 2); PSG_setFlags((PSG*)info->chip, Flags & ~YM2149_PIN26_LOW); break; } return rate; } void device_stop_ayxx(UINT8 ChipID) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->chip); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->chip); break; } info->chip = NULL; } void device_reset_ayxx(UINT8 ChipID) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->chip); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->chip); break; } } void ayxx_w(UINT8 ChipID, offs_t offset, UINT8 data) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->chip, offset, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->chip, offset, data); break; } } void ayxx_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_EMU2149; #endif return; } void ayxx_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_mute_mask_ym(info->chip, MuteMask); break; #endif case EC_EMU2149: PSG_setMask((PSG*)info->chip, MuteMask); break; } return; } void ayxx_set_stereo_mask(UINT8 ChipID, UINT32 StereoMask) { ayxx_state *info = &AYxxData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_stereo_mask_ym(info->chip, StereoMask); break; #endif case EC_EMU2149: PSG_setStereoMask((PSG*)info->chip, StereoMask); break; } return; } ================================================ FILE: VGMPlay/chips/ay_intf.h ================================================ #pragma once void ayxx_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ayxx(UINT8 ChipID, int clock, UINT8 chip_type, UINT8 Flags); void device_stop_ayxx(UINT8 ChipID); void device_reset_ayxx(UINT8 ChipID); void ayxx_w(UINT8 ChipID, offs_t offset, UINT8 data); void ayxx_set_emu_core(UINT8 Emulator); void ayxx_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void ayxx_set_stereo_mask(UINT8 ChipID, UINT32 StereoMask); ================================================ FILE: VGMPlay/chips/c140.c ================================================ /* C140.c Simulator based on AMUSE sources. The C140 sound chip is used by Namco System 2 and System 21 The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2 This chip controls 24 channels (C140) or 16 (219) of PCM. 16 bytes are associated with each channel. Channels can be 8 bit signed PCM, or 12 bit signed PCM. Timer behavior is not yet handled. Unmapped registers: 0x1f8:timer interval? (Nx0.1 ms) 0x1fa:irq ack? timer restart? 0x1fe:timer switch?(0:off 1:on) -------------- ASIC "219" notes On the 219 ASIC used on NA-1 and NA-2, the high registers have the following meaning instead: 0x1f7: bank for voices 0-3 0x1f1: bank for voices 4-7 0x1f3: bank for voices 8-11 0x1f5: bank for voices 12-15 Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15. Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2. Additionally, the 219's base pitch is the same as the C352's (42667). But these changes are IMO not sufficient to make this a separate file - all the other registers are fully compatible. Finally, the 219 only has 16 voices. */ /* 2000.06.26 CAB fixed compressed pcm playback 2002.07.20 R.Belmont added support for multiple banking types 2006.01.08 R.Belmont added support for NA-1/2 "219" derivative */ //#include "emu.h" #include #include // for memset #include // for NULL #include "mamedef.h" #include "c140.h" #define MAX_VOICE 24 struct voice_registers { UINT8 volume_right; UINT8 volume_left; UINT8 frequency_msb; UINT8 frequency_lsb; UINT8 bank; UINT8 mode; UINT8 start_msb; UINT8 start_lsb; UINT8 end_msb; UINT8 end_lsb; UINT8 loop_msb; UINT8 loop_lsb; UINT8 reserved[4]; }; typedef struct { long ptoffset; long pos; long key; //--work long lastdt; long prevdt; long dltdt; //--reg long rvol; long lvol; long frequency; long bank; long mode; long sample_start; long sample_end; long sample_loop; UINT8 Muted; } VOICE; typedef struct _c140_state c140_state; struct _c140_state { int sample_rate; //sound_stream *stream; int banking_type; /* internal buffers */ INT16 *mixer_buffer_left; INT16 *mixer_buffer_right; int baserate; UINT32 pRomSize; void *pRom; UINT8 REG[0x200]; INT16 pcmtbl[8]; //2000.06.26 CAB VOICE voi[MAX_VOICE]; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static c140_state C140Data[MAX_CHIPS]; /*INLINE c140_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == C140); return (c140_state *)downcast(device)->token(); }*/ static void init_voice( VOICE *v ) { v->key=0; v->ptoffset=0; v->rvol=0; v->lvol=0; v->frequency=0; v->bank=0; v->mode=0; v->sample_start=0; v->sample_end=0; v->sample_loop=0; } //READ8_DEVICE_HANDLER( c140_r ) UINT8 c140_r(UINT8 ChipID, offs_t offset) { //c140_state *info = get_safe_token(device); c140_state *info = &C140Data[ChipID]; offset&=0x1ff; return info->REG[offset]; } /* find_sample: compute the actual address of a sample given it's address and banking registers, as well as the board type. I suspect in "real life" this works like the Sega MultiPCM where the banking is done by a small PAL or GAL external to the sound chip, which can be switched per-game or at least per-PCB revision as addressing range needs grow. */ static long find_sample(c140_state *info, long adrs, long bank, int voice) { long newadr = 0; static const INT16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 }; adrs=(bank<<16)+adrs; switch (info->banking_type) { case C140_TYPE_SYSTEM2: // System 2 banking newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff); break; case C140_TYPE_SYSTEM21: // System 21 banking. // similar to System 2's. newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff); break; /*case C140_TYPE_SYSTEM21_A: // System 21 type A (simple) banking. // similar to System 2's. newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff); break; case C140_TYPE_SYSTEM21_B: // System 21 type B (chip select) banking // get base address of sample inside the bank newadr = ((adrs&0x100000)>>2) + (adrs&0x3ffff); // now add the starting bank offsets based on the 2 // chip select bits. // 0x40000 picks individual 512k ROMs if (adrs & 0x40000) { newadr += 0x80000; } // and 0x200000 which group of chips... if (adrs & 0x200000) { newadr += 0x100000; } break;*/ case C140_TYPE_ASIC219: // ASIC219's banking is fairly simple newadr = ((info->REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs; break; } return (newadr); } //WRITE8_DEVICE_HANDLER( c140_w ) void c140_w(UINT8 ChipID, offs_t offset, UINT8 data) { //c140_state *info = get_safe_token(device); c140_state *info = &C140Data[ChipID]; //info->stream->update(); offset&=0x1ff; // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE) if ((offset >= 0x1f8) && (info->banking_type == C140_TYPE_ASIC219)) { offset -= 8; } info->REG[offset]=data; if( offset<0x180 ) { VOICE *v = &info->voi[offset>>4]; if( (offset&0xf)==0x5 ) { if( data&0x80 ) { const struct voice_registers *vreg = (struct voice_registers *) &info->REG[offset&0x1f0]; v->key=1; v->ptoffset=0; v->pos=0; v->lastdt=0; v->prevdt=0; v->dltdt=0; v->bank = vreg->bank; v->mode = data; // on the 219 asic, addresses are in words if (info->banking_type == C140_TYPE_ASIC219) { v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2; v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2; v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2; #if 0 logerror("219: play v %d mode %02x start %x loop %x end %x\n", offset>>4, v->mode, find_sample(info, v->sample_start, v->bank, offset>>4), find_sample(info, v->sample_loop, v->bank, offset>>4), find_sample(info, v->sample_end, v->bank, offset>>4)); #endif } else { v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb; v->sample_start = vreg->start_msb*256 + vreg->start_lsb; v->sample_end = vreg->end_msb*256 + vreg->end_lsb; } } else { v->key=0; } } } } //void c140_set_base(device_t *device, void *base) void c140_set_base(UINT8 ChipID, void *base) { //c140_state *info = get_safe_token(device); c140_state *info = &C140Data[ChipID]; info->pRom = base; } /*INLINE int limit(INT32 in) { if(in>0x7fff) return 0x7fff; else if(in<-0x8000) return -0x8000; return in; }*/ //static STREAM_UPDATE( update_stereo ) void c140_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //c140_state *info = (c140_state *)param; c140_state *info = &C140Data[ChipID]; int i,j; INT32 rvol,lvol; INT32 dt; INT32 sdt; INT32 st,ed,sz; INT8 *pSampleData; INT32 frequency,delta,offset,pos; INT32 cnt, voicecnt; INT32 lastdt,prevdt,dltdt; float pbase=(float)info->baserate*2.0f / (float)info->sample_rate; INT16 *lmix, *rmix; if(samples>info->sample_rate) samples=info->sample_rate; /* zap the contents of the mixer buffer */ memset(info->mixer_buffer_left, 0, samples * sizeof(INT16)); memset(info->mixer_buffer_right, 0, samples * sizeof(INT16)); if (info->pRom == NULL) return; /* get the number of voices to update */ voicecnt = (info->banking_type == C140_TYPE_ASIC219) ? 16 : 24; //--- audio update for( i=0;ivoi[i]; const struct voice_registers *vreg = (struct voice_registers *)&info->REG[i*16]; if( v->key && ! v->Muted) { frequency= vreg->frequency_msb*256 + vreg->frequency_lsb; /* Abort voice if no frequency value set */ if(frequency==0) continue; /* Delta = frequency * ((8MHz/374)*2 / sample rate) */ delta=(long)((float)frequency * pbase); /* Calculate left/right channel volumes */ lvol=(vreg->volume_left*32)/MAX_VOICE; //32ch -> 24ch rvol=(vreg->volume_right*32)/MAX_VOICE; /* Set mixer outputs base pointers */ lmix = info->mixer_buffer_left; rmix = info->mixer_buffer_right; /* Retrieve sample start/end and calculate size */ st=v->sample_start; ed=v->sample_end; sz=ed-st; /* Retrieve base pointer to the sample data */ //pSampleData=(signed char*)((FPTR)info->pRom + find_sample(info, st, v->bank, i)); pSampleData = (INT8*)info->pRom + find_sample(info, st, v->bank, i); /* Fetch back previous data pointers */ offset=v->ptoffset; pos=v->pos; lastdt=v->lastdt; prevdt=v->prevdt; dltdt=v->dltdt; /* Switch on data type - compressed PCM is only for C140 */ if ((v->mode&8) && (info->banking_type != C140_TYPE_ASIC219)) { //compressed PCM (maybe correct...) /* Loop for enough to fill sample buffer as requested */ for(j=0;j>16)&0x7fff; offset &= 0xffff; pos+=cnt; //for(;cnt>0;cnt--) { /* Check for the end of the sample */ if(pos >= sz) { /* Check if its a looping sample, either stop or loop */ if(v->mode&0x10) { pos = (v->sample_loop - st); } else { v->key=0; break; } } /* Read the chosen sample byte */ dt=pSampleData[pos]; /* decompress to 13bit range */ //2000.06.26 CAB sdt=dt>>3; //signed if(sdt<0) sdt = (sdt<<(dt&7)) - info->pcmtbl[dt&7]; else sdt = (sdt<<(dt&7)) + info->pcmtbl[dt&7]; prevdt=lastdt; lastdt=sdt; dltdt=(lastdt - prevdt); } /* Caclulate the sample value */ dt=((dltdt*offset)>>16)+prevdt; /* Write the data to the sample buffers */ *lmix++ +=(dt*lvol)>>(5+5); *rmix++ +=(dt*rvol)>>(5+5); } } else { /* linear 8bit signed PCM */ for(j=0;j>16)&0x7fff; offset &= 0xffff; pos += cnt; /* Check for the end of the sample */ if(pos >= sz) { /* Check if its a looping sample, either stop or loop */ if( v->mode&0x10 ) { pos = (v->sample_loop - st); } else { v->key=0; break; } } if( cnt ) { prevdt=lastdt; if (info->banking_type == C140_TYPE_ASIC219) { //lastdt = pSampleData[BYTE_XOR_BE(pos)]; lastdt = pSampleData[pos ^ 0x01]; // Sign + magnitude format if ((v->mode & 0x01) && (lastdt & 0x80)) lastdt = -(lastdt & 0x7f); // Sign flip if (v->mode & 0x40) lastdt = -lastdt; } else { lastdt=pSampleData[pos]; } dltdt = (lastdt - prevdt); } /* Caclulate the sample value */ dt=((dltdt*offset)>>16)+prevdt; /* Write the data to the sample buffers */ *lmix++ +=(dt*lvol)>>5; *rmix++ +=(dt*rvol)>>5; } } /* Save positional data for next callback */ v->ptoffset=offset; v->pos=pos; v->lastdt=lastdt; v->prevdt=prevdt; v->dltdt=dltdt; } } /* render to MAME's stream buffer */ lmix = info->mixer_buffer_left; rmix = info->mixer_buffer_right; { stream_sample_t *dest1 = outputs[0]; stream_sample_t *dest2 = outputs[1]; for (i = 0; i < samples; i++) { //*dest1++ = limit(8*(*lmix++)); //*dest2++ = limit(8*(*rmix++)); *dest1++ = 8 * (*lmix ++); *dest2++ = 8 * (*rmix ++); } } } //static DEVICE_START( c140 ) int device_start_c140(UINT8 ChipID, int clock, int banking_type) { //const c140_interface *intf = (const c140_interface *)device->static_config(); //c140_state *info = get_safe_token(device); c140_state *info; int i; if (ChipID >= MAX_CHIPS) return 0; info = &C140Data[ChipID]; //info->sample_rate=info->baserate=device->clock(); if (clock < 1000000) info->baserate = clock; else info->baserate = clock / 384; // based on MAME's notes on Namco System II info->sample_rate = info->baserate; if (((CHIP_SAMPLING_MODE & 0x01) && info->sample_rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) info->sample_rate = CHIP_SAMPLE_RATE; if (info->sample_rate >= 0x1000000) // limit to 16 MHz sample rate (32 MB buffer) return 0; //info->banking_type = intf->banking_type; info->banking_type = banking_type; //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->sample_rate,info,update_stereo); //info->pRom=*device->region(); info->pRomSize = 0x00; info->pRom = NULL; /* make decompress pcm table */ //2000.06.26 CAB { INT32 segbase=0; for(i=0;i<8;i++) { info->pcmtbl[i]=segbase; //segment base value segbase += 16<REG,0,sizeof(info->REG)); { int i; for(i=0;ivoi[i] ); }*/ /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */ //info->mixer_buffer_left = auto_alloc_array(device->machine(), INT16, 2 * info->sample_rate); info->mixer_buffer_left = (INT16*)malloc(sizeof(INT16) * 2 * info->sample_rate); info->mixer_buffer_right = info->mixer_buffer_left + info->sample_rate; for (i = 0; i < MAX_VOICE; i ++) info->voi[i].Muted = 0x00; return info->sample_rate; } void device_stop_c140(UINT8 ChipID) { c140_state *info = &C140Data[ChipID]; free(info->pRom); info->pRom = NULL; free(info->mixer_buffer_left); return; } void device_reset_c140(UINT8 ChipID) { c140_state *info = &C140Data[ChipID]; int i; memset(info->REG, 0, sizeof(info->REG)); for(i = 0; i < MAX_VOICE; i ++) init_voice( &info->voi[i] ); return; } void c140_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { c140_state *info = &C140Data[ChipID]; if (info->pRomSize != ROMSize) { info->pRom = (UINT8*)realloc(info->pRom, ROMSize); info->pRomSize = ROMSize; memset(info->pRom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy((INT8*)info->pRom + DataStart, ROMData, DataLength); return; } void c140_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { c140_state *info = &C140Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < MAX_VOICE; CurChn ++) info->voi[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( c140 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c140_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c140 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: // nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "C140"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Namco PCM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(C140, c140);*/ ================================================ FILE: VGMPlay/chips/c140.h ================================================ /* C140.h */ #pragma once void c140_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_c140(UINT8 ChipID, int clock, int banking_type); void device_stop_c140(UINT8 ChipID); void device_reset_c140(UINT8 ChipID); //READ8_DEVICE_HANDLER( c140_r ); //WRITE8_DEVICE_HANDLER( c140_w ); UINT8 c140_r(UINT8 ChipID, offs_t offset); void c140_w(UINT8 ChipID, offs_t offset, UINT8 data); //void c140_set_base(device_t *device, void *base); void c140_set_base(UINT8 ChipID, void *base); enum { C140_TYPE_SYSTEM2, C140_TYPE_SYSTEM21, C140_TYPE_ASIC219 }; /*typedef struct _c140_interface c140_interface; struct _c140_interface { int banking_type; };*/ void c140_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void c140_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(C140, c140); ================================================ FILE: VGMPlay/chips/c352.c ================================================ // license:BSD-3-Clause // copyright-holders:R. Belmont, superctr /* c352.c - Namco C352 custom PCM chip emulation v2.0 By R. Belmont Rewritten and improved by superctr Additional code by cync and the hoot development team Thanks to Cap of VivaNonno for info and The_Author for preliminary reverse-engineering Chip specs: 32 voices Supports 8-bit linear and 8-bit muLaw samples Output: digital, 16 bit, 4 channels Output sample rate is the input clock / (288 * 2). */ //#include "emu.h" //#include "streams.h" #include #include #include // for memset #include // for NULL #include "mamedef.h" #include "c352.h" #define C352_VOICES 32 enum { C352_FLG_BUSY = 0x8000, // channel is busy C352_FLG_KEYON = 0x4000, // Keyon C352_FLG_KEYOFF = 0x2000, // Keyoff C352_FLG_LOOPTRG = 0x1000, // Loop Trigger C352_FLG_LOOPHIST = 0x0800, // Loop History C352_FLG_FM = 0x0400, // Frequency Modulation C352_FLG_PHASERL = 0x0200, // Rear Left invert phase 180 degrees C352_FLG_PHASEFL = 0x0100, // Front Left invert phase 180 degrees C352_FLG_PHASEFR = 0x0080, // invert phase 180 degrees (e.g. flip sign of sample) C352_FLG_LDIR = 0x0040, // loop direction C352_FLG_LINK = 0x0020, // "long-format" sample (can't loop, not sure what else it means) C352_FLG_NOISE = 0x0010, // play noise instead of sample C352_FLG_MULAW = 0x0008, // sample is mulaw instead of linear 8-bit PCM C352_FLG_FILTER = 0x0004, // don't apply filter C352_FLG_REVLOOP = 0x0003, // loop backwards C352_FLG_LOOP = 0x0002, // loop forward C352_FLG_REVERSE = 0x0001 // play sample backwards }; typedef struct { UINT32 pos; UINT32 counter; INT16 sample; INT16 last_sample; UINT16 vol_f; UINT16 vol_r; UINT8 curr_vol[4]; UINT16 freq; UINT16 flags; UINT16 wave_bank; UINT16 wave_start; UINT16 wave_end; UINT16 wave_loop; UINT8 mute; } C352_Voice; typedef struct { UINT32 sample_rate_base; UINT16 divider; C352_Voice v[C352_VOICES]; UINT16 random; UINT16 control; // control flags, purpose unknown. UINT8* wave; UINT32 wavesize; UINT32 wave_mask; UINT8 muteRear; // flag from VGM header //UINT8 optMuteRear; // option UINT16 mulaw_table[256]; } C352; #define MAX_CHIPS 0x02 static C352 C352Data[MAX_CHIPS]; static UINT8 MuteAllRear = 0x00; static void C352_fetch_sample(C352 *c, C352_Voice *v) { v->last_sample = v->sample; if(v->flags & C352_FLG_NOISE) { c->random = (c->random>>1) ^ ((-(c->random&1)) & 0xfff6); v->sample = c->random; } else { INT8 s; UINT16 pos; s = (INT8)c->wave[v->pos & c->wave_mask]; v->sample = s<<8; if(v->flags & C352_FLG_MULAW) { v->sample = c->mulaw_table[s&0xff]; } pos = v->pos&0xffff; if((v->flags & C352_FLG_LOOP) && v->flags & C352_FLG_REVERSE) { // backwards>forwards if((v->flags & C352_FLG_LDIR) && pos == v->wave_loop) v->flags &= ~C352_FLG_LDIR; // forwards>backwards else if(!(v->flags & C352_FLG_LDIR) && pos == v->wave_end) v->flags |= C352_FLG_LDIR; v->pos += (v->flags&C352_FLG_LDIR) ? -1 : 1; } else if(pos == v->wave_end) { if((v->flags & C352_FLG_LINK) && (v->flags & C352_FLG_LOOP)) { v->pos = (v->wave_start<<16) | v->wave_loop; v->flags |= C352_FLG_LOOPHIST; } else if(v->flags & C352_FLG_LOOP) { v->pos = (v->pos&0xff0000) | v->wave_loop; v->flags |= C352_FLG_LOOPHIST; } else { v->flags |= C352_FLG_KEYOFF; v->flags &= ~C352_FLG_BUSY; v->sample=0; } } else { v->pos += (v->flags&C352_FLG_REVERSE) ? -1 : 1; } } } static void c352_ramp_volume(C352_Voice* v,int ch,UINT8 val) { INT16 vol_delta = v->curr_vol[ch] - val; if(vol_delta != 0) v->curr_vol[ch] += (vol_delta>0) ? -1 : 1; } void c352_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { C352 *c = &C352Data[ChipID]; int i, j; INT16 s; INT32 next_counter; C352_Voice* v; stream_sample_t out[4]; memset(outputs[0], 0, samples * sizeof(stream_sample_t)); memset(outputs[1], 0, samples * sizeof(stream_sample_t)); for(i=0;iv[j]; s = 0; if(v->flags & C352_FLG_BUSY) { next_counter = v->counter+v->freq; if(next_counter & 0x10000) { C352_fetch_sample(c,v); } if((next_counter^v->counter) & 0x18000) { c352_ramp_volume(v,0,v->vol_f>>8); c352_ramp_volume(v,1,v->vol_f&0xff); c352_ramp_volume(v,2,v->vol_r>>8); c352_ramp_volume(v,3,v->vol_r&0xff); } v->counter = next_counter&0xffff; s = v->sample; // Interpolate samples if((v->flags & C352_FLG_FILTER) == 0) s = v->last_sample + (v->counter*(v->sample-v->last_sample)>>16); } if(!c->v[j].mute) { // Left out[0] += (((v->flags & C352_FLG_PHASEFL) ? -s : s) * v->curr_vol[0])>>8; out[2] += (((v->flags & C352_FLG_PHASERL) ? -s : s) * v->curr_vol[2])>>8; // Right out[1] += (((v->flags & C352_FLG_PHASEFR) ? -s : s) * v->curr_vol[1])>>8; out[3] += (((v->flags & C352_FLG_PHASEFR) ? -s : s) * v->curr_vol[3])>>8; } } outputs[0][i] += out[0]; outputs[1][i] += out[1]; if (!c->muteRear && !MuteAllRear) { outputs[0][i] += out[2]; outputs[1][i] += out[3]; } } } int device_start_c352(UINT8 ChipID, int clock, int clkdiv) { C352 *c; int i,j; if (ChipID >= MAX_CHIPS) return 0; c = &C352Data[ChipID]; c->wave = NULL; c->wavesize = 0x00; c->divider = clkdiv ? clkdiv : 288; c->sample_rate_base = (clock&0x7FFFFFFF) / c->divider; c->muteRear = (clock&0x80000000)>>31; memset(c->v,0,sizeof(C352_Voice)*C352_VOICES); c352_set_mute_mask(ChipID, 0x00000000); j=0; for(i=0;i<128;i++) { c->mulaw_table[i] = j<<5; if(i < 16) j += 1; else if(i < 24) j += 2; else if(i < 48) j += 4; else if(i < 100) j += 8; else j += 16; } for(i=128;i<256;i++) c->mulaw_table[i] = (~c->mulaw_table[i-128])&0xffe0; return c->sample_rate_base; } void device_stop_c352(UINT8 ChipID) { C352 *c = &C352Data[ChipID]; free(c->wave); c->wave = NULL; return; } void device_reset_c352(UINT8 ChipID) { C352 *c = &C352Data[ChipID]; UINT32 muteMask; muteMask = c352_get_mute_mask(ChipID); // clear all channels states memset(c->v,0,sizeof(C352_Voice)*C352_VOICES); // init noise generator c->random = 0x1234; c->control = 0; c352_set_mute_mask(ChipID, muteMask); return; } static UINT16 C352RegMap[8] = { offsetof(C352_Voice,vol_f) / sizeof(UINT16), offsetof(C352_Voice,vol_r) / sizeof(UINT16), offsetof(C352_Voice,freq) / sizeof(UINT16), offsetof(C352_Voice,flags) / sizeof(UINT16), offsetof(C352_Voice,wave_bank) / sizeof(UINT16), offsetof(C352_Voice,wave_start) / sizeof(UINT16), offsetof(C352_Voice,wave_end) / sizeof(UINT16), offsetof(C352_Voice,wave_loop) / sizeof(UINT16), }; UINT16 c352_r(UINT8 ChipID, offs_t address) { C352 *c = &C352Data[ChipID]; if(address < 0x100) return *((UINT16*)&c->v[address/8]+C352RegMap[address%8]); else if(address == 0x200) return c->control; else return 0; } void c352_w(UINT8 ChipID, offs_t address, UINT16 val) { C352 *c = &C352Data[ChipID]; int i; if(address < 0x100) // Channel registers, see map above. { *((UINT16*)&c->v[address/8]+C352RegMap[address%8]) = val; } else if(address == 0x200) { c->control = val; //logerror("C352 control register write: %04x\n",val); } else if(address == 0x202) // execute keyons/keyoffs { for(i=0;iv[i].flags & C352_FLG_KEYON)) { c->v[i].pos = (c->v[i].wave_bank<<16) | c->v[i].wave_start; c->v[i].sample = 0; c->v[i].last_sample = 0; c->v[i].counter = 0xffff; c->v[i].flags |= C352_FLG_BUSY; c->v[i].flags &= ~(C352_FLG_KEYON|C352_FLG_LOOPHIST); c->v[i].curr_vol[0] = c->v[i].curr_vol[1] = 0; c->v[i].curr_vol[2] = c->v[i].curr_vol[3] = 0; } else if(c->v[i].flags & C352_FLG_KEYOFF) { c->v[i].flags &= ~(C352_FLG_BUSY|C352_FLG_KEYOFF); c->v[i].counter = 0xffff; } } } } void c352_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { C352 *c = &C352Data[ChipID]; if (c->wavesize != ROMSize) { c->wave = (UINT8*)realloc(c->wave, ROMSize); c->wavesize = ROMSize; for (c->wave_mask = 1; c->wave_mask < c->wavesize; c->wave_mask <<= 1) ; c->wave_mask --; memset(c->wave, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(c->wave + DataStart, ROMData, DataLength); return; } void c352_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { C352 *c = &C352Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < C352_VOICES; CurChn ++) c->v[CurChn].mute = (MuteMask >> CurChn) & 0x01; return; } UINT32 c352_get_mute_mask(UINT8 ChipID) { C352 *c = &C352Data[ChipID]; UINT32 muteMask; UINT8 CurChn; muteMask = 0x00000000; for (CurChn = 0; CurChn < C352_VOICES; CurChn ++) muteMask |= (c->v[CurChn].mute << CurChn); return muteMask; } void c352_set_options(UINT8 Flags) { MuteAllRear = (Flags & 0x01) >> 0; return; } ================================================ FILE: VGMPlay/chips/c352.h ================================================ #pragma once #ifndef __C352_H__ #define __C352_H__ void c352_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_c352(UINT8 ChipID, int clock, int clkdiv); void device_stop_c352(UINT8 ChipID); void device_reset_c352(UINT8 ChipID); UINT16 c352_r(UINT8 ChipID, offs_t offset); void c352_w(UINT8 ChipID, offs_t offset, UINT16 data); void c352_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void c352_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); UINT32 c352_get_mute_mask(UINT8 ChipID); void c352_set_options(UINT8 Flags); #endif /* __C352_H__ */ ================================================ FILE: VGMPlay/chips/c6280.c ================================================ /* HuC6280 sound chip emulator by Charles MacDonald E-mail: cgfm2@hotmail.com WWW: http://cgfm2.emuviews.com Thanks to: - Paul Clifford for his PSG documentation. - Richard Bannister for the TGEmu-specific sound updating code. - http://www.uspto.gov for the PSG patents. - All contributors to the tghack-list. Changes: (03/30/2003) - Removed TGEmu specific code and added support functions for MAME. - Modified setup code to handle multiple chips with different clock and volume settings. Missing features / things to do: - Add LFO support. But do any games actually use it? - Add shared index for waveform playback and sample writes. Almost every game will reset the index prior to playback so this isn't an issue. - While the noise emulation is complete, the data for the pseudo-random bitstream is calculated by machine.rand() and is not a representation of what the actual hardware does. For some background on Hudson Soft's C62 chipset: - http://www.hudsonsoft.net/ww/about/about.html - http://www.hudson.co.jp/corp/eng/coinfo/history.html Legal information: Copyright Charles MacDonald This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#include "emu.h" #include // for rand #include // for memset #include // for pow #include "mamedef.h" #include "c6280.h" typedef struct { UINT16 frequency; UINT8 control; UINT8 balance; UINT8 waveform[32]; UINT8 index; INT16 dda; UINT8 noise_control; UINT32 noise_counter; UINT32 counter; UINT8 Muted; } t_channel; typedef struct { //sound_stream *stream; //device_t *device; //device_t *cpudevice; UINT8 select; UINT8 balance; UINT8 lfo_frequency; UINT8 lfo_control; t_channel channel[8]; // is 8, because: p->select = data & 0x07; INT16 volume_table[32]; UINT32 noise_freq_tab[32]; UINT32 wave_freq_tab[4096]; } c6280_t; /*INLINE c6280_t *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == C6280); return (c6280_t *)downcast(device)->token(); }*/ /* only needed for io_buffer */ //#include "cpu/h6280/h6280.h" static void c6280_init(/*device_t *device,*/ c6280_t *p, double clk, double rate) { //const c6280_interface *intf = (const c6280_interface *)device->static_config(); int i; double step; /* Loudest volume level for table */ //double level = 65535.0 / 6.0 / 32.0; double level = 65536.0 / 6.0 / 32.0; /* Clear context */ memset(p, 0, sizeof(c6280_t)); //p->device = device; //p->cpudevice = device->machine().device(intf->cpu); //if (p->cpudevice == NULL) // fatalerror("c6280_init: no CPU found with tag of '%s'\n", device->tag()); /* Make waveform frequency table */ for(i = 0; i < 4096; i += 1) { step = ((clk / rate) * 4096) / (i+1); p->wave_freq_tab[(1 + i) & 0xFFF] = (UINT32)step; } /* Make noise frequency table */ for(i = 0; i < 32; i += 1) { step = ((clk / rate) * 32) / (i+1); p->noise_freq_tab[i] = (UINT32)step; } /* Make volume table */ /* PSG has 48dB volume range spread over 32 steps */ step = 48.0 / 32.0; for(i = 0; i < 31; i++) { p->volume_table[i] = (UINT16)level; level /= pow(10.0, step / 20.0); } p->volume_table[31] = 0; } static void c6280_write(c6280_t *p, int offset, int data) { t_channel *q = &p->channel[p->select]; /* Update stream */ //p->stream->update(); switch(offset & 0x0F) { case 0x00: /* Channel select */ p->select = data & 0x07; break; case 0x01: /* Global balance */ p->balance = data; break; case 0x02: /* Channel frequency (LSB) */ q->frequency = (q->frequency & 0x0F00) | data; q->frequency &= 0x0FFF; break; case 0x03: /* Channel frequency (MSB) */ q->frequency = (q->frequency & 0x00FF) | (data << 8); q->frequency &= 0x0FFF; break; case 0x04: /* Channel control (key-on, DDA mode, volume) */ /* 1-to-0 transition of DDA bit resets waveform index */ if((q->control & 0x40) && ((data & 0x40) == 0)) { q->index = 0; } q->control = data; break; case 0x05: /* Channel balance */ q->balance = data; break; case 0x06: /* Channel waveform data */ switch(q->control & 0xC0) { case 0x00: q->waveform[q->index & 0x1F] = data & 0x1F; q->index = (q->index + 1) & 0x1F; break; case 0x40: break; case 0x80: q->waveform[q->index & 0x1F] = data & 0x1F; q->index = (q->index + 1) & 0x1F; break; case 0xC0: q->dda = data & 0x1F; break; } break; case 0x07: /* Noise control (enable, frequency) */ q->noise_control = data; break; case 0x08: /* LFO frequency */ p->lfo_frequency = data; break; case 0x09: /* LFO control (enable, mode) */ p->lfo_control = data; break; default: break; } } //static STREAM_UPDATE( c6280_update ) void c6280m_update(void* param, stream_sample_t **outputs, int samples) { static const int scale_tab[] = { 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F }; int ch; int i; c6280_t *p = (c6280_t *)param; int lmal = (p->balance >> 4) & 0x0F; int rmal = (p->balance >> 0) & 0x0F; int vll, vlr; lmal = scale_tab[lmal]; rmal = scale_tab[rmal]; /* Clear buffer */ for(i = 0; i < samples; i++) { outputs[0][i] = 0; outputs[1][i] = 0; } for(ch = 0; ch < 6; ch++) { /* Only look at enabled channels */ if((p->channel[ch].control & 0x80) && ! p->channel[ch].Muted) { int lal = (p->channel[ch].balance >> 4) & 0x0F; int ral = (p->channel[ch].balance >> 0) & 0x0F; int al = p->channel[ch].control & 0x1F; lal = scale_tab[lal]; ral = scale_tab[ral]; /* Calculate volume just as the patent says */ vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); if(vll > 0x1F) vll = 0x1F; vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); if(vlr > 0x1F) vlr = 0x1F; vll = p->volume_table[vll]; vlr = p->volume_table[vlr]; /* Check channel mode */ if((ch >= 4) && (p->channel[ch].noise_control & 0x80)) { /* Noise mode */ UINT32 step = p->noise_freq_tab[(p->channel[ch].noise_control & 0x1F) ^ 0x1F]; for(i = 0; i < samples; i += 1) { static int data = 0; p->channel[ch].noise_counter += step; if(p->channel[ch].noise_counter >= 0x800) { //data = (p->device->machine().rand() & 1) ? 0x1F : 0; data = (rand() & 1) ? 0x1F : 0; } p->channel[ch].noise_counter &= 0x7FF; outputs[0][i] += (INT16)(vll * (data - 16)); outputs[1][i] += (INT16)(vlr * (data - 16)); } } else if(p->channel[ch].control & 0x40) { /* DDA mode */ for(i = 0; i < samples; i++) { outputs[0][i] += (INT16)(vll * (p->channel[ch].dda - 16)); outputs[1][i] += (INT16)(vlr * (p->channel[ch].dda - 16)); } } else { /* Waveform mode */ UINT32 step = p->wave_freq_tab[p->channel[ch].frequency]; for(i = 0; i < samples; i += 1) { int offset; INT16 data; offset = (p->channel[ch].counter >> 12) & 0x1F; p->channel[ch].counter += step; p->channel[ch].counter &= 0x1FFFF; data = p->channel[ch].waveform[offset]; outputs[0][i] += (INT16)(vll * (data - 16)); outputs[1][i] += (INT16)(vlr * (data - 16)); } } } } } /*--------------------------------------------------------------------------*/ /* MAME specific code */ /*--------------------------------------------------------------------------*/ //static DEVICE_START( c6280 ) void* device_start_c6280m(int clock, int rate) { //int rate = device->clock()/16; //c6280_t *info = get_safe_token(device); c6280_t *info; UINT8 CurChn; info = (c6280_t*)malloc(sizeof(c6280_t)); if (info == NULL) return 0; memset(info, 0x00, sizeof(c6280_t)); /* Initialize PSG emulator */ //c6280_init(device, info, device->clock(), rate); c6280_init(info, clock & 0x7FFFFFFF, rate); /* Create stereo stream */ //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, rate, info, c6280_update); for (CurChn = 0; CurChn < 6; CurChn ++) info->channel[CurChn].Muted = 0x00; return info; } void device_stop_c6280m(void* chip) { c6280_t *info = (c6280_t *)chip; free(info); return; } void device_reset_c6280m(void* chip) { c6280_t *info = (c6280_t *)chip; UINT8 CurChn; t_channel* TempChn; info->select = 0x00; info->balance = 0x00; info->lfo_frequency = 0x00; info->lfo_control = 0x00; for (CurChn = 0; CurChn < 6; CurChn ++) { TempChn = &info->channel[CurChn]; TempChn->frequency = 0x00; TempChn->control = 0x00; TempChn->balance = 0x00; memset(TempChn->waveform, 0x00, 0x20); TempChn->index = 0x00; TempChn->dda = 0x00; TempChn->noise_control = 0x00; TempChn->noise_counter = 0x00; TempChn->counter = 0x00; } return; } //READ8_DEVICE_HANDLER( c6280_r ) UINT8 c6280m_r(void* chip, offs_t offset) { //c6280_t *info = get_safe_token(device); c6280_t *info = (c6280_t *)chip; //return h6280io_get_buffer(info->cpudevice); if (offset == 0) return info->select; return 0; } //WRITE8_DEVICE_HANDLER( c6280_w ) void c6280m_w(void* chip, offs_t offset, UINT8 data) { //c6280_t *info = get_safe_token(device); c6280_t *info = (c6280_t *)chip; //h6280io_set_buffer(info->cpudevice, data); c6280_write(info, offset, data); } void c6280m_set_mute_mask(void* chip, UINT32 MuteMask) { c6280_t *info = (c6280_t *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) info->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( c6280 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c6280_t); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c6280 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: // nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "HuC6280"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "????"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(C6280, c6280);*/ ================================================ FILE: VGMPlay/chips/c6280.h ================================================ #pragma once //#include "devlegcy.h" typedef struct _c6280_interface c6280_interface; struct _c6280_interface { const char * cpu; }; /* Function prototypes */ //WRITE8_DEVICE_HANDLER( c6280_w ); //READ8_DEVICE_HANDLER( c6280_r ); void c6280m_w(void* chip, offs_t offset, UINT8 data); UINT8 c6280m_r(void* chip, offs_t offset); void c6280m_update(void* param, stream_sample_t **outputs, int samples); void* device_start_c6280m(int clock, int rate); void device_stop_c6280m(void* chip); void device_reset_c6280m(void* chip); void c6280m_set_mute_mask(void* chip, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(C6280, c6280); ================================================ FILE: VGMPlay/chips/c6280intf.c ================================================ #include // for NULL #include "mamedef.h" #ifdef ENABLE_ALL_CORES #include "c6280.h" #endif #include "Ootake_PSG.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 #endif #define EC_OOTAKE 0x00 typedef struct _c6280_state { void* chip; } c6280_state; extern UINT32 SampleRate; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static c6280_state C6280Data[MAX_CHIPS]; void c6280_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: c6280m_update(info->chip, outputs, samples); break; #endif case EC_OOTAKE: PSG_Mix(info->chip, outputs, samples); break; } } int device_start_c6280(UINT8 ChipID, int clock) { c6280_state* info; int rate = 0; if (ChipID >= MAX_CHIPS) return 0; info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: rate = (clock & 0x7FFFFFFF)/16; if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; info->chip = device_start_c6280m(clock, rate); if (info->chip == NULL) return 0; break; #endif case EC_OOTAKE: rate = SampleRate; info->chip = PSG_Init(clock, rate); if (info->chip == NULL) return 0; break; } return rate; } void device_stop_c6280(UINT8 ChipID) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_stop_c6280m(info->chip); break; #endif case EC_OOTAKE: PSG_Deinit(info->chip); break; } info->chip = NULL; return; } void device_reset_c6280(UINT8 ChipID) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_reset_c6280m(info->chip); break; #endif case EC_OOTAKE: PSG_ResetVolumeReg(info->chip); break; } return; } UINT8 c6280_r(UINT8 ChipID, offs_t offset) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return c6280m_r(info->chip, offset); #endif case EC_OOTAKE: return PSG_Read(info->chip, offset); default: return 0x00; } } void c6280_w(UINT8 ChipID, offs_t offset, UINT8 data) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: c6280m_w(info->chip, offset, data); break; #endif case EC_OOTAKE: PSG_Write(info->chip, offset, data); break; } return; } void c6280_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_OOTAKE; #endif return; } void c6280_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { c6280_state* info = &C6280Data[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: c6280m_set_mute_mask(info->chip, MuteMask); break; #endif case EC_OOTAKE: PSG_SetMuteMask(info->chip, MuteMask); break; } return; } ================================================ FILE: VGMPlay/chips/c6280intf.h ================================================ #pragma once void c6280_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 c6280_r(UINT8 ChipID, offs_t offset); void c6280_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_c6280(UINT8 ChipID, int clock); void device_stop_c6280(UINT8 ChipID); void device_reset_c6280(UINT8 ChipID); void c6280_set_emu_core(UINT8 Emulator); void c6280_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/dac_control.c ================================================ // TODO: SCSP and (especially) WonderSwan /************************ * DAC Stream Control * ***********************/ // (Custom Driver to handle PCM Streams of YM2612 DAC and PWM.) // // Written on 3 February 2011 by Valley Bell // Last Update: 29 September 2017 // // Only for usage in non-commercial, VGM file related software. /* How it basically works: 1. send command X with data Y at frequency F to chip C 2. do that until you receive a STOP command, or until you sent N commands */ #include // for NULL #include "mamedef.h" #include "dac_control.h" #include "c6280intf.h" //#include "../ChipMapper.h" void chip_reg_write(UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data); extern UINT32 SampleRate; #define DAC_SMPL_RATE SampleRate typedef struct _dac_control { // Commands sent to dest-chip UINT8 DstChipType; UINT8 DstChipID; UINT16 DstCommand; UINT8 CmdSize; UINT32 Frequency; // Frequency (Hz) at which the commands are sent UINT32 DataLen; // to protect from reading beyond End Of Data const UINT8* Data; UINT32 DataStart; // Position where to start UINT8 StepSize; // usually 1, set to 2 for L/R interleaved data UINT8 StepBase; // usually 0, set to 0/1 for L/R interleaved data UINT32 CmdsToSend; // Running Bits: 0 (01) - is playing // 2 (04) - loop sample (simple loop from start to end) // 4 (10) - already sent this command // 7 (80) - disabled UINT8 Running; UINT8 Reverse; UINT32 Step; // Position in Player SampleRate UINT32 Pos; // Position in Data SampleRate UINT32 RemainCmds; UINT32 RealPos; // true Position in Data (== Pos, if Reverse is off) UINT8 DataStep; // always StepSize * CmdSize } dac_control; #define MAX_CHIPS 0xFF static dac_control DACData[MAX_CHIPS]; INLINE void daccontrol_SendCommand(dac_control *chip) { UINT8 Port; UINT8 Command; UINT8 Data; const UINT8* ChipData; if (chip->Running & 0x10) // command already sent return; if (chip->DataStart + chip->RealPos >= chip->DataLen) return; //if (! chip->Reverse) ChipData = chip->Data + (chip->DataStart + chip->RealPos); //else // ChipData = chip->Data + (chip->DataStart + chip->CmdsToSend - 1 - chip->Pos); switch(chip->DstChipType) { // Support for the important chips case 0x02: // YM2612 (16-bit Register (actually 9 Bit), 8-bit Data) Port = (chip->DstCommand & 0xFF00) >> 8; Command = (chip->DstCommand & 0x00FF) >> 0; Data = ChipData[0x00]; chip_reg_write(chip->DstChipType, chip->DstChipID, Port, Command, Data); break; case 0x11: // PWM (4-bit Register, 12-bit Data) Port = (chip->DstCommand & 0x000F) >> 0; Command = ChipData[0x01] & 0x0F; Data = ChipData[0x00]; chip_reg_write(chip->DstChipType, chip->DstChipID, Port, Command, Data); break; // Support for other chips (mainly for completeness) case 0x00: // SN76496 (4-bit Register, 4-bit/10-bit Data) Command = (chip->DstCommand & 0x00F0) >> 0; Data = ChipData[0x00] & 0x0F; if (Command & 0x10) { // Volume Change (4-Bit value) chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); } else { // Frequency Write (10-Bit value) Port = ((ChipData[0x01] & 0x03) << 4) | ((ChipData[0x00] & 0xF0) >> 4); chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, 0x00, Port); } break; case 0x18: // OKIM6295 - TODO: verify Command = (chip->DstCommand & 0x00FF) >> 0; Data = ChipData[0x00]; if (! Command) { Port = (chip->DstCommand & 0x0F00) >> 8; if (Data & 0x80) { // Sample Start // write sample ID chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command, Data); // write channel(s) that should play the sample chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command, Port << 4); } else { // Sample Stop chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command, Port << 3); } } else { chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command, Data); } break; // Generic support: 8-bit Register, 8-bit Data case 0x01: // YM2413 case 0x03: // YM2151 case 0x06: // YM2203 case 0x09: // YM3812 case 0x0A: // YM3526 case 0x0B: // Y8950 case 0x0F: // YMZ280B case 0x12: // AY8910 case 0x13: // GameBoy DMG case 0x14: // NES APU // case 0x15: // MultiPCM case 0x16: // UPD7759 case 0x17: // OKIM6258 case 0x1D: // K053260 - TODO: Verify case 0x1E: // Pokey - TODO: Verify Command = (chip->DstCommand & 0x00FF) >> 0; Data = ChipData[0x00]; chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command, Data); break; // Generic support: 16-bit Register, 8-bit Data case 0x07: // YM2608 case 0x08: // YM2610/B case 0x0C: // YMF262 case 0x0D: // YMF278B case 0x0E: // YMF271 case 0x19: // K051649 - TODO: Verify case 0x1A: // K054539 - TODO: Verify case 0x1C: // C140 - TODO: Verify Port = (chip->DstCommand & 0xFF00) >> 8; Command = (chip->DstCommand & 0x00FF) >> 0; Data = ChipData[0x00]; chip_reg_write(chip->DstChipType, chip->DstChipID, Port, Command, Data); break; // Generic support: 8-bit Register with Channel Select, 8-bit Data case 0x05: // RF5C68 case 0x10: // RF5C164 case 0x1B: // HuC6280 Port = (chip->DstCommand & 0xFF00) >> 8; Command = (chip->DstCommand & 0x00FF) >> 0; Data = ChipData[0x00]; if (Port == 0xFF) { chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command & 0x0F, Data); } else { UINT8 prevChn; prevChn = Port; // by default don't restore channel // get current channel for supported chips if (chip->DstChipType == 0x05) ; // TODO else if (chip->DstChipType == 0x05) ; // TODO else if (chip->DstChipType == 0x1B) prevChn = c6280_r(chip->DstChipID, 0x00); // Send Channel Select chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command >> 4, Port); // Send Data chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command & 0x0F, Data); // restore old channel if (prevChn != Port) chip_reg_write(chip->DstChipType, chip->DstChipID, 0x00, Command >> 4, prevChn); } break; // Generic support: 8-bit Register, 16-bit Data case 0x1F: // QSound Command = (chip->DstCommand & 0x00FF) >> 0; chip_reg_write(chip->DstChipType, chip->DstChipID, ChipData[0x00], ChipData[0x01], Command); break; } chip->Running |= 0x10; return; } INLINE UINT32 muldiv64round(UINT32 Multiplicand, UINT32 Multiplier, UINT32 Divisor) { // Yes, I'm correctly rounding the values. return (UINT32)(((UINT64)Multiplicand * Multiplier + Divisor / 2) / Divisor); } void daccontrol_update(UINT8 ChipID, UINT32 samples) { dac_control *chip = &DACData[ChipID]; UINT32 NewPos; INT16 RealDataStp; if (chip->Running & 0x80) // disabled return; if (! (chip->Running & 0x01)) // stopped return; if (! chip->Reverse) RealDataStp = chip->DataStep; else RealDataStp = -chip->DataStep; if (samples > 0x20) { // very effective Speed Hack for fast seeking NewPos = chip->Step + (samples - 0x10); NewPos = muldiv64round(NewPos * chip->DataStep, chip->Frequency, DAC_SMPL_RATE); while(chip->RemainCmds && chip->Pos < NewPos) { chip->Pos += chip->DataStep; chip->RealPos += RealDataStp; chip->RemainCmds --; } } chip->Step += samples; // Formula: Step * Freq / SampleRate NewPos = muldiv64round(chip->Step * chip->DataStep, chip->Frequency, DAC_SMPL_RATE); daccontrol_SendCommand(chip); while(chip->RemainCmds && chip->Pos < NewPos) { daccontrol_SendCommand(chip); chip->Pos += chip->DataStep; chip->RealPos += RealDataStp; chip->Running &= ~0x10; chip->RemainCmds --; } if (! chip->RemainCmds && (chip->Running & 0x04)) { // loop back to start chip->RemainCmds = chip->CmdsToSend; chip->Step = 0x00; chip->Pos = 0x00; if (! chip->Reverse) chip->RealPos = 0x00; else chip->RealPos = (chip->CmdsToSend - 0x01) * chip->DataStep; } if (! chip->RemainCmds) chip->Running &= ~0x01; // stop return; } UINT8 device_start_daccontrol(UINT8 ChipID) { dac_control *chip; if (ChipID >= MAX_CHIPS) return 0; chip = &DACData[ChipID]; chip->DstChipType = 0xFF; chip->DstChipID = 0x00; chip->DstCommand = 0x0000; chip->Running = 0xFF; // disable all actions (except setup_chip) return 1; } void device_stop_daccontrol(UINT8 ChipID) { dac_control *chip = &DACData[ChipID]; chip->Running = 0xFF; return; } void device_reset_daccontrol(UINT8 ChipID) { dac_control *chip = &DACData[ChipID]; chip->DstChipType = 0x00; chip->DstChipID = 0x00; chip->DstCommand = 0x00; chip->CmdSize = 0x00; chip->Frequency = 0; chip->DataLen = 0x00; chip->Data = NULL; chip->DataStart = 0x00; chip->StepSize = 0x00; chip->StepBase = 0x00; chip->Running = 0x00; chip->Reverse = 0x00; chip->Step = 0x00; chip->Pos = 0x00; chip->RealPos = 0x00; chip->RemainCmds = 0x00; chip->DataStep = 0x00; return; } void daccontrol_setup_chip(UINT8 ChipID, UINT8 ChType, UINT8 ChNum, UINT16 Command) { dac_control *chip = &DACData[ChipID]; chip->DstChipType = ChType; // TypeID (e.g. 0x02 for YM2612) chip->DstChipID = ChNum; // chip number (to send commands to 1st or 2nd chip) chip->DstCommand = Command; // Port and Command (would be 0x02A for YM2612) switch(chip->DstChipType) { case 0x00: // SN76496 if (chip->DstCommand & 0x0010) chip->CmdSize = 0x01; // Volume Write else chip->CmdSize = 0x02; // Frequency Write break; case 0x02: // YM2612 chip->CmdSize = 0x01; break; case 0x11: // PWM case 0x1F: // QSound chip->CmdSize = 0x02; break; default: chip->CmdSize = 0x01; break; } chip->DataStep = chip->CmdSize * chip->StepSize; return; } void daccontrol_set_data(UINT8 ChipID, UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase) { dac_control *chip = &DACData[ChipID]; if (chip->Running & 0x80) return; if (DataLen && Data != NULL) { chip->DataLen = DataLen; chip->Data = Data; } else { chip->DataLen = 0x00; chip->Data = NULL; } chip->StepSize = StepSize ? StepSize : 1; chip->StepBase = StepBase; chip->DataStep = chip->CmdSize * chip->StepSize; return; } void daccontrol_refresh_data(UINT8 ChipID, UINT8* Data, UINT32 DataLen) { // Should be called to fix the data pointer. (e.g. after a realloc) dac_control *chip = &DACData[ChipID]; if (chip->Running & 0x80) return; if (DataLen && Data != NULL) { chip->DataLen = DataLen; chip->Data = Data; } else { chip->DataLen = 0x00; chip->Data = NULL; } return; } void daccontrol_set_frequency(UINT8 ChipID, UINT32 Frequency) { dac_control *chip = &DACData[ChipID]; if (chip->Running & 0x80) return; if (Frequency) chip->Step = chip->Step * chip->Frequency / Frequency; chip->Frequency = Frequency; return; } void daccontrol_start(UINT8 ChipID, UINT32 DataPos, UINT8 LenMode, UINT32 Length) { dac_control *chip = &DACData[ChipID]; UINT16 CmdStepBase; if (chip->Running & 0x80) return; CmdStepBase = chip->CmdSize * chip->StepBase; if (DataPos != 0xFFFFFFFF) // skip setting DataStart, if Pos == -1 { chip->DataStart = DataPos + CmdStepBase; if (chip->DataStart > chip->DataLen) // catch bad value and force silence chip->DataStart = chip->DataLen; } switch(LenMode & 0x0F) { case DCTRL_LMODE_IGNORE: // Length is already set - ignore break; case DCTRL_LMODE_CMDS: // Length = number of commands chip->CmdsToSend = Length; break; case DCTRL_LMODE_MSEC: // Length = time in msec chip->CmdsToSend = 1000 * Length / chip->Frequency; break; case DCTRL_LMODE_TOEND: // play unti stop-command is received (or data-end is reached) chip->CmdsToSend = (chip->DataLen - (chip->DataStart - CmdStepBase)) / chip->DataStep; break; case DCTRL_LMODE_BYTES: // raw byte count chip->CmdsToSend = Length / chip->DataStep; break; default: chip->CmdsToSend = 0x00; break; } chip->Reverse = (LenMode & 0x10) >> 4; chip->RemainCmds = chip->CmdsToSend; chip->Step = 0x00; chip->Pos = 0x00; if (! chip->Reverse) chip->RealPos = 0x00; else chip->RealPos = (chip->CmdsToSend - 0x01) * chip->DataStep; chip->Running &= ~0x04; chip->Running |= (LenMode & 0x80) ? 0x04 : 0x00; // set loop mode chip->Running |= 0x01; // start chip->Running &= ~0x10; // command isn't yet sent return; } void daccontrol_stop(UINT8 ChipID) { dac_control *chip = &DACData[ChipID]; if (chip->Running & 0x80) return; chip->Running &= ~0x01; // stop return; } ================================================ FILE: VGMPlay/chips/dac_control.h ================================================ void daccontrol_update(UINT8 ChipID, UINT32 samples); UINT8 device_start_daccontrol(UINT8 ChipID); void device_stop_daccontrol(UINT8 ChipID); void device_reset_daccontrol(UINT8 ChipID); void daccontrol_setup_chip(UINT8 ChipID, UINT8 ChType, UINT8 ChNum, UINT16 Command); void daccontrol_set_data(UINT8 ChipID, UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase); void daccontrol_refresh_data(UINT8 ChipID, UINT8* Data, UINT32 DataLen); void daccontrol_set_frequency(UINT8 ChipID, UINT32 Frequency); void daccontrol_start(UINT8 ChipID, UINT32 DataPos, UINT8 LenMode, UINT32 Length); void daccontrol_stop(UINT8 ChipID); #define DCTRL_LMODE_IGNORE 0x00 #define DCTRL_LMODE_CMDS 0x01 #define DCTRL_LMODE_MSEC 0x02 #define DCTRL_LMODE_TOEND 0x03 #define DCTRL_LMODE_BYTES 0x0F ================================================ FILE: VGMPlay/chips/emu2149.c ================================================ /**************************************************************************** emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 2001 04-28 : Version 1.00beta -- 1st Beta Release. 2001 08-14 : Version 1.10 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. 2002 10-13 : Version 1.14 -- Fixed the envelope unit. 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope frequency register is written before key-on. References: psg.vhd -- 2000 written by Kazuhiro Tsujikawa. s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). ay8910.c -- 1998-2001 Author unknown (MAME). MSX-Datapack -- 1991 ASCII Corp. AY-3-8910 data sheet *****************************************************************************/ #include #include #include #include "emu2149.h" static e_uint32 voltbl[2][32] = { {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x12, 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, 0xB4, 0xD6, 0xEB, 0xFF}, {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x0B, 0x0B, 0x0F, 0x0F, 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, 0xB4, 0xB4, 0xFF, 0xFF} }; #define GETA_BITS 24 static void internal_refresh (PSG * psg) { if (psg->quality) { psg->base_incr = 1 << GETA_BITS; psg->realstep = (e_uint32) ((1 << 31) / psg->rate); psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 8)); psg->psgtime = 0; } else { psg->base_incr = (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (8.0 * psg->rate)); } } EMU2149_API void PSG_set_clock(PSG * psg, e_uint32 c) { psg->clk = c; internal_refresh(psg); } EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r) { psg->rate = r ? r : 44100; internal_refresh (psg); } EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q) { psg->quality = q; internal_refresh (psg); } EMU2149_API PSG * PSG_new (e_uint32 c, e_uint32 r) { PSG *psg; psg = (PSG *) malloc (sizeof (PSG)); if (psg == NULL) return NULL; memset(psg, 0x00, sizeof(PSG)); PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); psg->clk = c; psg->rate = r ? r : 44100; PSG_set_quality (psg, 0); psg->stereo_mask[0] = 0x03; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x03; return psg; } EMU2149_API void PSG_setFlags (PSG * psg, e_uint8 flags) { if (flags & EMU2149_ZX_STEREO) { // ABC Stereo psg->stereo_mask[0] = 0x01; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x02; } else { psg->stereo_mask[0] = 0x03; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x03; } return; } EMU2149_API void PSG_setVolumeMode (PSG * psg, int type) { switch (type) { case 1: psg->voltbl = voltbl[EMU2149_VOL_YM2149]; break; case 2: psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; break; default: psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; break; } } EMU2149_API e_uint32 PSG_setMask (PSG *psg, e_uint32 mask) { e_uint32 ret = 0; if(psg) { ret = psg->mask; psg->mask = mask; } return ret; } EMU2149_API void PSG_setStereoMask (PSG *psg, e_uint32 mask) { e_uint32 ret = 0; if(psg) { psg->stereo_mask[0] = (mask >>0) &3; psg->stereo_mask[1] = (mask >>2) &3; psg->stereo_mask[2] = (mask >>4) &3; } } EMU2149_API e_uint32 PSG_toggleMask (PSG *psg, e_uint32 mask) { e_uint32 ret = 0; if(psg) { ret = psg->mask; psg->mask ^= mask; } return ret; } EMU2149_API void PSG_reset (PSG * psg) { int i; psg->base_count = 0; for (i = 0; i < 3; i++) { psg->cout[i] = 0; psg->count[i] = 0x1000; psg->freq[i] = 0; psg->edge[i] = 0; psg->volume[i] = 0; } psg->mask = 0; for (i = 0; i < 16; i++) psg->reg[i] = 0; psg->adr = 0; psg->noise_seed = 0xffff; psg->noise_count = 0x40; psg->noise_freq = 0; psg->env_volume = 0; psg->env_ptr = 0; psg->env_freq = 0; psg->env_count = 0; psg->env_pause = 1; psg->out = 0; } EMU2149_API void PSG_delete (PSG * psg) { free (psg); } EMU2149_API e_uint8 PSG_readIO (PSG * psg) { return (e_uint8) (psg->reg[psg->adr]); } EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg) { return (e_uint8) (psg->reg[reg & 0x1f]); } EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) { if (adr & 1) PSG_writeReg (psg, psg->adr, val); else psg->adr = val & 0x1f; } INLINE static e_int16 calc (PSG * psg) { int i, noise; e_uint32 incr; e_int32 mix = 0; psg->base_count += psg->base_incr; incr = (psg->base_count >> GETA_BITS); psg->base_count &= (1 << GETA_BITS) - 1; /* Envelope */ psg->env_count += incr; while (psg->env_count>=0x10000 && psg->env_freq!=0) { if (!psg->env_pause) { if(psg->env_face) psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; else psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; } if (psg->env_ptr & 0x20) /* if carry or borrow */ { if (psg->env_continue) { if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; if (psg->env_hold) psg->env_pause = 1; psg->env_ptr = psg->env_face?0:0x1f; } else { psg->env_pause = 1; psg->env_ptr = 0; } } psg->env_count -= psg->env_freq; } /* Noise */ psg->noise_count += incr; if (psg->noise_count & 0x40) { if (psg->noise_seed & 1) psg->noise_seed ^= 0x24000; psg->noise_seed >>= 1; psg->noise_count -= psg->noise_freq; } noise = psg->noise_seed & 1; /* Tone */ for (i = 0; i < 3; i++) { psg->count[i] += incr; if (psg->count[i] & 0x1000) { if (psg->freq[i] > 1) { psg->edge[i] = !psg->edge[i]; psg->count[i] -= psg->freq[i]; } else { psg->edge[i] = 1; } } psg->cout[i] = 0; // BS maintaining cout for stereo mix if (psg->mask&PSG_MASK_CH(i)) continue; if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) { if (!(psg->volume[i] & 32)) psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; else psg->cout[i] = psg->voltbl[psg->env_ptr]; mix += psg->cout[i]; } } return (e_int16) mix; } EMU2149_API e_int16 PSG_calc (PSG * psg) { if (!psg->quality) return (e_int16) (calc (psg) << 4); /* Simple rate converter */ while (psg->realstep > psg->psgtime) { psg->psgtime += psg->psgstep; psg->out += calc (psg); psg->out >>= 1; } psg->psgtime = psg->psgtime - psg->realstep; return (e_int16) (psg->out << 4); } INLINE static void calc_stereo (PSG * psg, e_int32 out[2]) { int i, noise; e_uint32 incr; e_int32 l = 0, r = 0; psg->base_count += psg->base_incr; incr = (psg->base_count >> GETA_BITS); psg->base_count &= (1 << GETA_BITS) - 1; /* Envelope */ psg->env_count += incr; while (psg->env_count>=0x10000 && psg->env_freq!=0) { if (!psg->env_pause) { if(psg->env_face) psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; else psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; } if (psg->env_ptr & 0x20) /* if carry or borrow */ { if (psg->env_continue) { if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; if (psg->env_hold) psg->env_pause = 1; psg->env_ptr = psg->env_face?0:0x1f; } else { psg->env_pause = 1; psg->env_ptr = 0; } } psg->env_count -= psg->env_freq; } /* Noise */ psg->noise_count += incr; if (psg->noise_count & 0x40) { if (psg->noise_seed & 1) psg->noise_seed ^= 0x24000; psg->noise_seed >>= 1; psg->noise_count -= psg->noise_freq; } noise = psg->noise_seed & 1; /* Tone */ for (i = 0; i < 3; i++) { psg->count[i] += incr; if (psg->count[i] & 0x1000) { if (psg->freq[i] > 1) { psg->edge[i] = !psg->edge[i]; psg->count[i] -= psg->freq[i]; } else { psg->edge[i] = 1; } } psg->cout[i] = 0; // BS maintaining cout for stereo mix if (psg->mask&PSG_MASK_CH(i)) continue; if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) { if (!(psg->volume[i] & 32)) psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; else psg->cout[i] = psg->voltbl[psg->env_ptr]; if (psg->stereo_mask[i] & 0x01) l += psg->cout[i]; if (psg->stereo_mask[i] & 0x02) r += psg->cout[i]; } } out[0] = l << 5; out[1] = r << 5; return; } EMU2149_API void PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples) { e_int32 *bufMO = out[0]; e_int32 *bufRO = out[1]; e_int32 buffers[2]; int i; for (i = 0; i < samples; i ++) { if (!psg->quality) { calc_stereo (psg, buffers); bufMO[i] = buffers[0]; bufRO[i] = buffers[1]; } else { while (psg->realstep > psg->psgtime) { psg->psgtime += psg->psgstep; psg->sprev[0] = psg->snext[0]; psg->sprev[1] = psg->snext[1]; calc_stereo (psg, psg->snext); } psg->psgtime -= psg->realstep; bufMO[i] = (e_int32) (((double) psg->snext[0] * (psg->psgstep - psg->psgtime) + (double) psg->sprev[0] * psg->psgtime) / psg->psgstep); bufRO[i] = (e_int32) (((double) psg->snext[1] * (psg->psgstep - psg->psgtime) + (double) psg->sprev[1] * psg->psgtime) / psg->psgstep); } } } EMU2149_API void PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) { int c; if (reg > 15) return; psg->reg[reg] = (e_uint8) (val & 0xff); switch (reg) { case 0: case 2: case 4: case 1: case 3: case 5: c = reg >> 1; psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; break; case 6: psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); break; case 7: psg->tmask[0] = (val & 1); psg->tmask[1] = (val & 2); psg->tmask[2] = (val & 4); psg->nmask[0] = (val & 8); psg->nmask[1] = (val & 16); psg->nmask[2] = (val & 32); break; case 8: case 9: case 10: psg->volume[reg - 8] = val << 1; break; case 11: case 12: psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; break; case 13: psg->env_continue = (val >> 3) & 1; psg->env_attack = (val >> 2) & 1; psg->env_alternate = (val >> 1) & 1; psg->env_hold = val & 1; psg->env_face = psg->env_attack; psg->env_pause = 0; psg->env_count = 0x10000 - psg->env_freq; psg->env_ptr = psg->env_face?0:0x1f; break; case 14: case 15: default: break; } return; } ================================================ FILE: VGMPlay/chips/emu2149.h ================================================ /* emu2149.h */ #ifndef _EMU2149_H_ #define _EMU2149_H_ #include "emutypes.h" /*#ifdef EMU2149_DLL_EXPORTS #define EMU2149_API __declspec(dllexport) #elif EMU2149_DLL_IMPORTS #define EMU2149_API __declspec(dllimport) #else*/ #define EMU2149_API //#endif #define EMU2149_VOL_DEFAULT 1 #define EMU2149_VOL_YM2149 0 #define EMU2149_VOL_AY_3_8910 1 #define EMU2149_ZX_STEREO 0x80 #define PSG_MASK_CH(x) (1<<(x)) /*#ifdef __cplusplus extern "C" { #endif*/ typedef struct __PSG { /* Volume Table */ e_uint32 *voltbl; e_uint8 reg[0x20]; e_int32 out; e_int32 cout[3]; e_uint32 clk, rate, base_incr, quality; e_uint32 count[3]; e_uint32 volume[3]; e_uint32 freq[3]; e_uint32 edge[3]; e_uint32 tmask[3]; e_uint32 nmask[3]; e_uint32 mask; e_uint32 stereo_mask[3]; e_uint32 base_count; e_uint32 env_volume; e_uint32 env_ptr; e_uint32 env_face; e_uint32 env_continue; e_uint32 env_attack; e_uint32 env_alternate; e_uint32 env_hold; e_uint32 env_pause; e_uint32 env_reset; e_uint32 env_freq; e_uint32 env_count; e_uint32 noise_seed; e_uint32 noise_count; e_uint32 noise_freq; /* rate converter */ e_uint32 realstep; e_uint32 psgtime; e_uint32 psgstep; e_int32 prev, next; e_int32 sprev[2], snext[2]; /* I/O Ctrl */ e_uint32 adr; } PSG; EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); EMU2149_API void PSG_set_clock(PSG * psg, e_uint32 c); EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); EMU2149_API void PSG_reset (PSG *); EMU2149_API void PSG_delete (PSG *); EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); EMU2149_API e_uint8 PSG_readIO (PSG * psg); EMU2149_API e_int16 PSG_calc (PSG *); EMU2149_API void PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples); EMU2149_API void PSG_setFlags (PSG * psg, e_uint8 flags); EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); EMU2149_API void PSG_setStereoMask (PSG *psg, e_uint32 mask); /*#ifdef __cplusplus } #endif*/ #endif ================================================ FILE: VGMPlay/chips/emu2413.c ================================================ /** * emu2413 v1.5.2 * https://github.com/digital-sound-antiques/emu2413 * Copyright (C) 2020 Mitsutaka Okazaki * * This source refers to the following documents. The author would like to thank all the authors who have * contributed to the writing of them. * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete * - ymf262.c by Jarek Burczynski * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT * - YMF281B presets by Chabin */ #include "emu2413.h" #include #include #include #include #include "panning.h" // Maxim #ifndef INLINE #if defined(_MSC_VER) #define INLINE __inline #elif defined(__GNUC__) #define INLINE __inline__ #else #define INLINE inline #endif #endif #define _PI_ 3.14159265358979323846264338327950288 #define OPLL_TONE_NUM 3 /* clang-format off */ static uint8_t default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = {{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0: Original 0x71,0x61,0x1e,0x17,0xd0,0x78,0x00,0x17, // 1: Violin 0x13,0x41,0x1a,0x0d,0xd8,0xf7,0x23,0x13, // 2: Guitar 0x13,0x01,0x99,0x00,0xf2,0xc4,0x21,0x23, // 3: Piano 0x11,0x61,0x0e,0x07,0x8d,0x64,0x70,0x27, // 4: Flute 0x32,0x21,0x1e,0x06,0xe1,0x76,0x01,0x28, // 5: Clarinet 0x31,0x22,0x16,0x05,0xe0,0x71,0x00,0x18, // 6: Oboe 0x21,0x61,0x1d,0x07,0x82,0x81,0x11,0x07, // 7: Trumpet 0x33,0x21,0x2d,0x13,0xb0,0x70,0x00,0x07, // 8: Organ 0x61,0x61,0x1b,0x06,0x64,0x65,0x10,0x17, // 9: Horn 0x41,0x61,0x0b,0x18,0x85,0xf0,0x81,0x07, // A: Synthesizer 0x33,0x01,0x83,0x11,0xea,0xef,0x10,0x04, // B: Harpsichord 0x17,0xc1,0x24,0x07,0xf8,0xf8,0x22,0x12, // C: Vibraphone 0x61,0x50,0x0c,0x05,0xd2,0xf5,0x40,0x42, // D: Synthsizer Bass 0x01,0x01,0x55,0x03,0xe9,0x90,0x03,0x02, // E: Acoustic Bass 0x41,0x41,0x89,0x03,0xf1,0xe4,0xc0,0x13, // F: Electric Guitar 0x01,0x01,0x18,0x0f,0xdf,0xf8,0x6a,0x6d, // R: Bass Drum (from VRC7) 0x01,0x01,0x00,0x00,0xc8,0xd8,0xa7,0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) 0x05,0x01,0x00,0x00,0xf8,0xaa,0x59,0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) },{ /* VRC7 presets from Nuke.YKT */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x03,0x21,0x05,0x06,0xe8,0x81,0x42,0x27, 0x13,0x41,0x14,0x0d,0xd8,0xf6,0x23,0x12, 0x11,0x11,0x08,0x08,0xfa,0xb2,0x20,0x12, 0x31,0x61,0x0c,0x07,0xa8,0x64,0x61,0x27, 0x32,0x21,0x1e,0x06,0xe1,0x76,0x01,0x28, 0x02,0x01,0x06,0x00,0xa3,0xe2,0xf4,0xf4, 0x21,0x61,0x1d,0x07,0x82,0x81,0x11,0x07, 0x23,0x21,0x22,0x17,0xa2,0x72,0x01,0x17, 0x35,0x11,0x25,0x00,0x40,0x73,0x72,0x01, 0xb5,0x01,0x0f,0x0F,0xa8,0xa5,0x51,0x02, 0x17,0xc1,0x24,0x07,0xf8,0xf8,0x22,0x12, 0x71,0x23,0x11,0x06,0x65,0x74,0x18,0x16, 0x01,0x02,0xd3,0x05,0xc9,0x95,0x03,0x02, 0x61,0x63,0x0c,0x00,0x94,0xC0,0x33,0xf6, 0x21,0x72,0x0d,0x00,0xc1,0xd5,0x56,0x06, 0x01,0x01,0x18,0x0f,0xdf,0xf8,0x6a,0x6d, 0x01,0x01,0x00,0x00,0xc8,0xd8,0xa7,0x68, 0x05,0x01,0x00,0x00,0xf8,0xaa,0x59,0x55, },{ /* YMF281B presets by Chabin */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, 0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, 0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, 0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, 0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, 0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, 0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, 0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, 0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, 0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, 0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, 0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, 0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, 0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, 0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, 0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, 0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, 0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, }}; /* clang-format on */ /* phase increment counter */ #define DP_BITS 19 #define DP_WIDTH (1 << DP_BITS) #define DP_BASE_BITS (DP_BITS - PG_BITS) /* dynamic range of envelope output */ #define EG_STEP 0.375 #define EG_BITS 7 #define EG_MUTE ((1 << EG_BITS) - 1) #define EG_MAX (EG_MUTE - 3) /* dynamic range of total level */ #define TL_STEP 0.75 #define TL_BITS 6 /* dynamic range of sustine level */ #define SL_STEP 3.0 #define SL_BITS 4 /* damper speed before key-on. key-scale affects. */ #define DAMPER_RATE 12 #define TL2EG(d) ((d) << 1) /* sine table */ #define PG_BITS 10 /* 2^10 = 1024 length sine table */ #define PG_WIDTH (1 << PG_BITS) /* clang-format off */ /* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ static uint16_t exp_table[256] = { 0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, 142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, 194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, 248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, 304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, 363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, 424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, 488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, 555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, 625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, 698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, 774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, 854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, 937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 }; /* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ static uint16_t fullsin_table[PG_WIDTH] = { 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, }; /* clang-format on */ static uint16_t halfsin_table[PG_WIDTH]; static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; /* pitch modulator */ /* offset to fnum, rough approximation of 14 cents depth. */ static int8_t pm_table[8][8] = { {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx }; /* amplitude lfo table */ /* The following envelop pattern is verified on real YM2413. */ /* each element repeates 64 cycles */ static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // 12, 12, 12, 12, 12, 12, 12, 12, // 13, 13, 13, // 12, 12, 12, 12, 12, 12, 12, 12, // 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; /* envelope decay increment step table */ /* based on andete's research */ static uint8_t eg_step_tables[4][8] = { {0, 1, 0, 1, 0, 1, 0, 1}, {0, 1, 0, 1, 1, 1, 0, 1}, {0, 1, 1, 1, 0, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 1}, }; enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; #define dB2(x) ((x)*2) static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; static int32_t rks_table[8 * 2][2]; static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; #define min(i, j) (((i) < (j)) ? (i) : (j)) #define max(i, j) (((i) > (j)) ? (i) : (j)) /*************************************************** Internal Sample Rate Converter ****************************************************/ /* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ /* * LW is truncate length of sinc(x) calculation. * Lower LW is faster, higher LW results better quality. * LW must be a non-zero positive even number, no upper limit. * LW=16 or greater is recommended when upsampling. * LW=8 is practically okay for downsampling. */ #define LW 16 /* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ #define SINC_RESO 256 #define SINC_AMP_BITS 12 // double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } /* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); int i; conv->ch = ch; conv->f_ratio = f_inp / f_out; conv->buf = malloc(sizeof(void *) * ch); for (i = 0; i < ch; i++) { conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); } /* create sinc_table for positive 0 <= x < LW/2 */ conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); for (i = 0; i < SINC_RESO * LW / 2; i++) { const double x = (double)i / SINC_RESO; if (f_out < f_inp) { /* for downsampling */ conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); } else { /* for upsampling */ conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); } } return conv; } static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { int16_t index = (int16_t)(x * SINC_RESO); if (index < 0) index = -index; return table[min(SINC_RESO * LW / 2 - 1, index)]; } void OPLL_RateConv_reset(OPLL_RateConv *conv) { int i; conv->timer = 0; for (i = 0; i < conv->ch; i++) { memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); } } /* put original data to this converter at f_inp. */ void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { int16_t *buf = conv->buf[ch]; int i; for (i = 0; i < LW - 1; i++) { buf[i] = buf[i + 1]; } buf[LW - 1] = data; } /* get resampled data from this converter at f_out. */ /* this function must be called f_out / f_inp times per one putData call. */ int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { int16_t *buf = conv->buf[ch]; int32_t sum = 0; int k; double dn; conv->timer += conv->f_ratio; dn = conv->timer - floor(conv->timer); conv->timer = dn; for (k = 0; k < LW; k++) { double x = ((double)k - (LW / 2 - 1)) - dn; sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); } return sum >> SINC_AMP_BITS; } void OPLL_RateConv_delete(OPLL_RateConv *conv) { int i; for (i = 0; i < conv->ch; i++) { free(conv->buf[i]); } free(conv->buf); free(conv->sinc_table); free(conv); } /*************************************************** Create tables ****************************************************/ static void makeSinTable(void) { int x; for (x = 0; x < PG_WIDTH / 4; x++) { fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; } for (x = 0; x < PG_WIDTH / 2; x++) { fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; } for (x = 0; x < PG_WIDTH / 2; x++) halfsin_table[x] = fullsin_table[x]; for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) halfsin_table[x] = 0xfff; } static void makeTllTable(void) { int32_t tmp; int32_t fnum, block, TL, KL; for (fnum = 0; fnum < 16; fnum++) { for (block = 0; block < 8; block++) { for (TL = 0; TL < 64; TL++) { for (KL = 0; KL < 4; KL++) { if (KL == 0) { tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); } else { tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); if (tmp <= 0) tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); else tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); } } } } } } static void makeRksTable(void) { int fnum8, block; for (fnum8 = 0; fnum8 < 2; fnum8++) for (block = 0; block < 8; block++) { rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; rks_table[(block << 1) | fnum8][0] = block >> 1; } } static void makeDefaultPatch() { int i, j; for (i = 0; i < OPLL_TONE_NUM; i++) for (j = 0; j < 19; j++) OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); } static uint8_t table_initialized = 0; static void initializeTables() { makeTllTable(); makeRksTable(); makeSinTable(); makeDefaultPatch(); table_initialized = 1; } /********************************************************* Synthesizing *********************************************************/ #define SLOT_BD1 12 #define SLOT_BD2 13 #define SLOT_HH 14 #define SLOT_SD 15 #define SLOT_TOM 16 #define SLOT_CYM 17 /* utility macros */ #define MOD(o, x) (&(o)->slot[(x) << 1]) #define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) #define BIT(s, b) (((s) >> (b)) & 1) #if OPLL_DEBUG static void _debug_print_patch(OPLL_SLOT *slot) { OPLL_PATCH *p = slot->patch; printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // p->AM, p->PM, p->EG, p->KR, p->ML, // p->KL, p->TL, p->WS, p->FB, // p->AR, p->DR, p->SL, p->RR); } static char *_debug_eg_state_name(OPLL_SLOT *slot) { switch (slot->eg_state) { case ATTACK: return "attack"; case DECAY: return "decay"; case SUSTAIN: return "sustain"; case RELEASE: return "release"; case DAMP: return "damp"; default: return "unknown"; } } static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { char *name = _debug_eg_state_name(slot); printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, slot->eg_rate_l); _debug_print_patch(slot); fflush(stdout); } #endif static INLINE int get_parameter_rate(OPLL_SLOT *slot) { if ((slot->type & 1) == 0 && slot->key_flag == 0) { return 0; } switch (slot->eg_state) { case ATTACK: return slot->patch->AR; case DECAY: return slot->patch->DR; case SUSTAIN: return slot->patch->EG ? 0 : slot->patch->RR; case RELEASE: if (slot->sus_flag) { return 5; } else if (slot->patch->EG) { return slot->patch->RR; } else { return 7; } case DAMP: return DAMPER_RATE; default: return 0; } } enum SLOT_UPDATE_FLAG { UPDATE_WS = 1, UPDATE_TLL = 2, UPDATE_RKS = 4, UPDATE_EG = 8, UPDATE_ALL = 255, }; static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } static void commit_slot_update(OPLL_SLOT *slot) { #if OPLL_DEBUG if (slot->last_eg_state != slot->eg_state) { _debug_print_slot_info(slot); slot->last_eg_state = slot->eg_state; } #endif if (slot->update_requests & UPDATE_WS) { slot->wave_table = wave_table_map[slot->patch->WS]; } if (slot->update_requests & UPDATE_TLL) { if ((slot->type & 1) == 0) { slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; } else { slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; } } if (slot->update_requests & UPDATE_RKS) { slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; } if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { int p_rate = get_parameter_rate(slot); if (p_rate == 0) { slot->eg_shift = 0; slot->eg_rate_h = 0; slot->eg_rate_l = 0; return; } slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); slot->eg_rate_l = slot->rks & 3; if (slot->eg_state == ATTACK) { slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; } else { slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; } } slot->update_requests = 0; } static void reset_slot(OPLL_SLOT *slot, int number) { slot->number = number; slot->type = number % 2; slot->pg_keep = 0; slot->wave_table = wave_table_map[0]; slot->pg_phase = 0; slot->output[0] = 0; slot->output[1] = 0; slot->eg_state = RELEASE; slot->eg_shift = 0; slot->rks = 0; slot->tll = 0; slot->key_flag = 0; slot->sus_flag = 0; slot->blk_fnum = 0; slot->blk = 0; slot->fnum = 0; slot->volume = 0; slot->pg_out = 0; slot->eg_out = EG_MUTE; slot->patch = &null_patch; } static INLINE void slotOn(OPLL *opll, int i) { OPLL_SLOT *slot = &opll->slot[i]; slot->key_flag = 1; slot->eg_state = DAMP; request_update(slot, UPDATE_EG); } static INLINE void slotOff(OPLL *opll, int i) { OPLL_SLOT *slot = &opll->slot[i]; slot->key_flag = 0; if (slot->type & 1) { slot->eg_state = RELEASE; request_update(slot, UPDATE_EG); } } static INLINE void update_key_status(OPLL *opll) { const uint8_t r14 = opll->reg[0x0e]; const uint8_t rhythm_mode = BIT(r14, 5); uint32_t new_slot_key_status = 0; uint32_t updated_status; int ch; for (ch = 0; ch < 9; ch++) if (opll->reg[0x20 + ch] & 0x10) new_slot_key_status |= 3 << (ch * 2); if (rhythm_mode) { if (r14 & 0x10) new_slot_key_status |= 3 << SLOT_BD1; if (r14 & 0x01) new_slot_key_status |= 1 << SLOT_HH; if (r14 & 0x08) new_slot_key_status |= 1 << SLOT_SD; if (r14 & 0x04) new_slot_key_status |= 1 << SLOT_TOM; if (r14 & 0x02) new_slot_key_status |= 1 << SLOT_CYM; } updated_status = opll->slot_key_status ^ new_slot_key_status; if (updated_status) { int i; for (i = 0; i < 18; i++) if (BIT(updated_status, i)) { if (BIT(new_slot_key_status, i)) { slotOn(opll, i); } else { slotOff(opll, i); } } } opll->slot_key_status = new_slot_key_status; } static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { opll->patch_number[ch] = num; MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; request_update(MOD(opll, ch), UPDATE_ALL); request_update(CAR(opll, ch), UPDATE_ALL); } static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { CAR(opll, ch)->sus_flag = flag; request_update(CAR(opll, ch), UPDATE_EG); if (MOD(opll, ch)->type & 1) { MOD(opll, ch)->sus_flag = flag; request_update(MOD(opll, ch), UPDATE_EG); } } /* set volume ( volume : 6bit, register value << 2 ) */ static INLINE void set_volume(OPLL *opll, int ch, int volume) { CAR(opll, ch)->volume = volume; request_update(CAR(opll, ch), UPDATE_TLL); } static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { slot->volume = volume; request_update(slot, UPDATE_TLL); } /* set f-Nnmber ( fnum : 9bit ) */ static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { OPLL_SLOT *car = CAR(opll, ch); OPLL_SLOT *mod = MOD(opll, ch); car->fnum = fnum; car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); mod->fnum = fnum; mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); } /* set block data (blk : 3bit ) */ static INLINE void set_block(OPLL *opll, int ch, int blk) { OPLL_SLOT *car = CAR(opll, ch); OPLL_SLOT *mod = MOD(opll, ch); car->blk = blk; car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); mod->blk = blk; mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); } static INLINE void update_rhythm_mode(OPLL *opll) { const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; if (opll->rhythm_mode != new_rhythm_mode) { if (new_rhythm_mode) { opll->slot[SLOT_HH].type = 3; opll->slot[SLOT_HH].pg_keep = 1; opll->slot[SLOT_SD].type = 3; opll->slot[SLOT_TOM].type = 3; opll->slot[SLOT_CYM].type = 3; opll->slot[SLOT_CYM].pg_keep = 1; set_patch(opll, 6, 16); set_patch(opll, 7, 17); set_patch(opll, 8, 18); set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); } else { opll->slot[SLOT_HH].type = 0; opll->slot[SLOT_HH].pg_keep = 0; opll->slot[SLOT_SD].type = 1; opll->slot[SLOT_TOM].type = 0; opll->slot[SLOT_CYM].type = 1; opll->slot[SLOT_CYM].pg_keep = 0; set_patch(opll, 6, opll->reg[0x36] >> 4); set_patch(opll, 7, opll->reg[0x37] >> 4); set_patch(opll, 8, opll->reg[0x38] >> 4); } } opll->rhythm_mode = new_rhythm_mode; } static void update_ampm(OPLL *opll) { if (opll->test_flag & 2) { opll->pm_phase = 0; opll->am_phase = 0; } else { opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; opll->am_phase += (opll->test_flag & 8) ? 64 : 1; } opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; } static void update_noise(OPLL *opll, int cycle) { int i; for (i = 0; i < cycle; i++) { if (opll->noise & 1) { opll->noise ^= 0x800200; } opll->noise >>= 1; } } static void update_short_noise(OPLL *opll) { const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); } static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; if (reset) { slot->pg_phase = 0; } slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; slot->pg_phase &= (DP_WIDTH - 1); slot->pg_out = slot->pg_phase >> DP_BASE_BITS; } static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { int index; switch (slot->eg_rate_h) { case 12: index = (counter & 0xc) >> 1; return 4 - eg_step_tables[slot->eg_rate_l][index]; case 13: index = (counter & 0xc) >> 1; return 3 - eg_step_tables[slot->eg_rate_l][index]; case 14: index = (counter & 0xc) >> 1; return 2 - eg_step_tables[slot->eg_rate_l][index]; case 0: case 15: return 0; default: index = counter >> slot->eg_shift; return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; } } static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { int index; switch (slot->eg_rate_h) { case 0: return 0; case 13: index = ((counter & 0xc) >> 1) | (counter & 1); return eg_step_tables[slot->eg_rate_l][index]; case 14: index = ((counter & 0xc) >> 1); return eg_step_tables[slot->eg_rate_l][index] + 1; case 15: return 2; default: index = counter >> slot->eg_shift; return eg_step_tables[slot->eg_rate_l][index & 7]; } } static INLINE void start_envelope(OPLL_SLOT *slot) { if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { slot->eg_state = DECAY; slot->eg_out = 0; } else { slot->eg_state = ATTACK; slot->eg_out = EG_MUTE; } request_update(slot, UPDATE_EG); } static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { uint32_t mask = (1 << slot->eg_shift) - 1; uint8_t s; if (slot->eg_state == ATTACK) { if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { s = lookup_attack_step(slot, eg_counter); if (0 < s) { slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); } } } else { if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); } } switch (slot->eg_state) { case DAMP: if (slot->eg_out >= EG_MUTE) { start_envelope(slot); if (slot->type & 1) { if (!slot->pg_keep) { slot->pg_phase = 0; } if (buddy && !buddy->pg_keep) { buddy->pg_phase = 0; } } } break; case ATTACK: if (slot->eg_out == 0) { slot->eg_state = DECAY; request_update(slot, UPDATE_EG); } break; case DECAY: if ((slot->eg_out >> 3) == slot->patch->SL) { slot->eg_state = SUSTAIN; request_update(slot, UPDATE_EG); } break; case SUSTAIN: case RELEASE: default: break; } if (test) { slot->eg_out = 0; } } static void update_slots(OPLL *opll) { int i; opll->eg_counter++; for (i = 0; i < 18; i++) { OPLL_SLOT *slot = &opll->slot[i]; OPLL_SLOT *buddy = NULL; if (slot->type == 0) { buddy = &opll->slot[i + 1]; } if (slot->type == 1) { buddy = &opll->slot[i - 1]; } if (slot->update_requests) { commit_slot_update(slot); } calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); calc_phase(slot, opll->pm_phase, opll->test_flag & 4); } } /* output: -4095...4095 */ static INLINE int16_t lookup_exp_table(uint16_t i) { /* from andete's expressoin */ int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); int16_t res = t >> ((i & 0x7f00) >> 8); return ((i & 0x8000) ? ~res : res) << 1; } static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { uint16_t att; if (slot->eg_out >= EG_MAX) return 0; att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; return lookup_exp_table(h + att); } static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { OPLL_SLOT *slot = CAR(opll, ch); uint8_t am = slot->patch->AM ? opll->lfo_am : 0; slot->output[1] = slot->output[0]; slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); return slot->output[0]; } static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { OPLL_SLOT *slot = MOD(opll, ch); int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; uint8_t am = slot->patch->AM ? opll->lfo_am : 0; slot->output[1] = slot->output[0]; slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); return slot->output[0]; } static INLINE int16_t calc_slot_tom(OPLL *opll) { OPLL_SLOT *slot = MOD(opll, 8); return to_linear(slot->wave_table[slot->pg_out], slot, 0); } /* Specify phase offset directly based on 10-bit (1024-length) sine table */ #define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) static INLINE int16_t calc_slot_snare(OPLL *opll) { OPLL_SLOT *slot = CAR(opll, 7); uint32_t phase; if (BIT(slot->pg_out, PG_BITS - 2)) phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); else phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); return to_linear(slot->wave_table[phase], slot, 0); } static INLINE int16_t calc_slot_cym(OPLL *opll) { OPLL_SLOT *slot = CAR(opll, 8); uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); return to_linear(slot->wave_table[phase], slot, 0); } static INLINE int16_t calc_slot_hat(OPLL *opll) { OPLL_SLOT *slot = MOD(opll, 7); uint32_t phase; if (opll->short_noise) phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); else phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); return to_linear(slot->wave_table[phase], slot, 0); } #define _MO(x) (-(x) >> 1) #define _RO(x) (x) static void update_output(OPLL *opll) { int16_t *out; int i; update_ampm(opll); update_short_noise(opll); update_slots(opll); out = opll->ch_out; /* CH1-6 */ for (i = 0; i < 6; i++) { if (!(opll->mask & OPLL_MASK_CH(i))) { out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); } } /* CH7 */ if (!opll->rhythm_mode) { if (!(opll->mask & OPLL_MASK_CH(6))) { out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); } } else { if (!(opll->mask & OPLL_MASK_BD)) { out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); } } update_noise(opll, 14); /* CH8 */ if (!opll->rhythm_mode) { if (!(opll->mask & OPLL_MASK_CH(7))) { out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); } } else { if (!(opll->mask & OPLL_MASK_HH)) { out[10] = _RO(calc_slot_hat(opll)); } if (!(opll->mask & OPLL_MASK_SD)) { out[11] = _RO(calc_slot_snare(opll)); } } update_noise(opll, 2); /* CH9 */ if (!opll->rhythm_mode) { if (!(opll->mask & OPLL_MASK_CH(8))) { out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); } } else { if (!(opll->mask & OPLL_MASK_TOM)) { out[12] = _RO(calc_slot_tom(opll)); } if (!(opll->mask & OPLL_MASK_CYM)) { out[13] = _RO(calc_slot_cym(opll)); } } update_noise(opll, 2); } INLINE static void mix_output(OPLL *opll) { int16_t out = 0; int i; for (i = 0; i < 14; i++) { out += opll->ch_out[i]; } if (opll->conv) { OPLL_RateConv_putData(opll->conv, 0, out); } else { opll->mix_out[0] = out; } } INLINE static void mix_output_stereo(OPLL *opll) { int16_t *out = opll->mix_out; int i; out[0] = out[1] = 0; for (i = 0; i < 14; i++) { /* Maxim/Valley Bell: added stereo control (multiply each side by a float in opll->pan[ch][side]) */ if (opll->pan[i] & 2) out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); if (opll->pan[i] & 1) out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); } if (opll->conv) { OPLL_RateConv_putData(opll->conv, 0, out[0]); OPLL_RateConv_putData(opll->conv, 1, out[1]); } } /*********************************************************** External Interfaces ***********************************************************/ OPLL *OPLL_new(uint32_t clk, uint32_t rate) { OPLL *opll; int i; if (!table_initialized) { initializeTables(); } opll = (OPLL *)calloc(sizeof(OPLL), 1); if (opll == NULL) return NULL; for (i = 0; i < 19 * 2; i++) memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); opll->clk = clk; opll->rate = rate; opll->mask = 0; opll->conv = NULL; opll->mix_out[0] = 0; opll->mix_out[1] = 0; OPLL_reset(opll); OPLL_setChipType(opll, 0); OPLL_resetPatch(opll, 0); return opll; } void OPLL_delete(OPLL *opll) { if (opll->conv) { OPLL_RateConv_delete(opll->conv); opll->conv = NULL; } free(opll); } static void reset_rate_conversion_params(OPLL *opll) { const double f_out = opll->rate; const double f_inp = opll->clk / 72; opll->out_time = 0; opll->out_step = ((uint32_t)f_inp) << 8; opll->inp_step = ((uint32_t)f_out) << 8; if (opll->conv) { OPLL_RateConv_delete(opll->conv); opll->conv = NULL; } if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); } if (opll->conv) { OPLL_RateConv_reset(opll->conv); } } void OPLL_reset(OPLL *opll) { int i; if (!opll) return; opll->adr = 0; opll->pm_phase = 0; opll->am_phase = 0; opll->noise = 0x1; opll->mask = 0; opll->rhythm_mode = 0; opll->slot_key_status = 0; opll->eg_counter = 0; reset_rate_conversion_params(opll); for (i = 0; i < 18; i++) reset_slot(&opll->slot[i], i); for (i = 0; i < 9; i++) { set_patch(opll, i, 0); } for (i = 0; i < 0x40; i++) OPLL_writeReg(opll, i, 0); for (i = 0; i < 15; i++) { opll->pan[i] = 3; centre_panning(opll->pan_fine[i]); } for (i = 0; i < 14; i++) { opll->ch_out[i] = 0; } } void OPLL_forceRefresh(OPLL *opll) { int i; if (opll == NULL) return; for (i = 0; i < 9; i++) { set_patch(opll, i, opll->patch_number[i]); } for (i = 0; i < 18; i++) { request_update(&opll->slot[i], UPDATE_ALL); } } void OPLL_setRate(OPLL *opll, uint32_t rate) { opll->rate = rate; reset_rate_conversion_params(opll); } void OPLL_setQuality(OPLL *opll, uint8_t q) {} void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { int ch, i; if (reg >= 0x40) return; /* mirror registers */ if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { reg -= 9; } opll->reg[reg] = (uint8_t)data; switch (reg) { case 0x00: opll->patch[0].AM = (data >> 7) & 1; opll->patch[0].PM = (data >> 6) & 1; opll->patch[0].EG = (data >> 5) & 1; opll->patch[0].KR = (data >> 4) & 1; opll->patch[0].ML = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); } } break; case 0x01: opll->patch[1].AM = (data >> 7) & 1; opll->patch[1].PM = (data >> 6) & 1; opll->patch[1].EG = (data >> 5) & 1; opll->patch[1].KR = (data >> 4) & 1; opll->patch[1].ML = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); } } break; case 0x02: opll->patch[0].KL = (data >> 6) & 3; opll->patch[0].TL = (data)&63; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(MOD(opll, i), UPDATE_TLL); } } break; case 0x03: opll->patch[1].KL = (data >> 6) & 3; opll->patch[1].WS = (data >> 4) & 1; opll->patch[0].WS = (data >> 3) & 1; opll->patch[0].FB = (data)&7; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(MOD(opll, i), UPDATE_WS); request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); } } break; case 0x04: opll->patch[0].AR = (data >> 4) & 15; opll->patch[0].DR = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(MOD(opll, i), UPDATE_EG); } } break; case 0x05: opll->patch[1].AR = (data >> 4) & 15; opll->patch[1].DR = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(CAR(opll, i), UPDATE_EG); } } break; case 0x06: opll->patch[0].SL = (data >> 4) & 15; opll->patch[0].RR = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(MOD(opll, i), UPDATE_EG); } } break; case 0x07: opll->patch[1].SL = (data >> 4) & 15; opll->patch[1].RR = (data)&15; for (i = 0; i < 9; i++) { if (opll->patch_number[i] == 0) { request_update(CAR(opll, i), UPDATE_EG); } } break; case 0x0e: if (opll->chip_type == 1) break; update_rhythm_mode(opll); update_key_status(opll); break; case 0x0f: opll->test_flag = data; break; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: ch = reg - 0x10; set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); break; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: ch = reg - 0x20; set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); set_block(opll, ch, (data >> 1) & 7); set_sus_flag(opll, ch, (data >> 5) & 1); update_key_status(opll); break; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { switch (reg) { case 0x37: set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); break; case 0x38: set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); break; default: break; } } else { set_patch(opll, reg - 0x30, (data >> 4) & 15); } set_volume(opll, reg - 0x30, (data & 15) << 2); break; default: break; } } void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { if (adr & 1) OPLL_writeReg(opll, opll->adr, val); else opll->adr = val; } void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } void OPLL_setPanEx(OPLL *opll, uint32_t ch, int16_t pan) { calc_panning(opll->pan_fine[ch & 15], pan); // Maxim/Valley Bell } void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { opll->pan_fine[ch & 15][0] = pan[0]; opll->pan_fine[ch & 15][1] = pan[1]; } void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { patch[0].AM = (dump[0] >> 7) & 1; patch[1].AM = (dump[1] >> 7) & 1; patch[0].PM = (dump[0] >> 6) & 1; patch[1].PM = (dump[1] >> 6) & 1; patch[0].EG = (dump[0] >> 5) & 1; patch[1].EG = (dump[1] >> 5) & 1; patch[0].KR = (dump[0] >> 4) & 1; patch[1].KR = (dump[1] >> 4) & 1; patch[0].ML = (dump[0]) & 15; patch[1].ML = (dump[1]) & 15; patch[0].KL = (dump[2] >> 6) & 3; patch[1].KL = (dump[3] >> 6) & 3; patch[0].TL = (dump[2]) & 63; patch[1].TL = 0; patch[0].FB = (dump[3]) & 7; patch[1].FB = 0; patch[0].WS = (dump[3] >> 3) & 1; patch[1].WS = (dump[3] >> 4) & 1; patch[0].AR = (dump[4] >> 4) & 15; patch[1].AR = (dump[5] >> 4) & 15; patch[0].DR = (dump[4]) & 15; patch[1].DR = (dump[5]) & 15; patch[0].SL = (dump[6] >> 4) & 15; patch[1].SL = (dump[7] >> 4) & 15; patch[0].RR = (dump[6]) & 15; patch[1].RR = (dump[7]) & 15; } void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { OPLL_dump2patch(default_inst[type] + num * 8, patch); } void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { OPLL_PATCH patch[2]; int i; for (i = 0; i < 19; i++) { OPLL_dump2patch(dump + i * 8, patch); memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); } } void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); } void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); } void OPLL_resetPatch(OPLL *opll, uint8_t type) { int i; for (i = 0; i < 19 * 2; i++) OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); } int16_t OPLL_calc(OPLL *opll) { while (opll->out_step > opll->out_time) { opll->out_time += opll->inp_step; update_output(opll); mix_output(opll); } opll->out_time -= opll->out_step; if (opll->conv) { opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); } return opll->mix_out[0]; } void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { while (opll->out_step > opll->out_time) { opll->out_time += opll->inp_step; update_output(opll); mix_output_stereo(opll); } opll->out_time -= opll->out_step; if (opll->conv) { out[0] = OPLL_RateConv_getData(opll->conv, 0); out[1] = OPLL_RateConv_getData(opll->conv, 1); } else { out[0] = opll->mix_out[0]; out[1] = opll->mix_out[1]; } } uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { uint32_t ret; if (opll) { ret = opll->mask; opll->mask = mask; return ret; } else return 0; } uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { uint32_t ret; if (opll) { ret = opll->mask; opll->mask ^= mask; return ret; } else return 0; } ================================================ FILE: VGMPlay/chips/emu2413.h ================================================ #ifndef _EMU2413_H_ #define _EMU2413_H_ #include "emutypes.h" #ifdef __cplusplus extern "C" { #endif #define OPLL_DEBUG 0 enum OPLL_TONE_ENUM { OPLL_2413_TONE = 0, OPLL_VRC7_TONE = 1, OPLL_281B_TONE = 2 }; /* voice data */ typedef struct __OPLL_PATCH { uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; } OPLL_PATCH; /* slot */ typedef struct __OPLL_SLOT { uint8_t number; /* type flags: * 000000SM * |+-- M: 0:modulator 1:carrier * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) */ uint8_t type; OPLL_PATCH *patch; /* voice parameter */ /* slot output */ int32_t output[2]; /* output value, latest and previous. */ /* phase generator (pg) */ uint16_t *wave_table; /* wave table */ uint32_t pg_phase; /* pg phase */ uint32_t pg_out; /* pg output, as index of wave table */ uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ uint16_t blk_fnum; /* (block << 9) | f-number */ uint16_t fnum; /* f-number (9 bits) */ uint8_t blk; /* block (3 bits) */ /* envelope generator (eg) */ uint8_t eg_state; /* current state */ int32_t volume; /* current volume */ uint8_t key_flag; /* key-on flag 1:on 0:off */ uint8_t sus_flag; /* key-sus option 1:on 0:off */ uint16_t tll; /* total level + key scale level*/ uint8_t rks; /* key scale offset (rks) for eg speed */ uint8_t eg_rate_h; /* eg speed rate high 4bits */ uint8_t eg_rate_l; /* eg speed rate low 2bits */ uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ uint32_t eg_out; /* eg output */ uint32_t update_requests; /* flags to debounce update */ #if OPLL_DEBUG uint8_t last_eg_state; #endif } OPLL_SLOT; /* mask */ #define OPLL_MASK_CH(x) (1 << (x)) #define OPLL_MASK_HH (1 << (9)) #define OPLL_MASK_CYM (1 << (10)) #define OPLL_MASK_TOM (1 << (11)) #define OPLL_MASK_SD (1 << (12)) #define OPLL_MASK_BD (1 << (13)) #define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) /* rate conveter */ typedef struct __OPLL_RateConv { int ch; double timer; double f_ratio; int16_t *sinc_table; int16_t **buf; } OPLL_RateConv; OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); void OPLL_RateConv_reset(OPLL_RateConv *conv); void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); void OPLL_RateConv_delete(OPLL_RateConv *conv); typedef struct __OPLL { uint32_t clk; uint32_t rate; uint8_t chip_type; uint32_t adr; uint32_t inp_step; uint32_t out_step; uint32_t out_time; uint8_t reg[0x40]; uint8_t test_flag; uint32_t slot_key_status; uint8_t rhythm_mode; uint32_t eg_counter; uint32_t pm_phase; int32_t am_phase; uint8_t lfo_am; uint32_t noise; uint8_t short_noise; int32_t patch_number[9]; OPLL_SLOT slot[18]; OPLL_PATCH patch[19 * 2]; uint8_t pan[16]; float pan_fine[16][2]; uint32_t mask; /* channel output */ /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ int16_t ch_out[14]; int16_t mix_out[2]; OPLL_RateConv *conv; } OPLL; OPLL *OPLL_new(uint32_t clk, uint32_t rate); void OPLL_delete(OPLL *); void OPLL_reset(OPLL *); void OPLL_resetPatch(OPLL *, uint8_t); /** * Set output wave sampling rate. * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is * disabled. */ void OPLL_setRate(OPLL *opll, uint32_t rate); /** * Set internal calcuration quality. Currently no effects, just for compatibility. * >= v1.0.0 always synthesizes internal output at clock/72 Hz. */ void OPLL_setQuality(OPLL *opll, uint8_t q); /** * Set pan pot (extra function - not YM2413 chip feature) * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved * @param pan 0:mute 1:right 2:left 3:center * ``` * pan: 76543210 * |+- bit 1: enable Left output * +-- bit 0: enable Right output * ``` */ void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); /** * Set fine-grained panning * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved * @param pan output strength of left/right channel. * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. */ void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); /** * Set chip type. If vrc7 is selected, r#14 is ignored. * This method not change the current ROM patch set. * To change ROM patch set, use OPLL_resetPatch. * @param type 0:YM2413 1:VRC7 */ void OPLL_setChipType(OPLL *opll, uint8_t type); void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); /** * Calculate one sample */ int16_t OPLL_calc(OPLL *opll); /** * Calulate stereo sample */ void OPLL_calcStereo(OPLL *opll, int32_t out[2]); void OPLL_setPatch(OPLL *, const uint8_t *dump); void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); /** * Force to refresh. * External program should call this function after updating patch parameters. */ void OPLL_forceRefresh(OPLL *); void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); /** * Set channel mask * @param mask mask flag: OPLL_MASK_* can be used. * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) * - bit 11: mask for Tom (OPLL_MASK_TOM) * - bit 12: mask for Snare Drum (OPLL_MASK_SD) * - bit 13: mask for Bass Drum (OPLL_MASK_BD) */ uint32_t OPLL_setMask(OPLL *, uint32_t mask); /** * Toggler channel mask flag */ uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); /* for compatibility */ #define OPLL_set_rate OPLL_setRate #define OPLL_set_quality OPLL_setQuality #define OPLL_set_pan OPLL_setPan #define OPLL_set_pan_fine OPLL_setPanFine #define OPLL_calc_stereo OPLL_calcStereo #define OPLL_reset_patch OPLL_resetPatch #define OPLL_dump2patch OPLL_dumpToPatch #define OPLL_patch2dump OPLL_patchToDump #define OPLL_setChipMode OPLL_setChipType /* for vgmplay */ void OPLL_setPanEx(OPLL *opll, uint32_t ch, int16_t pan); #ifdef __cplusplus } #endif #endif ================================================ FILE: VGMPlay/chips/emu2413_NESpatches.txt ================================================ http://forums.nesdev.com/viewtopic.php?f=6&t=9141 ================================================ FILE: VGMPlay/chips/emutypes.h ================================================ #ifndef _EMUTYPES_H_ #define _EMUTYPES_H_ #ifndef INLINE #if defined(_MSC_VER) //#define INLINE __forceinline #define INLINE __inline #elif defined(__GNUC__) #define INLINE __inline__ #elif defined(_MWERKS_) #define INLINE inline #else #define INLINE #endif #endif typedef unsigned int e_uint; typedef signed int e_int; typedef unsigned char e_uint8 ; typedef signed char e_int8 ; typedef unsigned short e_uint16 ; typedef signed short e_int16 ; typedef unsigned int e_uint32 ; typedef signed int e_int32 ; #if !defined(__int8_t_defined) && !defined(_STDINT) #define __int8_t_defined // for GCC #define _STDINT // for MSVC typedef e_uint8 uint8_t; typedef e_int8 int8_t; typedef e_uint16 uint16_t; typedef e_int16 int16_t; typedef e_uint32 uint32_t; typedef e_int32 int32_t; #ifdef _MSC_VER typedef unsigned __int64 uint64_t; typedef signed __int64 int64_t; #else #include #endif #endif #endif ================================================ FILE: VGMPlay/chips/es5503.c ================================================ /* ES5503 - Ensoniq ES5503 "DOC" emulator v2.1.1 By R. Belmont. Copyright R. Belmont. This software is dual-licensed: it may be used in MAME and properly licensed MAME derivatives under the terms of the MAME license. For use outside of MAME and properly licensed derivatives, it is available under the terms of the GNU Lesser General Public License (LGPL), version 2.1. You may read the LGPL at http://www.gnu.org/licenses/lgpl.html History: the ES5503 was the next design after the famous C64 "SID" by Bob Yannes. It powered the legendary Mirage sampler (the first affordable pro sampler) as well as the ESQ-1 synth/sequencer. The ES5505 (used in Taito's F3 System) and 5506 (used in the "Soundscape" series of ISA PC sound cards) followed on a fundamentally similar architecture. Bugs: On the real silicon, oscillators 30 and 31 have random volume fluctuations and are unusable for playback. We don't attempt to emulate that. :-) Additionally, in "swap" mode, there's one cycle when the switch takes place where the oscillator's output is 0x80 (centerline) regardless of the sample data. This can cause audible clicks and a general degradation of audio quality if the correct sample data at that point isn't 0x80 or very near it. Changes: 0.2 (RB) - improved behavior for volumes > 127, fixes missing notes in Nucleus & missing voices in Thexder 0.3 (RB) - fixed extraneous clicking, improved timing behavior for e.g. Music Construction Set & Music Studio 0.4 (RB) - major fixes to IRQ semantics and end-of-sample handling. 0.5 (RB) - more flexible wave memory hookup (incl. banking) and save state support. 1.0 (RB) - properly respects the input clock 2.0 (RB) - C++ conversion, more accurate oscillator IRQ timing 2.1 (RB) - Corrected phase when looping; synthLAB, Arkanoid, and Arkanoid II no longer go out of tune 2.1.1 (RB) - Fixed issue introduced in 2.0 where IRQs were delayed */ //#include "emu.h" //#include "streams.h" #include #include #include "mamedef.h" #include "es5503.h" typedef struct { UINT16 freq; UINT16 wtsize; UINT8 control; UINT8 vol; UINT8 data; UINT32 wavetblpointer; UINT8 wavetblsize; UINT8 resolution; UINT32 accumulator; UINT8 irqpend; UINT8 Muted; } ES5503Osc; typedef struct { ES5503Osc oscillators[32]; UINT32 dramsize; UINT8 *docram; //sound_stream * stream; //void (*irq_callback)(running_device *, int); // IRQ callback //read8_device_func adc_read; // callback for the 5503's built-in analog to digital converter INT8 oscsenabled; // # of oscillators enabled int rege0; // contents of register 0xe0 UINT8 channel_strobe; UINT32 clock; int output_channels; int outchn_mask; UINT32 output_rate; //running_device *device; SRATE_CALLBACK SmpRateFunc; void* SmpRateData; } ES5503Chip; #define MAX_CHIPS 0x02 static ES5503Chip ES5503Data[MAX_CHIPS]; /*INLINE ES5503Chip *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == ES5503); return (ES5503Chip *)downcast(device)->token(); }*/ static const UINT16 wavesizes[8] = { 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; static const UINT32 wavemasks[8] = { 0x1ff00, 0x1fe00, 0x1fc00, 0x1f800, 0x1f000, 0x1e000, 0x1c000, 0x18000 }; static const UINT32 accmasks[8] = { 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff }; static const int resshifts[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; enum { MODE_FREE = 0, MODE_ONESHOT = 1, MODE_SYNCAM = 2, MODE_SWAP = 3 }; // halt_osc: handle halting an oscillator // chip = chip ptr // onum = oscillator # // type = 1 for 0 found in sample data, 0 for hit end of table size static void es5503_halt_osc(ES5503Chip *chip, int onum, int type, UINT32 *accumulator, int resshift) { ES5503Osc *pOsc = &chip->oscillators[onum]; ES5503Osc *pPartner = &chip->oscillators[onum^1]; int mode = (pOsc->control>>1) & 3; int omode = (pPartner->control>>1) & 3; // if 0 found in sample data or mode is not free-run, halt this oscillator if ((mode != MODE_FREE) || (type != 0)) { pOsc->control |= 1; } else // preserve the relative phase of the oscillator when looping { UINT16 wtsize = pOsc->wtsize - 1; UINT32 altram = (*accumulator) >> resshift; if (altram > wtsize) { altram -= wtsize; } else { altram = 0; } *accumulator = altram << resshift; } // if swap mode, start the partner // Note: The swap mode fix breaks Silpheed and other games. if ((mode == MODE_SWAP) /*|| (omode == MODE_SWAP)*/) { pPartner->control &= ~1; // clear the halt bit pPartner->accumulator = 0; // and make sure it starts from the top (does this also need phase preservation?) } // IRQ enabled for this voice? if (pOsc->control & 0x08) { pOsc->irqpend = 1; /*if (chip->irq_callback) { chip->irq_callback(chip->device, 1); }*/ } } //static STREAM_UPDATE( es5503_pcm_update ) void es5503_pcm_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { // Note: The advantage of NOT using this buffer is not only less RAM usage, // but also a huge speedup. This is, because the array is not marked // as 'static' and thus re-allocated for every single call. //INT32 mix[48000*2]; //INT32 *mixp; int osc, snum; UINT32 ramptr; //ES5503Chip *chip = (ES5503Chip *)param; ES5503Chip *chip = &ES5503Data[ChipID]; int chnsStereo, chan; memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); //memset(mix, 0, sizeof(mix)); chnsStereo = chip->output_channels & ~1; for (osc = 0; osc < chip->oscsenabled; osc++) { ES5503Osc *pOsc = &chip->oscillators[osc]; if (!(pOsc->control & 1) && ! pOsc->Muted) { UINT32 wtptr = pOsc->wavetblpointer & wavemasks[pOsc->wavetblsize], altram; UINT32 acc = pOsc->accumulator; UINT16 wtsize = pOsc->wtsize - 1; //UINT8 ctrl = pOsc->control; UINT16 freq = pOsc->freq; INT16 vol = pOsc->vol; //INT8 data = -128; UINT8 chnMask = (pOsc->control >> 4) & 0x0F; int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize; UINT32 sizemask = accmasks[pOsc->wavetblsize]; INT32 outData; //mixp = &mix[0] + chan; chnMask &= chip->outchn_mask; for (snum = 0; snum < samples; snum++) { altram = acc >> resshift; ramptr = altram & sizemask; acc += freq; // channel strobe is always valid when reading; this allows potentially banking per voice //chip->channel_strobe = (pOsc->control>>4) & 0xf; //data = (INT32)chip->docram[ramptr + wtptr] ^ 0x80; pOsc->data = chip->docram[ramptr + wtptr]; if (pOsc->data == 0x00) { es5503_halt_osc(chip, osc, 1, &acc, resshift); } else { outData = (pOsc->data - 0x80) * vol; //*mixp += outData; //mixp += output_channels; // send groups of 2 channels to L or R for (chan = 0; chan < chnsStereo; chan ++) { if (chan == chnMask) outputs[chan & 1][snum] += outData; } outData = (outData * 181) >> 8; // outData *= sqrt(2) // send remaining channels to L+R for (; chan < chip->output_channels; chan ++) { if (chan == chnMask) { outputs[0][snum] += outData; outputs[1][snum] += outData; } } if (altram >= wtsize) { es5503_halt_osc(chip, osc, 0, &acc, resshift); } } // if oscillator halted, we've got no more samples to generate if (pOsc->control & 1) { //pOsc->control |= 1; break; } } //pOsc->control = ctrl; pOsc->accumulator = acc; //pOsc->data = data ^ 0x80; } } /* mixp = &mix[0]; for (i = 0; i < samples; i++) for (int chan = 0; chan < output_channels; chan++) outputs[chan][i] = (*mixp++)>>1;*/ } //static DEVICE_START( es5503 ) int device_start_es5503(UINT8 ChipID, int clock, int channels) { //const es5503_interface *intf; int osc; //ES5503Chip *chip = get_safe_token(device); ES5503Chip *chip; if (ChipID >= MAX_CHIPS) return 0; chip = &ES5503Data[ChipID]; //intf = (const es5503_interface *)device->baseconfig().static_config(); //chip->irq_callback = intf->irq_callback; //chip->adc_read = intf->adc_read; //chip->docram = intf->wave_memory; chip->dramsize = 0x20000; // 128 KB chip->docram = (UINT8*)malloc(chip->dramsize); //chip->clock = device->clock(); //chip->device = device; chip->clock = clock; chip->output_channels = channels; chip->outchn_mask = 1; while(chip->outchn_mask < chip->output_channels) chip->outchn_mask <<= 1; chip->outchn_mask --; chip->rege0 = 0xff; /*for (osc = 0; osc < 32; osc++) { state_save_register_device_item(device, osc, chip->oscillators[osc].freq); state_save_register_device_item(device, osc, chip->oscillators[osc].wtsize); state_save_register_device_item(device, osc, chip->oscillators[osc].control); state_save_register_device_item(device, osc, chip->oscillators[osc].vol); state_save_register_device_item(device, osc, chip->oscillators[osc].data); state_save_register_device_item(device, osc, chip->oscillators[osc].wavetblpointer); state_save_register_device_item(device, osc, chip->oscillators[osc].wavetblsize); state_save_register_device_item(device, osc, chip->oscillators[osc].resolution); state_save_register_device_item(device, osc, chip->oscillators[osc].accumulator); state_save_register_device_item(device, osc, chip->oscillators[osc].irqpend); }*/ //chip->output_rate = (device->clock()/8)/34; // (input clock / 8) / # of oscs. enabled + 2 //chip->stream = stream_create(device, 0, 2, chip->output_rate, chip, es5503_pcm_update); chip->output_rate = (chip->clock/8)/34; // (input clock / 8) / # of oscs. enabled + 2 for (osc = 0; osc < 32; osc ++) chip->oscillators[osc].Muted = 0x00; return chip->output_rate; } void device_stop_es5503(UINT8 ChipID) { ES5503Chip *chip = &ES5503Data[ChipID]; free(chip->docram); chip->docram = NULL; return; } void device_reset_es5503(UINT8 ChipID) { ES5503Chip *chip = &ES5503Data[ChipID]; int osc; ES5503Osc* tempOsc; for (osc = 0; osc < 32; osc++) { tempOsc = &chip->oscillators[osc]; tempOsc->freq = 0; tempOsc->wtsize = 0; tempOsc->control = 0; tempOsc->vol = 0; tempOsc->data = 0x80; tempOsc->wavetblpointer = 0; tempOsc->wavetblsize = 0; tempOsc->resolution = 0; tempOsc->accumulator = 0; tempOsc->irqpend = 0; } chip->oscsenabled = 1; chip->channel_strobe = 0; memset(chip->docram, 0x00, chip->dramsize); chip->output_rate = (chip->clock/8)/(2+chip->oscsenabled); // (input clock / 8) / # of oscs. enabled + 2 if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->output_rate); return; } //READ8_DEVICE_HANDLER( es5503_r ) UINT8 es5503_r(UINT8 ChipID, offs_t offset) { UINT8 retval; int i; //ES5503Chip *chip = get_safe_token(device); ES5503Chip *chip = &ES5503Data[ChipID]; //stream_update(chip->stream); if (offset < 0xe0) { int osc = offset & 0x1f; switch(offset & 0xe0) { case 0: // freq lo return (chip->oscillators[osc].freq & 0xff); case 0x20: // freq hi return (chip->oscillators[osc].freq >> 8); case 0x40: // volume return chip->oscillators[osc].vol; case 0x60: // data return chip->oscillators[osc].data; case 0x80: // wavetable pointer return (chip->oscillators[osc].wavetblpointer>>8) & 0xff; case 0xa0: // oscillator control return chip->oscillators[osc].control; case 0xc0: // bank select / wavetable size / resolution retval = 0; if (chip->oscillators[osc].wavetblpointer & 0x10000) { retval |= 0x40; } retval |= (chip->oscillators[osc].wavetblsize<<3); retval |= chip->oscillators[osc].resolution; return retval; } } else // global registers { switch (offset) { case 0xe0: // interrupt status retval = chip->rege0; //m_irq_func(0); // scan all oscillators for (i = 0; i < chip->oscsenabled; i++) { if (chip->oscillators[i].irqpend) { // signal this oscillator has an interrupt retval = i<<1; chip->rege0 = retval | 0x80; // and clear its flag chip->oscillators[i].irqpend = 0; /*if (chip->irq_callback) { chip->irq_callback(chip->device, 0); }*/ break; } } // if any oscillators still need to be serviced, assert IRQ again immediately /*for (i = 0; i < chip->oscsenabled; i++) { if (chip->oscillators[i].irqpend) { if (chip->irq_callback) { chip->irq_callback(chip->device, 1); } break; } }*/ return retval; case 0xe1: // oscillator enable return (chip->oscsenabled-1)<<1; case 0xe2: // A/D converter /*if (chip->adc_read) { return chip->adc_read(chip->device, 0); }*/ break; } } return 0; } //WRITE8_DEVICE_HANDLER( es5503_w ) void es5503_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ES5503Chip *chip = get_safe_token(device); ES5503Chip *chip = &ES5503Data[ChipID]; //stream_update(chip->stream); if (offset < 0xe0) { int osc = offset & 0x1f; switch(offset & 0xe0) { case 0: // freq lo chip->oscillators[osc].freq &= 0xff00; chip->oscillators[osc].freq |= data; break; case 0x20: // freq hi chip->oscillators[osc].freq &= 0x00ff; chip->oscillators[osc].freq |= (data<<8); break; case 0x40: // volume chip->oscillators[osc].vol = data; break; case 0x60: // data - ignore writes break; case 0x80: // wavetable pointer chip->oscillators[osc].wavetblpointer = (data<<8); break; case 0xa0: // oscillator control // if a fresh key-on, reset the ccumulator if ((chip->oscillators[osc].control & 1) && (!(data&1))) { chip->oscillators[osc].accumulator = 0; } chip->oscillators[osc].control = data; break; case 0xc0: // bank select / wavetable size / resolution if (data & 0x40) // bank select - not used on the Apple IIgs { chip->oscillators[osc].wavetblpointer |= 0x10000; } else { chip->oscillators[osc].wavetblpointer &= 0xffff; } chip->oscillators[osc].wavetblsize = ((data>>3) & 7); chip->oscillators[osc].wtsize = wavesizes[chip->oscillators[osc].wavetblsize]; chip->oscillators[osc].resolution = (data & 7); break; } } else // global registers { switch (offset) { case 0xe0: // interrupt status break; case 0xe1: // oscillator enable chip->oscsenabled = 1 + ((data>>1) & 0x1f); chip->output_rate = (chip->clock/8)/(2+chip->oscsenabled); //stream_set_sample_rate(chip->stream, chip->output_rate); if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->output_rate); break; case 0xe2: // A/D converter break; } } } /*void es5503_set_base(running_device *device, UINT8 *wavemem) { ES5503Chip *chip = get_safe_token(device); chip->docram = wavemem; }*/ void es5503_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { ES5503Chip *chip = &ES5503Data[ChipID]; if (DataStart >= chip->dramsize) return; if (DataStart + DataLength > chip->dramsize) DataLength = chip->dramsize - DataStart; memcpy(chip->docram + DataStart, RAMData, DataLength); return; } void es5503_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ES5503Chip *chip = &ES5503Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 32; CurChn ++) chip->oscillators[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } void es5503_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr) { ES5503Chip *chip = &ES5503Data[ChipID]; // set Sample Rate Change Callback routine chip->SmpRateFunc = CallbackFunc; chip->SmpRateData = DataPtr; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( es5503 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ES5503Chip); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5503 ); break; case DEVINFO_FCT_STOP: // Nothing // break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "ES5503"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq ES550x"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright R. Belmont"); break; } } DEFINE_LEGACY_SOUND_DEVICE(ES5503, es5503);*/ ================================================ FILE: VGMPlay/chips/es5503.h ================================================ #pragma once #ifndef __ES5503_H__ #define __ES5503_H__ ///#include "devlegcy.h" /*typedef struct _es5503_interface es5503_interface; struct _es5503_interface { void (*irq_callback)(running_device *device, int state); read8_device_func adc_read; UINT8 *wave_memory; };*/ //READ8_DEVICE_HANDLER( es5503_r ); //WRITE8_DEVICE_HANDLER( es5503_w ); void es5503_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 es5503_r(UINT8 ChipID, offs_t offset); void es5503_pcm_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_es5503(UINT8 ChipID, int clock, int channels); void device_stop_es5503(UINT8 ChipID); void device_reset_es5503(UINT8 ChipID); //void es5503_set_base(running_device *device, UINT8 *wavemem); void es5503_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void es5503_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void es5503_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr); //DECLARE_LEGACY_SOUND_DEVICE(ES5503, es5503); #endif /* __ES5503_H__ */ ================================================ FILE: VGMPlay/chips/es5506.c ================================================ /********************************************************************************************** Ensoniq ES5505/6 driver by Aaron Giles Ensoniq OTIS - ES5505 Ensoniq OTTO - ES5506 OTIS is a VLSI device designed in a 2 micron double metal OTTO is a VLSI device designed in a 1.5 micron double metal CMOS process. The device is the next generation of audio CMOS process. The device is the next generation of audio technology from ENSONIQ. This new chip achieves a new technology from ENSONIQ. All calculations in the device are level of audio fidelity performance. These improvements made with at least 18-bit accuracy. are achieved through the use of frequency interpolation and on board real time digital filters. All calculations The major features of OTTO are: in the device are made with at least 16 bit accuracy. - 68 pin PLCC package - On chip real time digital filters The major features of OTIS are: - Frequency interpolation - 48 Pin dual in line package - 32 independent voices - On chip real time digital filters - Loop start and stop posistions for each voice - Frequency interpolation - Bidirectional and reverse looping - 32 independent voices (up from 25 in DOCII) - 68000 compatibility for asynchronous bus communication - Loop start and stop positions for each voice - separate host and sound memory interface - Bidirectional and reverse looping - 6 channel stereo serial communication port - 68000 compatibility for asynchronous bus communication - Programmable clocks for defining serial protocol - On board pulse width modulation D to A - Internal volume multiplication and stereo panning - 4 channel stereo serial communication port - A to D input for pots and wheels - Internal volume multiplication and stereo panning - Hardware support for envelopes - A to D input for pots and wheels - Support for dual OTTO systems - Up to 10MHz operation - Optional compressed data format for sample data - Up to 16MHz operation ______ ______ _|o \__/ |_ A17/D13 - |_|1 48|_| - VSS A A A A A A _| |_ 2 1 1 1 1 1 A A18/D14 - |_|2 47|_| - A16/D12 0 9 8 7 6 5 1 _| |_ / / / / / / 4 A19/D15 - |_|3 46|_| - A15/D11 H H H H H H H V V H D D D D D D / _| |_ D D D D D D D S D D 1 1 1 1 1 1 D BS - |_|4 45|_| - A14/D10 0 1 2 3 4 5 6 S D 7 5 4 3 2 1 0 9 _| |_ ------------------------------------+ PWZERO - |_|5 44|_| - A13/D9 / 9 8 7 6 5 4 3 2 1 6 6 6 6 6 6 6 6 | _| |_ / 8 7 6 5 4 3 2 1 | SER0 - |_|6 43|_| - A12/D8 | | _| E |_ SER0|10 60|A13/D8 SER1 - |_|7 N 42|_| - A11/D7 SER1|11 59|A12/D7 _| S |_ SER2|12 58|A11/D6 SER2 - |_|8 O 41|_| - A10/D6 SER3|13 ENSONIQ 57|A10/D5 _| N |_ SER4|14 56|A9/D4 SER3 - |_|9 I 40|_| - A9/D5 SER5|15 55|A8/D3 _| Q |_ WCLK|16 54|A7/D2 SERWCLK - |_|10 39|_| - A8/D4 LRCLK|17 ES5506 53|A6/D1 _| |_ BCLK|18 52|A5/D0 SERLR - |_|11 38|_| - A7/D3 RESB|19 51|A4 _| |_ HA5|20 50|A3 SERBCLK - |_|12 E 37|_| - A6/D2 HA4|21 OTTO 49|A2 _| S |_ HA3|22 48|A1 RLO - |_|13 5 36|_| - A5/D1 HA2|23 47|A0 _| 5 |_ HA1|24 46|BS1 RHI - |_|14 0 35|_| - A4/D0 HA0|25 45|BS0 _| 5 |_ POT_IN|26 44|DTACKB LLO - |_|15 34|_| - CLKIN | 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 | _| |_ | 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 | LHI - |_|16 33|_| - CAS +--------------------------------------+ _| |_ B E E B E B B D S B B B E K B W W POT - |_|17 O 32|_| - AMUX S B L N L S S D S S X S L Q / / _| T |_ E E R E H M C V V A U A C R R R DTACK - |_|18 I 31|_| - RAS R R D H R M C I M _| S |_ _ D A R/W - |_|19 30|_| - E T _| |_ O MS - |_|20 29|_| - IRQ P _| |_ CS - |_|21 28|_| - A3 _| |_ RES - |_|22 27|_| - A2 _| |_ VSS - |_|23 26|_| - A1 _| |_ VDD - |_|24 25|_| - A0 |________________| ***********************************************************************************************/ //#include "emu.h" #include #include // for memset #include "mamedef.h" #include "es5506.h" /********************************************************************************************** CONSTANTS ***********************************************************************************************/ #define LOG_COMMANDS 0 #define RAINE_CHECK 0 #define MAKE_WAVS 0 #if MAKE_WAVS #include "wavwrite.h" #endif #define ACCESSING_BITS_0_7 ( byteOfs) #define ACCESSING_BITS_8_15 (! byteOfs) #define MAX_SAMPLE_CHUNK 10000 #define ULAW_MAXBITS 8 #define CONTROL_BS1 0x8000 #define CONTROL_BS0 0x4000 #define CONTROL_CMPD 0x2000 #define CONTROL_CA2 0x1000 #define CONTROL_CA1 0x0800 #define CONTROL_CA0 0x0400 #define CONTROL_LP4 0x0200 #define CONTROL_LP3 0x0100 #define CONTROL_IRQ 0x0080 #define CONTROL_DIR 0x0040 #define CONTROL_IRQE 0x0020 #define CONTROL_BLE 0x0010 #define CONTROL_LPE 0x0008 #define CONTROL_LEI 0x0004 #define CONTROL_STOP1 0x0002 #define CONTROL_STOP0 0x0001 #define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0) #define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0) #define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3) #define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE) #define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0) /********************************************************************************************** INTERNAL DATA STRUCTURES ***********************************************************************************************/ /* struct describing a single playing voice */ typedef struct _es5506_voice es5506_voice; struct _es5506_voice { /* external state */ UINT32 control; /* control register */ UINT32 freqcount; /* frequency count register */ UINT32 start; /* start register */ UINT32 lvol; /* left volume register */ UINT32 end; /* end register */ UINT32 lvramp; /* left volume ramp register */ UINT32 accum; /* accumulator register */ UINT32 rvol; /* right volume register */ UINT32 rvramp; /* right volume ramp register */ UINT32 ecount; /* envelope count register */ UINT32 k2; /* k2 register */ UINT32 k2ramp; /* k2 ramp register */ UINT32 k1; /* k1 register */ UINT32 k1ramp; /* k1 ramp register */ INT32 o4n1; /* filter storage O4(n-1) */ INT32 o3n1; /* filter storage O3(n-1) */ INT32 o3n2; /* filter storage O3(n-2) */ INT32 o2n1; /* filter storage O2(n-1) */ INT32 o2n2; /* filter storage O2(n-2) */ INT32 o1n1; /* filter storage O1(n-1) */ UINT32 exbank; /* external address bank */ /* internal state */ UINT8 index; /* index of this voice */ UINT8 filtcount; /* filter count */ UINT8 Muted; UINT32 accum_mask; }; typedef struct _es5506_state es5506_state; struct _es5506_state { //sound_stream *stream; /* which stream are we using */ int sample_rate; /* current sample rate */ UINT32 region_size[4]; UINT16 * region_base[4]; /* pointer to the base of the region */ UINT32 write_latch; /* currently accumulated data for write */ UINT32 read_latch; /* currently accumulated data for read */ UINT32 master_clock; /* master clock frequency */ //void (*irq_callback)(device_t *, int); /* IRQ callback */ //UINT16 (*port_read)(void); /* input port read */ UINT8 current_page; /* current register page */ UINT8 active_voices; /* number of active voices */ UINT8 mode; /* MODE register */ UINT8 wst; /* W_ST register */ UINT8 wend; /* W_END register */ UINT8 lrend; /* LR_END register */ UINT8 irqv; /* IRQV register */ es5506_voice voice[32]; /* the 32 voices */ INT32 * scratch; INT16 * ulaw_lookup; UINT16 * volume_lookup; //device_t *device; #if MAKE_WAVS void * wavraw; /* raw waveform */ #endif int channels; /* number of output channels: 1 .. 6 */ UINT8 sndtype; /* 0 - ES5505, 1 - ES5506 */ SRATE_CALLBACK SmpRateFunc; void* SmpRateData; }; #define MAX_CHIPS 0x02 static es5506_state ES5506Data[MAX_CHIPS]; /*INLINE es5506_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == ES5505 || device->type() == ES5506); return (es5506_state *)downcast(device)->token(); }*/ /********************************************************************************************** GLOBAL VARIABLES ***********************************************************************************************/ //static FILE *eslog; /********************************************************************************************** update_irq_state -- update the IRQ state ***********************************************************************************************/ static void update_irq_state(es5506_state *chip) { /* ES5505/6 irq line has been set high - inform the host */ //if (chip->irq_callback) // (*chip->irq_callback)(chip->device, 1); /* IRQB set high */ } static void update_internal_irq_state(es5506_state *chip) { /* Host (cpu) has just read the voice interrupt vector (voice IRQ ack). Reset the voice vector to show the IRQB line is low (top bit set). If we have any stacked interrupts (other voices waiting to be processed - with their IRQ bit set) then they will be moved into the vector next time the voice is processed. In emulation terms they get updated next time generate_samples() is called. */ chip->irqv=0x80; //if (chip->irq_callback) // (*chip->irq_callback)(chip->device, 0); /* IRQB set low */ } /********************************************************************************************** compute_tables -- compute static tables ***********************************************************************************************/ static void compute_tables(es5506_state *chip) { int i; /* allocate ulaw lookup table */ //chip->ulaw_lookup = auto_alloc_array(chip->device->machine(), INT16, 1 << ULAW_MAXBITS); chip->ulaw_lookup = (INT16*)malloc((1 << ULAW_MAXBITS) * sizeof(INT16)); /* generate ulaw lookup table */ for (i = 0; i < (1 << ULAW_MAXBITS); i++) { UINT16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); UINT8 exponent = rawval >> 13; UINT32 mantissa = (rawval << 3) & 0xffff; if (exponent == 0) chip->ulaw_lookup[i] = (INT16)mantissa >> 7; else { mantissa = (mantissa >> 1) | (~mantissa & 0x8000); chip->ulaw_lookup[i] = (INT16)mantissa >> (7 - exponent); } } /* allocate volume lookup table */ //chip->volume_lookup = auto_alloc_array(chip->device->machine(), UINT16, 4096); chip->volume_lookup = (UINT16*)malloc(4096 * sizeof(UINT16)); /* generate volume lookup table */ for (i = 0; i < 4096; i++) { UINT8 exponent = i >> 8; UINT32 mantissa = (i & 0xff) | 0x100; chip->volume_lookup[i] = (mantissa << 11) >> (20 - exponent); } } /********************************************************************************************** interpolate -- interpolate between two samples ***********************************************************************************************/ #define interpolate(sample1, sample2, accum) \ (sample1 * (INT32)(0x800 - (accum & 0x7ff)) + \ sample2 * (INT32)(accum & 0x7ff)) >> 11; /********************************************************************************************** apply_filters -- apply the 4-pole digital filter to the sample ***********************************************************************************************/ #define apply_filters(voice, sample) \ do \ { \ /* pole 1 is always low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \ voice->o1n1 = sample; \ \ /* pole 2 is always low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \ voice->o2n2 = voice->o2n1; \ voice->o2n1 = sample; \ \ /* remaining poles depend on the current filter setting */ \ switch (voice->control & CONTROL_LPMASK) \ { \ case 0: \ /* pole 3 is high-pass using K2 */ \ sample = sample - voice->o2n2 + ((INT32)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is high-pass using K2 */ \ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP3: \ /* pole 3 is low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is high-pass using K2 */ \ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP4: \ /* pole 3 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP4 | CONTROL_LP3: \ /* pole 3 is low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ voice->o4n1 = sample; \ break; \ } \ } while (0) /********************************************************************************************** update_envelopes -- update the envelopes ***********************************************************************************************/ #define update_envelopes(voice, samples) \ do \ { \ int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \ \ /* decrement the envelope counter */ \ voice->ecount -= count; \ \ /* ramp left volume */ \ if (voice->lvramp) \ { \ voice->lvol += (INT8)voice->lvramp * count; \ if ((INT32)voice->lvol < 0) voice->lvol = 0; \ else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \ } \ \ /* ramp right volume */ \ if (voice->rvramp) \ { \ voice->rvol += (INT8)voice->rvramp * count; \ if ((INT32)voice->rvol < 0) voice->rvol = 0; \ else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \ } \ \ /* ramp k1 filter constant */ \ if (voice->k1ramp && ((INT32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \ { \ voice->k1 += (INT8)voice->k1ramp * count; \ if ((INT32)voice->k1 < 0) voice->k1 = 0; \ else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \ } \ \ /* ramp k2 filter constant */ \ if (voice->k2ramp && ((INT32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \ { \ voice->k2 += (INT8)voice->k2ramp * count; \ if ((INT32)voice->k2 < 0) voice->k2 = 0; \ else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \ } \ \ /* update the filter constant counter */ \ voice->filtcount += count; \ \ } while (0) /********************************************************************************************** check_for_end_forward check_for_end_reverse -- check for loop end and loop appropriately ***********************************************************************************************/ #define check_for_end_forward(voice, accum) \ do \ { \ /* are we past the end? */ \ if (accum > voice->end && !(voice->control & CONTROL_LEI)) \ { \ /* generate interrupt if required */ \ if (voice->control&CONTROL_IRQE) \ voice->control |= CONTROL_IRQ; \ \ /* handle the different types of looping */ \ switch (voice->control & CONTROL_LOOPMASK) \ { \ /* non-looping */ \ case 0: \ voice->control |= CONTROL_STOP0; \ goto alldone; \ \ /* uni-directional looping */ \ case CONTROL_LPE: \ accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ break; \ \ /* trans-wave looping */ \ case CONTROL_BLE: \ accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ break; \ \ /* bi-directional looping */ \ case CONTROL_LPE | CONTROL_BLE: \ accum = (voice->end - (accum - voice->end)) & voice->accum_mask; \ voice->control ^= CONTROL_DIR; \ goto reverse; \ } \ } \ } while (0) #define check_for_end_reverse(voice, accum) \ do \ { \ /* are we past the end? */ \ if (accum < voice->start && !(voice->control & CONTROL_LEI)) \ { \ /* generate interrupt if required */ \ if (voice->control&CONTROL_IRQE) \ voice->control |= CONTROL_IRQ; \ \ /* handle the different types of looping */ \ switch (voice->control & CONTROL_LOOPMASK) \ { \ /* non-looping */ \ case 0: \ voice->control |= CONTROL_STOP0; \ goto alldone; \ \ /* uni-directional looping */ \ case CONTROL_LPE: \ accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ break; \ \ /* trans-wave looping */ \ case CONTROL_BLE: \ accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ break; \ \ /* bi-directional looping */ \ case CONTROL_LPE | CONTROL_BLE: \ accum = (voice->start + (voice->start - accum)) & voice->accum_mask;\ voice->control ^= CONTROL_DIR; \ goto reverse; \ } \ } \ } while (0) /********************************************************************************************** generate_dummy -- generate nothing, just apply envelopes ***********************************************************************************************/ static void generate_dummy(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) { UINT32 freqcount = voice->freqcount; UINT32 accum = voice->accum & voice->accum_mask; /* outer loop, in case we switch directions */ while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) { reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ accum = (accum + freqcount) & voice->accum_mask; /* update filters/volumes */ if (voice->ecount != 0) update_envelopes(voice, 1); /* check for loop end */ check_for_end_forward(voice, accum); } } /* two cases: second case is backward direction */ else { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ accum = (accum - freqcount) & voice->accum_mask; /* update filters/volumes */ if (voice->ecount != 0) update_envelopes(voice, 1); /* check for loop end */ check_for_end_reverse(voice, accum); } } } /* if we stopped, process any additional envelope */ alldone: voice->accum = accum; if (samples > 0) update_envelopes(voice, samples); } /********************************************************************************************** generate_ulaw -- general u-law decoding routine ***********************************************************************************************/ static void generate_ulaw(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) { UINT32 freqcount = voice->freqcount; UINT32 accum = voice->accum & voice->accum_mask; INT32 lvol = chip->volume_lookup[voice->lvol >> 4]; INT32 rvol = chip->volume_lookup[voice->rvol >> 4]; /* pre-add the bank offset */ base += voice->exbank; /* outer loop, in case we switch directions */ while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) { reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ #ifdef VGM_BIG_ENDIAN #warning "ES5506 sound emulation not Endian-Safe!" #endif INT32 val1 = base[accum >> 11]; INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; /* decompress u-law */ val1 = chip->ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; val2 = chip->ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; /* interpolate */ val1 = interpolate(val1, val2, accum); accum = (accum + freqcount) & voice->accum_mask; /* apply filters */ apply_filters(voice, val1); /* update filters/volumes */ if (voice->ecount != 0) { update_envelopes(voice, 1); lvol = chip->volume_lookup[voice->lvol >> 4]; rvol = chip->volume_lookup[voice->rvol >> 4]; } /* apply volumes and add */ *lbuffer++ += (val1 * lvol) >> 11; *rbuffer++ += (val1 * rvol) >> 11; /* check for loop end */ check_for_end_forward(voice, accum); } } /* two cases: second case is backward direction */ else { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ #ifdef VGM_BIG_ENDIAN #warning "ES5506 sound emulation not Endian-Safe!" #endif INT32 val1 = base[accum >> 11]; INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; /* decompress u-law */ val1 = chip->ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; val2 = chip->ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; /* interpolate */ val1 = interpolate(val1, val2, accum); accum = (accum - freqcount) & voice->accum_mask; /* apply filters */ apply_filters(voice, val1); /* update filters/volumes */ if (voice->ecount != 0) { update_envelopes(voice, 1); lvol = chip->volume_lookup[voice->lvol >> 4]; rvol = chip->volume_lookup[voice->rvol >> 4]; } /* apply volumes and add */ *lbuffer++ += (val1 * lvol) >> 11; *rbuffer++ += (val1 * rvol) >> 11; /* check for loop end */ check_for_end_reverse(voice, accum); } } } /* if we stopped, process any additional envelope */ alldone: voice->accum = accum; if (samples > 0) update_envelopes(voice, samples); } /********************************************************************************************** generate_pcm -- general PCM decoding routine ***********************************************************************************************/ static void generate_pcm(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) { UINT32 freqcount = voice->freqcount; UINT32 accum = voice->accum & voice->accum_mask; INT32 lvol = chip->volume_lookup[voice->lvol >> 4]; INT32 rvol = chip->volume_lookup[voice->rvol >> 4]; /* pre-add the bank offset */ base += voice->exbank; /* outer loop, in case we switch directions */ while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) { reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ #ifdef VGM_BIG_ENDIAN #warning "ES5506 sound emulation not Endian-Safe!" #endif INT32 val1 = (INT16)base[accum >> 11]; INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; /* interpolate */ val1 = interpolate(val1, val2, accum); accum = (accum + freqcount) & voice->accum_mask; /* apply filters */ apply_filters(voice, val1); /* update filters/volumes */ if (voice->ecount != 0) { update_envelopes(voice, 1); lvol = chip->volume_lookup[voice->lvol >> 4]; rvol = chip->volume_lookup[voice->rvol >> 4]; } /* apply volumes and add */ *lbuffer++ += (val1 * lvol) >> 11; *rbuffer++ += (val1 * rvol) >> 11; /* check for loop end */ check_for_end_forward(voice, accum); } } /* two cases: second case is backward direction */ else { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ #ifdef VGM_BIG_ENDIAN #warning "ES5506 sound emulation not Endian-Safe!" #endif INT32 val1 = (INT16)base[accum >> 11]; INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; /* interpolate */ val1 = interpolate(val1, val2, accum); accum = (accum - freqcount) & voice->accum_mask; /* apply filters */ apply_filters(voice, val1); /* update filters/volumes */ if (voice->ecount != 0) { update_envelopes(voice, 1); lvol = chip->volume_lookup[voice->lvol >> 4]; rvol = chip->volume_lookup[voice->rvol >> 4]; } /* apply volumes and add */ *lbuffer++ += (val1 * lvol) >> 11; *rbuffer++ += (val1 * rvol) >> 11; /* check for loop end */ check_for_end_reverse(voice, accum); } } } /* if we stopped, process any additional envelope */ alldone: voice->accum = accum; if (samples > 0) update_envelopes(voice, samples); } /********************************************************************************************** generate_samples -- tell each voice to generate samples ***********************************************************************************************/ static void generate_samples(es5506_state *chip, INT32 **outputs, int offset, int samples) { int v; int i; int voice_channel; int channel; int l; int r; INT32 *left; INT32 *right; /* skip if nothing to do */ if (!samples) return; /* clear out the accumulator */ for (i = 0; i < chip->channels << 1; i++) { memset(outputs[i] + offset, 0, sizeof(INT32) * samples); } /* loop over voices */ for (v = 0; v <= chip->active_voices; v++) { es5506_voice *voice = &chip->voice[v]; UINT16 *base = chip->region_base[voice->control >> 14]; /* special case: if end == start, stop the voice */ if (voice->start == voice->end) voice->control |= CONTROL_STOP0; voice_channel = (voice->control & CONTROL_CAMASK) >> 10; channel = voice_channel % chip->channels; l = channel << 1; r = l + 1; left = outputs[l] + offset; right = outputs[r] + offset; /* generate from the appropriate source */ if (!base) { //logerror("es5506: NULL region base %d\n",voice->control >> 14); generate_dummy(chip, voice, base, left, right, samples); } else if (voice->control & 0x2000) generate_ulaw(chip, voice, base, left, right, samples); else generate_pcm(chip, voice, base, left, right, samples); /* does this voice have it's IRQ bit raised? */ if (voice->control&CONTROL_IRQ) { logerror("es5506: IRQ raised on voice %d!!\n",v); /* only update voice vector if existing IRQ is acked by host */ if (chip->irqv&0x80) { /* latch voice number into vector, and set high bit low */ chip->irqv=v&0x7f; /* take down IRQ bit on voice */ voice->control&=~CONTROL_IRQ; /* inform host of irq */ update_irq_state(chip); } } } } /********************************************************************************************** es5506_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( es5506_update ) void es5506_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //es5506_state *chip = (es5506_state *)param; es5506_state *chip = &ES5506Data[ChipID]; int offset; #if MAKE_WAVS /* start the logging once we have a sample rate */ if (chip->sample_rate) { if (!chip->wavraw) chip->wavraw = wav_open("raw.wav", chip->sample_rate, 2); } #endif /* loop until all samples are output */ offset = 0; while (samples) { int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; generate_samples(chip, outputs, offset, length); /* account for these samples */ offset += length; samples -= length; } } /********************************************************************************************** DEVICE_START( es5506 ) -- start emulation of the ES5506 ***********************************************************************************************/ //static void es5506_start_common(device_t *device, const void *config, device_type sndtype) static void es5506_start_common(es5506_state *chip, int clock, UINT8 sndtype) { //const es5506_interface *intf = (const es5506_interface *)config; //es5506_state *chip = get_safe_token(device); // int j; // UINT32 accum_mask; int max_chns; chip->sndtype = sndtype; /* only override the number of channels if the value is in the valid range 1 .. 6 */ max_chns = chip->sndtype ? 6 : 4; // 6 for ES5506, 4 for ES5505 if (chip->channels < 1 || chip->channels > max_chns) chip->channels = 1; /* 1 channel by default, for backward compatibility */ /* debugging */ //if (LOG_COMMANDS && !eslog) // eslog = fopen("es.log", "w"); /* create the stream */ // chip->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock() / (16*32), chip, es5506_update); /* initialize the regions */ // chip->region_base[0] = intf->region0 ? (UINT16 *)device->machine().region(intf->region0)->base() : NULL; // chip->region_base[1] = intf->region1 ? (UINT16 *)device->machine().region(intf->region1)->base() : NULL; // chip->region_base[2] = intf->region2 ? (UINT16 *)device->machine().region(intf->region2)->base() : NULL; // chip->region_base[3] = intf->region3 ? (UINT16 *)device->machine().region(intf->region3)->base() : NULL; /* initialize the rest of the structure */ // chip->device = device; // chip->master_clock = device->clock(); // chip->irq_callback = intf->irq_callback; chip->master_clock = clock; chip->irqv = 0x80; if (chip->sndtype) { /* KT-76 assumes all voices are active on an ES5506 without setting them! */ chip->active_voices = 31; chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); //chip->stream->set_sample_rate(m_sample_rate); } else { // ES5505 chip->sample_rate = chip->master_clock / (16*32); } /* compute the tables */ compute_tables(chip); /* init the voices */ //accum_mask = (chip->sndtype == ES5506) ? 0xffffffff : 0x7fffffff; // now done in device_reset /* allocate memory */ //chip->scratch = auto_alloc_array(device->machine(), INT32, 2 * MAX_SAMPLE_CHUNK); chip->scratch = (INT32*)malloc(2 * MAX_SAMPLE_CHUNK * sizeof(INT32)); /* register save */ /*device->save_item(NAME(chip->sample_rate)); device->save_item(NAME(chip->write_latch)); device->save_item(NAME(chip->read_latch)); device->save_item(NAME(chip->current_page)); device->save_item(NAME(chip->active_voices)); device->save_item(NAME(chip->mode)); device->save_item(NAME(chip->wst)); device->save_item(NAME(chip->wend)); device->save_item(NAME(chip->lrend)); device->save_item(NAME(chip->irqv)); device->save_pointer(NAME(chip->scratch), 2 * MAX_SAMPLE_CHUNK); for (j = 0; j < 32; j++) { device->save_item(NAME(chip->voice[j].control), j); device->save_item(NAME(chip->voice[j].freqcount), j); device->save_item(NAME(chip->voice[j].start), j); device->save_item(NAME(chip->voice[j].lvol), j); device->save_item(NAME(chip->voice[j].end), j); device->save_item(NAME(chip->voice[j].lvramp), j); device->save_item(NAME(chip->voice[j].accum), j); device->save_item(NAME(chip->voice[j].rvol), j); device->save_item(NAME(chip->voice[j].rvramp), j); device->save_item(NAME(chip->voice[j].ecount), j); device->save_item(NAME(chip->voice[j].k2), j); device->save_item(NAME(chip->voice[j].k2ramp), j); device->save_item(NAME(chip->voice[j].k1), j); device->save_item(NAME(chip->voice[j].k1ramp), j); device->save_item(NAME(chip->voice[j].o4n1), j); device->save_item(NAME(chip->voice[j].o3n1), j); device->save_item(NAME(chip->voice[j].o3n2), j); device->save_item(NAME(chip->voice[j].o2n1), j); device->save_item(NAME(chip->voice[j].o2n2), j); device->save_item(NAME(chip->voice[j].o1n1), j); device->save_item(NAME(chip->voice[j].exbank), j); device->save_item(NAME(chip->voice[j].filtcount), j); }*/ /* success */ } //static DEVICE_START( es5506 ) int device_start_es5506(UINT8 ChipID, int clock, int channels) { es5506_state* chip; if (ChipID >= MAX_CHIPS) return 0; chip = &ES5506Data[ChipID]; //es5506_start_common(device, device->static_config(), ES5506); //chip->channels = channels; chip->channels = 1; // fixed to L+R for now es5506_start_common(chip, clock & 0x7FFFFFFF, clock >> 31); return chip->master_clock / (16*32); } /********************************************************************************************** DEVICE_STOP( es5506 ) -- stop emulation of the ES5506 ***********************************************************************************************/ //static DEVICE_STOP( es5506 ) void device_stop_es5506(UINT8 ChipID) { es5506_state *chip = &ES5506Data[ChipID]; free(chip->ulaw_lookup); chip->ulaw_lookup = NULL; free(chip->volume_lookup); chip->volume_lookup = NULL; free(chip->scratch); chip->scratch = NULL; /* debugging */ /*if (LOG_COMMANDS && eslog) { fclose(eslog); eslog = NULL; }*/ #if MAKE_WAVS { int i; for (i = 0; i < MAX_ES5506; i++) { if (es5506[i].wavraw) wav_close(es5506[i].wavraw); } } #endif } //static DEVICE_RESET( es5506 ) void device_reset_es5506(UINT8 ChipID) { es5506_state *chip = &ES5506Data[ChipID]; int j; UINT32 accum_mask; accum_mask = chip->sndtype ? 0xffffffff : 0x7fffffff; for (j = 0; j < 32; j++) { chip->voice[j].index = j; chip->voice[j].control = CONTROL_STOPMASK; chip->voice[j].lvol = 0xffff; chip->voice[j].rvol = 0xffff; chip->voice[j].exbank = 0; chip->voice[j].accum_mask = accum_mask; } } /********************************************************************************************** es5506_reg_write -- handle a write to the selected ES5506 register ***********************************************************************************************/ INLINE void es5506_reg_write_low(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) { switch (offset) { case 0x00/8: /* CR */ voice->control = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, control=%04x\n", chip->current_page & 0x1f, voice->control); break; case 0x08/8: /* FC */ voice->freqcount = data & 0x1ffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, freq count=%08x\n", chip->current_page & 0x1f, voice->freqcount); break; case 0x10/8: /* LVOL */ voice->lvol = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, left vol=%04x\n", chip->current_page & 0x1f, voice->lvol); break; case 0x18/8: /* LVRAMP */ voice->lvramp = (data & 0xff00) >> 8; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, left vol ramp=%04x\n", chip->current_page & 0x1f, voice->lvramp); break; case 0x20/8: /* RVOL */ voice->rvol = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, right vol=%04x\n", chip->current_page & 0x1f, voice->rvol); break; case 0x28/8: /* RVRAMP */ voice->rvramp = (data & 0xff00) >> 8; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, right vol ramp=%04x\n", chip->current_page & 0x1f, voice->rvramp); break; case 0x30/8: /* ECOUNT */ voice->ecount = data & 0x1ff; voice->filtcount = 0; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, envelope count=%04x\n", chip->current_page & 0x1f, voice->ecount); break; case 0x38/8: /* K2 */ voice->k2 = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, K2=%04x\n", chip->current_page & 0x1f, voice->k2); break; case 0x40/8: /* K2RAMP */ voice->k2ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, K2 ramp=%04x\n", chip->current_page & 0x1f, voice->k2ramp); break; case 0x48/8: /* K1 */ voice->k1 = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, K1=%04x\n", chip->current_page & 0x1f, voice->k1); break; case 0x50/8: /* K1RAMP */ voice->k1ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, K1 ramp=%04x\n", chip->current_page & 0x1f, voice->k1ramp); break; case 0x58/8: /* ACTV */ { chip->active_voices = data & 0x1f; chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); //chip->stream->set_sample_rate(chip->sample_rate); if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); break; } case 0x60/8: /* MODE */ chip->mode = data & 0x1f; break; case 0x68/8: /* PAR - read only */ case 0x70/8: /* IRQV - read only */ break; case 0x78/8: /* PAGE */ chip->current_page = data & 0x7f; break; } } INLINE void es5506_reg_write_high(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) { switch (offset) { case 0x00/8: /* CR */ voice->control = data & 0xffff; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, control=%04x\n", chip->current_page & 0x1f, voice->control); break; case 0x08/8: /* START */ voice->start = data & 0xfffff800; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, loop start=%08x\n", chip->current_page & 0x1f, voice->start); break; case 0x10/8: /* END */ voice->end = data & 0xffffff80; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, loop end=%08x\n", chip->current_page & 0x1f, voice->end); break; case 0x18/8: /* ACCUM */ voice->accum = data; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, accum=%08x\n", chip->current_page & 0x1f, voice->accum); break; case 0x20/8: /* O4(n-1) */ voice->o4n1 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O4(n-1)=%05x\n", chip->current_page & 0x1f, voice->o4n1 & 0x3ffff); break; case 0x28/8: /* O3(n-1) */ voice->o3n1 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O3(n-1)=%05x\n", chip->current_page & 0x1f, voice->o3n1 & 0x3ffff); break; case 0x30/8: /* O3(n-2) */ voice->o3n2 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O3(n-2)=%05x\n", chip->current_page & 0x1f, voice->o3n2 & 0x3ffff); break; case 0x38/8: /* O2(n-1) */ voice->o2n1 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O2(n-1)=%05x\n", chip->current_page & 0x1f, voice->o2n1 & 0x3ffff); break; case 0x40/8: /* O2(n-2) */ voice->o2n2 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O2(n-2)=%05x\n", chip->current_page & 0x1f, voice->o2n2 & 0x3ffff); break; case 0x48/8: /* O1(n-1) */ voice->o1n1 = (INT32)(data << 14) >> 14; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "voice %d, O1(n-1)=%05x\n", chip->current_page & 0x1f, voice->o1n1 & 0x3ffff); break; case 0x50/8: /* W_ST */ chip->wst = data & 0x7f; break; case 0x58/8: /* W_END */ chip->wend = data & 0x7f; break; case 0x60/8: /* LR_END */ chip->lrend = data & 0x7f; break; case 0x68/8: /* PAR - read only */ case 0x70/8: /* IRQV - read only */ break; case 0x78/8: /* PAGE */ chip->current_page = data & 0x7f; break; } } INLINE void es5506_reg_write_test(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) { switch (offset) { case 0x00/8: /* CHANNEL 0 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 0 left test write %08x\n", data); break; case 0x08/8: /* CHANNEL 0 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 0 right test write %08x\n", data); break; case 0x10/8: /* CHANNEL 1 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 1 left test write %08x\n", data); break; case 0x18/8: /* CHANNEL 1 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 1 right test write %08x\n", data); break; case 0x20/8: /* CHANNEL 2 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 2 left test write %08x\n", data); break; case 0x28/8: /* CHANNEL 2 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 2 right test write %08x\n", data); break; case 0x30/8: /* CHANNEL 3 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 3 left test write %08x\n", data); break; case 0x38/8: /* CHANNEL 3 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 3 right test write %08x\n", data); break; case 0x40/8: /* CHANNEL 4 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 4 left test write %08x\n", data); break; case 0x48/8: /* CHANNEL 4 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 4 right test write %08x\n", data); break; case 0x50/8: /* CHANNEL 5 LEFT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 5 left test write %08x\n", data); break; case 0x58/8: /* CHANNEL 6 RIGHT */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Channel 5 right test write %08x\n", data); break; case 0x60/8: /* EMPTY */ //if (LOG_COMMANDS && eslog) // fprintf(eslog, "Test write EMPTY %08x\n", data); break; case 0x68/8: /* PAR - read only */ case 0x70/8: /* IRQV - read only */ break; case 0x78/8: /* PAGE */ chip->current_page = data & 0x7f; break; } } //WRITE8_DEVICE_HANDLER( es5506_w ) static void es5506_w(es5506_state *chip, offs_t offset, UINT8 data) { //es5506_state *chip = get_safe_token(device); es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; int shift = 8 * (offset & 3); /* accumulate the data */ chip->write_latch = (chip->write_latch & ~(0xff000000 >> shift)) | (data << (24 - shift)); /* wait for a write to complete */ if (shift != 24) return; /* force an update */ //chip->stream->update(); /* switch off the page and register */ if (chip->current_page < 0x20) es5506_reg_write_low(chip, voice, offset / 4, chip->write_latch); else if (chip->current_page < 0x40) es5506_reg_write_high(chip, voice, offset / 4, chip->write_latch); else es5506_reg_write_test(chip, voice, offset / 4, chip->write_latch); /* clear the write latch when done */ chip->write_latch = 0; } /********************************************************************************************** es5506_reg_read -- read from the specified ES5506 register ***********************************************************************************************/ INLINE UINT32 es5506_reg_read_low(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT32 result = 0; switch (offset) { case 0x00/8: /* CR */ result = voice->control; break; case 0x08/8: /* FC */ result = voice->freqcount; break; case 0x10/8: /* LVOL */ result = voice->lvol; break; case 0x18/8: /* LVRAMP */ result = voice->lvramp << 8; break; case 0x20/8: /* RVOL */ result = voice->rvol; break; case 0x28/8: /* RVRAMP */ result = voice->rvramp << 8; break; case 0x30/8: /* ECOUNT */ result = voice->ecount; break; case 0x38/8: /* K2 */ result = voice->k2; break; case 0x40/8: /* K2RAMP */ result = (voice->k2ramp << 8) | (voice->k2ramp >> 31); break; case 0x48/8: /* K1 */ result = voice->k1; break; case 0x50/8: /* K1RAMP */ result = (voice->k1ramp << 8) | (voice->k1ramp >> 31); break; case 0x58/8: /* ACTV */ result = chip->active_voices; break; case 0x60/8: /* MODE */ result = chip->mode; break; case 0x68/8: /* PAR */ //if (chip->port_read) // result = (*chip->port_read)(); break; case 0x70/8: /* IRQV */ result = chip->irqv; update_internal_irq_state(chip); break; case 0x78/8: /* PAGE */ result = chip->current_page; break; } return result; } INLINE UINT32 es5506_reg_read_high(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT32 result = 0; switch (offset) { case 0x00/8: /* CR */ result = voice->control; break; case 0x08/8: /* START */ result = voice->start; break; case 0x10/8: /* END */ result = voice->end; break; case 0x18/8: /* ACCUM */ result = voice->accum; break; case 0x20/8: /* O4(n-1) */ result = voice->o4n1 & 0x3ffff; break; case 0x28/8: /* O3(n-1) */ result = voice->o3n1 & 0x3ffff; break; case 0x30/8: /* O3(n-2) */ result = voice->o3n2 & 0x3ffff; break; case 0x38/8: /* O2(n-1) */ result = voice->o2n1 & 0x3ffff; break; case 0x40/8: /* O2(n-2) */ result = voice->o2n2 & 0x3ffff; break; case 0x48/8: /* O1(n-1) */ result = voice->o1n1 & 0x3ffff; break; case 0x50/8: /* W_ST */ result = chip->wst; break; case 0x58/8: /* W_END */ result = chip->wend; break; case 0x60/8: /* LR_END */ result = chip->lrend; break; case 0x68/8: /* PAR */ //if (chip->port_read) // result = (*chip->port_read)(); break; case 0x70/8: /* IRQV */ result = chip->irqv; update_internal_irq_state(chip); break; case 0x78/8: /* PAGE */ result = chip->current_page; break; } return result; } INLINE UINT32 es5506_reg_read_test(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT32 result = 0; switch (offset) { case 0x68/8: /* PAR */ //if (chip->port_read) // result = (*chip->port_read)(); break; case 0x70/8: /* IRQV */ result = chip->irqv; break; case 0x78/8: /* PAGE */ result = chip->current_page; break; } return result; } //READ8_DEVICE_HANDLER( es5506_r ) static UINT8 es5506_r(es5506_state *chip, offs_t offset) { //es5506_state *chip = get_safe_token(device); es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; int shift = 8 * (offset & 3); /* only read on offset 0 */ if (shift != 0) return chip->read_latch >> (24 - shift); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "read from %02x/%02x -> ", chip->current_page, offset / 4 * 8); /* force an update */ //chip->stream->update(); /* switch off the page and register */ if (chip->current_page < 0x20) chip->read_latch = es5506_reg_read_low(chip, voice, offset / 4); else if (chip->current_page < 0x40) chip->read_latch = es5506_reg_read_high(chip, voice, offset / 4); else chip->read_latch = es5506_reg_read_test(chip, voice, offset / 4); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%08x\n", chip->read_latch); /* return the high byte */ return chip->read_latch >> 24; } //void es5506_voice_bank_w(device_t *device, int voice, int bank) void es5506_voice_bank_w(UINT8 ChipID, int voice, int bank) { //es5506_state *chip = get_safe_token(device); es5506_state *chip = &ES5506Data[ChipID]; chip->voice[voice].exbank=bank; } /********************************************************************************************** DEVICE_START( es5505 ) -- start emulation of the ES5505 ***********************************************************************************************/ /*static DEVICE_START( es5505 ) { const es5505_interface *intf = (const es5505_interface *)device->static_config(); es5506_interface es5506intf; memset(&es5506intf, 0, sizeof(es5506intf)); es5506intf.region0 = intf->region0; es5506intf.region1 = intf->region1; es5506intf.irq_callback = intf->irq_callback; es5506intf.read_port = intf->read_port; es5506_start_common(device, &es5506intf, ES5505); }*/ /********************************************************************************************** DEVICE_STOP( es5505 ) -- stop emulation of the ES5505 ***********************************************************************************************/ /*static DEVICE_STOP( es5505 ) { DEVICE_STOP_CALL( es5506 ); } static DEVICE_RESET( es5505 ) { DEVICE_RESET_CALL( es5506 ); }*/ /********************************************************************************************** es5505_reg_write -- handle a write to the selected ES5505 register ***********************************************************************************************/ INLINE void es5505_reg_write_low(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) { //running_machine &machine = chip->device->machine(); UINT8 byteOfs; byteOfs = offset & 0x01; if (! byteOfs) data <<= 8; switch (offset >> 1) { case 0x00: /* CR */ if (ACCESSING_BITS_0_7) { #if RAINE_CHECK voice->control &= ~(CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_DIR); #else voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); #endif voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | ((data << 12) & CONTROL_BS0); } if (ACCESSING_BITS_8_15) { voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); voice->control |= ((data >> 2) & CONTROL_LPMASK) | ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); } //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff); break; case 0x01: /* FC */ if (ACCESSING_BITS_0_7) voice->freqcount = (voice->freqcount & ~0x001fe) | ((data & 0x00ff) << 1); if (ACCESSING_BITS_8_15) voice->freqcount = (voice->freqcount & ~0x1fe00) | ((data & 0xff00) << 1); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, freq count=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->freqcount); break; case 0x02: /* STRT (hi) */ if (ACCESSING_BITS_0_7) voice->start = (voice->start & ~0x03fc0000) | ((data & 0x00ff) << 18); if (ACCESSING_BITS_8_15) voice->start = (voice->start & ~0x7c000000) | ((data & 0x1f00) << 18); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, loop start=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->start); break; case 0x03: /* STRT (lo) */ if (ACCESSING_BITS_0_7) voice->start = (voice->start & ~0x00000380) | ((data & 0x00e0) << 2); if (ACCESSING_BITS_8_15) voice->start = (voice->start & ~0x0003fc00) | ((data & 0xff00) << 2); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, loop start=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->start); break; case 0x04: /* END (hi) */ if (ACCESSING_BITS_0_7) voice->end = (voice->end & ~0x03fc0000) | ((data & 0x00ff) << 18); if (ACCESSING_BITS_8_15) voice->end = (voice->end & ~0x7c000000) | ((data & 0x1f00) << 18); #if RAINE_CHECK voice->control |= CONTROL_STOP0; #endif //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, loop end=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->end); break; case 0x05: /* END (lo) */ if (ACCESSING_BITS_0_7) voice->end = (voice->end & ~0x00000380) | ((data & 0x00e0) << 2); if (ACCESSING_BITS_8_15) voice->end = (voice->end & ~0x0003fc00) | ((data & 0xff00) << 2); #if RAINE_CHECK voice->control |= CONTROL_STOP0; #endif //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, loop end=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->end); break; case 0x06: /* K2 */ if (ACCESSING_BITS_0_7) voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0); if (ACCESSING_BITS_8_15) voice->k2 = (voice->k2 & ~0xff00) | (data & 0xff00); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, K2=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->k2); break; case 0x07: /* K1 */ if (ACCESSING_BITS_0_7) voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0); if (ACCESSING_BITS_8_15) voice->k1 = (voice->k1 & ~0xff00) | (data & 0xff00); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, K1=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->k1); break; case 0x08: /* LVOL */ if (ACCESSING_BITS_8_15) voice->lvol = (voice->lvol & ~0xff00) | (data & 0xff00); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, left vol=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->lvol); break; case 0x09: /* RVOL */ if (ACCESSING_BITS_8_15) voice->rvol = (voice->rvol & ~0xff00) | (data & 0xff00); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, right vol=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->rvol); break; case 0x0a: /* ACC (hi) */ if (ACCESSING_BITS_0_7) voice->accum = (voice->accum & ~0x03fc0000) | ((data & 0x00ff) << 18); if (ACCESSING_BITS_8_15) voice->accum = (voice->accum & ~0x7c000000) | ((data & 0x1f00) << 18); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, accum=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->accum); break; case 0x0b: /* ACC (lo) */ if (ACCESSING_BITS_0_7) voice->accum = (voice->accum & ~0x000003fc) | ((data & 0x00ff) << 2); if (ACCESSING_BITS_8_15) voice->accum = (voice->accum & ~0x0003fc00) | ((data & 0xff00) << 2); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, accum=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->accum); break; case 0x0c: /* unused */ break; case 0x0d: /* ACT */ if (ACCESSING_BITS_0_7) { chip->active_voices = data & 0x1f; chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); //chip->stream->set_sample_rate(chip->sample_rate); if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); } break; case 0x0e: /* IRQV - read only */ break; case 0x0f: /* PAGE */ if (ACCESSING_BITS_0_7) chip->current_page = data & 0x7f; break; } } INLINE void es5505_reg_write_high(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) { //running_machine &machine = chip->device->machine(); UINT8 byteOfs; byteOfs = offset & 0x01; if (! byteOfs) data <<= 8; switch (offset >> 1) { case 0x00: /* CR */ if (ACCESSING_BITS_0_7) { voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | ((data << 12) & CONTROL_BS0); } if (ACCESSING_BITS_8_15) { voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); voice->control |= ((data >> 2) & CONTROL_LPMASK) | ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); } //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->control, data, mem_mask); break; case 0x01: /* O4(n-1) */ if (ACCESSING_BITS_0_7) voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o4n1 = (INT16)((voice->o4n1 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O4(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o4n1 & 0x3ffff); break; case 0x02: /* O3(n-1) */ if (ACCESSING_BITS_0_7) voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o3n1 = (INT16)((voice->o3n1 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O3(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o3n1 & 0x3ffff); break; case 0x03: /* O3(n-2) */ if (ACCESSING_BITS_0_7) voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o3n2 = (INT16)((voice->o3n2 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O3(n-2)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o3n2 & 0x3ffff); break; case 0x04: /* O2(n-1) */ if (ACCESSING_BITS_0_7) voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o2n1 = (INT16)((voice->o2n1 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O2(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n1 & 0x3ffff); break; case 0x05: /* O2(n-2) */ if (ACCESSING_BITS_0_7) voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o2n2 = (INT16)((voice->o2n2 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O2(n-2)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n2 & 0x3ffff); break; case 0x06: /* O1(n-1) */ if (ACCESSING_BITS_0_7) voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) voice->o1n1 = (INT16)((voice->o1n1 & ~0xff00) | (data & 0xff00)); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%s:voice %d, O1(n-1)=%05x (accum=%08x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n1 & 0x3ffff, voice->accum); break; case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: /* unused */ break; case 0x0d: /* ACT */ if (ACCESSING_BITS_0_7) { chip->active_voices = data & 0x1f; chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); //chip->stream->set_sample_rate(chip->sample_rate); if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); } break; case 0x0e: /* IRQV - read only */ break; case 0x0f: /* PAGE */ if (ACCESSING_BITS_0_7) chip->current_page = data & 0x7f; break; } } INLINE void es5505_reg_write_test(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) { UINT8 byteOfs; byteOfs = offset & 0x01; if (! byteOfs) data <<= 8; switch (offset >> 1) { case 0x00: /* CH0L */ case 0x01: /* CH0R */ case 0x02: /* CH1L */ case 0x03: /* CH1R */ case 0x04: /* CH2L */ case 0x05: /* CH2R */ case 0x06: /* CH3L */ case 0x07: /* CH3R */ break; case 0x08: /* SERMODE */ if (ACCESSING_BITS_0_7) chip->mode = data & 0x07; break; case 0x09: /* PAR */ break; case 0x0d: /* ACT */ if (ACCESSING_BITS_0_7) { chip->active_voices = data & 0x1f; chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); //chip->stream->set_sample_rate(chip->sample_rate); if (chip->SmpRateFunc != NULL) chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); } break; case 0x0e: /* IRQV - read only */ break; case 0x0f: /* PAGE */ if (ACCESSING_BITS_0_7) chip->current_page = data & 0x7f; break; } } //WRITE16_DEVICE_HANDLER( es5505_w ) static void es5505_w(es5506_state *chip, offs_t offset, UINT8 data) { //es5506_state *chip = get_safe_token(device); es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; // logerror("%s:ES5505 write %02x/%02x = %04x & %04x\n", machine.describe_context(), chip->current_page, offset, data, mem_mask); /* force an update */ //chip->stream->update(); /* switch off the page and register */ if (chip->current_page < 0x20) es5505_reg_write_low(chip, voice, offset, data); else if (chip->current_page < 0x40) es5505_reg_write_high(chip, voice, offset, data); else es5505_reg_write_test(chip, voice, offset, data); } /********************************************************************************************** es5505_reg_read -- read from the specified ES5505 register ***********************************************************************************************/ INLINE UINT16 es5505_reg_read_low(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT16 result = 0; switch (offset) { case 0x00: /* CR */ result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | ((voice->control & CONTROL_BS0) >> 12) | ((voice->control & CONTROL_LPMASK) << 2) | ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | 0xf000; break; case 0x01: /* FC */ result = voice->freqcount >> 1; break; case 0x02: /* STRT (hi) */ result = voice->start >> 18; break; case 0x03: /* STRT (lo) */ result = voice->start >> 2; break; case 0x04: /* END (hi) */ result = voice->end >> 18; break; case 0x05: /* END (lo) */ result = voice->end >> 2; break; case 0x06: /* K2 */ result = voice->k2; break; case 0x07: /* K1 */ result = voice->k1; break; case 0x08: /* LVOL */ result = voice->lvol; break; case 0x09: /* RVOL */ result = voice->rvol; break; case 0x0a: /* ACC (hi) */ result = voice->accum >> 18; break; case 0x0b: /* ACC (lo) */ result = voice->accum >> 2; break; case 0x0c: /* unused */ break; case 0x0d: /* ACT */ result = chip->active_voices; break; case 0x0e: /* IRQV */ result = chip->irqv; update_internal_irq_state(chip); break; case 0x0f: /* PAGE */ result = chip->current_page; break; } return result; } INLINE UINT16 es5505_reg_read_high(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT16 result = 0; switch (offset) { case 0x00: /* CR */ result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | ((voice->control & CONTROL_BS0) >> 12) | ((voice->control & CONTROL_LPMASK) << 2) | ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | 0xf000; break; case 0x01: /* O4(n-1) */ result = voice->o4n1; break; case 0x02: /* O3(n-1) */ result = voice->o3n1; break; case 0x03: /* O3(n-2) */ result = voice->o3n2; break; case 0x04: /* O2(n-1) */ result = voice->o2n1; break; case 0x05: /* O2(n-2) */ result = voice->o2n2; break; case 0x06: /* O1(n-1) */ /* special case for the Taito F3 games: they set the accumulator on a stopped */ /* voice and assume the filters continue to process the data. They then read */ /* the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't */ /* want to waste time filtering stopped channels, we just look for a read from */ /* this register on a stopped voice, and return the raw sample data at the */ /* accumulator */ if ((voice->control & CONTROL_STOPMASK) && chip->region_base[voice->control >> 14]) { voice->o1n1 = chip->region_base[voice->control >> 14][voice->exbank + (voice->accum >> 11)]; logerror("%02x %08x ==> %08x\n",voice->o1n1,voice->control >> 14,voice->exbank + (voice->accum >> 11)); } result = voice->o1n1; break; case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: /* unused */ break; case 0x0d: /* ACT */ result = chip->active_voices; break; case 0x0e: /* IRQV */ result = chip->irqv; update_internal_irq_state(chip); break; case 0x0f: /* PAGE */ result = chip->current_page; break; } return result; } INLINE UINT16 es5505_reg_read_test(es5506_state *chip, es5506_voice *voice, offs_t offset) { UINT16 result = 0; switch (offset) { case 0x00: /* CH0L */ case 0x01: /* CH0R */ case 0x02: /* CH1L */ case 0x03: /* CH1R */ case 0x04: /* CH2L */ case 0x05: /* CH2R */ case 0x06: /* CH3L */ case 0x07: /* CH3R */ break; case 0x08: /* SERMODE */ result = chip->mode; break; case 0x09: /* PAR */ //if (chip->port_read) // result = (*chip->port_read)(); break; case 0x0f: /* PAGE */ result = chip->current_page; break; } return result; } //READ16_DEVICE_HANDLER( es5505_r ) static UINT8 es5505_r(es5506_state *chip, offs_t offset) { //es5506_state *chip = get_safe_token(device); es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; UINT16 result = 0; //if (LOG_COMMANDS && eslog) // fprintf(eslog, "read from %02x/%02x -> ", chip->current_page, offset); /* force an update */ //chip->stream->update(); /* switch off the page and register */ if (chip->current_page < 0x20) result = es5505_reg_read_low(chip, voice, offset); else if (chip->current_page < 0x40) result = es5505_reg_read_high(chip, voice, offset); else result = es5505_reg_read_test(chip, voice, offset); //if (LOG_COMMANDS && eslog) // fprintf(eslog, "%04x (accum=%08x)\n", result, voice->accum); /* return the high byte */ if (offset & 0x01) return (result & 0x00FF) >> 0; else return (result & 0xFF00) >> 8; } UINT8 es550x_r(UINT8 ChipID, offs_t offset) { es5506_state *chip = &ES5506Data[ChipID]; if (! chip->sndtype) return es5505_r(chip, offset); else return es5506_r(chip, offset); } void es550x_w(UINT8 ChipID, offs_t offset, UINT8 data) { es5506_state *chip = &ES5506Data[ChipID]; if (offset < 0x40) { if (! chip->sndtype) es5505_w(chip, offset, data); else es5506_w(chip, offset, data); } else { es5506_voice_bank_w(ChipID, offset & 0x1F, data << 20); } return; } void es550x_w16(UINT8 ChipID, offs_t offset, UINT16 data) { es5506_state *chip = &ES5506Data[ChipID]; if (offset < 0x40) { if (! chip->sndtype) { es5505_w(chip, offset | 0, (data & 0xFF00) >> 8); es5505_w(chip, offset | 1, (data & 0x00FF) >> 0); } else { es5506_w(chip, offset | 0, (data & 0xFF00) >> 8); es5506_w(chip, offset | 1, (data & 0x00FF) >> 0); } } else { es5506_voice_bank_w(ChipID, offset & 0x1F, data << 20); } return; } /*void es5505_voice_bank_w(device_t *device, int voice, int bank) { //es5506_state *chip = get_safe_token(device); es5506_state *chip = &ES5506Data[ChipID]; #if RAINE_CHECK chip->voice[voice].control = CONTROL_STOPMASK; #endif chip->voice[voice].exbank=bank; }*/ /*void es5505_set_channel_volume(device_t *device, int channel, int volume) { //es5506_state *chip = get_safe_token(device); es5506_state *chip = &ES5506Data[ChipID]; chip->stream->set_output_gain(channel,volume / 100.0); }*/ void es5506_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { es5506_state *info = &ES5506Data[ChipID]; UINT8 curRgn; UINT8 is8bROM; UINT32 curPos; curRgn = (DataStart >> 28) & 0x03; is8bROM = (DataStart >> 31) & 0x01; DataStart &= 0x0FFFFFFF; if (is8bROM) { // multiply all values with 2 for boundary checks ROMSize *= 2; DataStart *= 2; DataLength *= 2; } if (info->region_size[curRgn] != ROMSize) { info->region_base[curRgn] = (UINT16*)realloc(info->region_base[curRgn], ROMSize); info->region_size[curRgn] = ROMSize; memset(info->region_base[curRgn], 0x00, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; if (! is8bROM) { memcpy((UINT8*)info->region_base[curRgn] + DataStart, ROMData, DataLength); } else { DataLength /= 2; for (curPos = 0x00; curPos < DataLength; curPos ++) info->region_base[curRgn][DataStart + curPos] = ROMData[curPos] << 8; } return; } void es5506_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { es5506_state *chip = &ES5506Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 32; CurChn ++) chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } void es5506_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr) { es5506_state *chip = &ES5506Data[ChipID]; // set Sample Rate Change Callback routine chip->SmpRateFunc = CallbackFunc; chip->SmpRateData = DataPtr; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( es5505 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(es5506_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5505 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( es5505 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( es5505 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "ES5505"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq Wavetable"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( es5506 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(es5506_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5506 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( es5506 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( es5506 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "ES5506"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq Wavetable"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(ES5505, es5505); DEFINE_LEGACY_SOUND_DEVICE(ES5506, es5506);*/ ================================================ FILE: VGMPlay/chips/es5506.h ================================================ /********************************************************************************************** * * Ensoniq ES5505/6 driver * by Aaron Giles * **********************************************************************************************/ #pragma once #ifndef __ES5506_H__ #define __ES5506_H__ //#include "devlegcy.h" /*typedef struct _es5505_interface es5505_interface; struct _es5505_interface { const char * region0; // memory region where the sample ROM lives const char * region1; // memory region where the sample ROM lives void (*irq_callback)(device_t *device, int state); // irq callback UINT16 (*read_port)(device_t *device); // input port read };* READ16_DEVICE_HANDLER( es5505_r ); WRITE16_DEVICE_HANDLER( es5505_w ); void es5505_voice_bank_w(device_t *device, int voice, int bank); void es5505_set_channel_volume(device_t *device, int channel, int volume); //DECLARE_LEGACY_SOUND_DEVICE(ES5505, es5505); typedef struct _es5506_interface es5506_interface; struct _es5506_interface { const char * region0; // memory region where the sample ROM lives const char * region1; // memory region where the sample ROM lives const char * region2; // memory region where the sample ROM lives const char * region3; // memory region where the sample ROM lives void (*irq_callback)(device_t *device, int state); // irq callback UINT16 (*read_port)(device_t *device); // input port read };*/ //READ8_DEVICE_HANDLER( es5506_r ); //WRITE8_DEVICE_HANDLER( es5506_w ); UINT8 es550x_r(UINT8 ChipID, offs_t offset); void es550x_w(UINT8 ChipID, offs_t offset, UINT8 data); void es550x_w16(UINT8 ChipID, offs_t offset, UINT16 data); void es5506_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_es5506(UINT8 ChipID, int clock, int channels); void device_stop_es5506(UINT8 ChipID); void device_reset_es5506(UINT8 ChipID); //void es5506_set_base(running_device *device, UINT8 *wavemem); void es5506_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void es5506_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void es5506_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr); //void es5506_voice_bank_w(device_t *device, int voice, int bank); //DECLARE_LEGACY_SOUND_DEVICE(ES5506, es5506); #endif /* __ES5506_H__ */ ================================================ FILE: VGMPlay/chips/fm.c ================================================ #define YM2610B_WARNING /* ** ** File: fm.c -- software implementation of Yamaha FM sound generator ** ** Copyright Jarek Burczynski (bujar at mame dot net) ** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** ** Version 1.4.2 (final beta) ** */ /* ** History: ** ** 2006-2008 Eke-Eke (Genesis Plus GX), MAME backport by R. Belmont. ** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis ** - fixed SSG-EG support, credits to Nemesis and additional fixes from Alone Coder ** - modified EG rates and frequency, tested by Nemesis on real hardware ** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound) ** - fixed Attack Rate update (Batman & Robin intro) ** - fixed attenuation level at the start of Substain (Gynoug explosions) ** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...) ** ** 06-23-2007 Zsolt Vasvari: ** - changed the timing not to require the use of floating point calculations ** ** 03-08-2003 Jarek Burczynski: ** - fixed YM2608 initial values (after the reset) ** - fixed flag and irqmask handling (YM2608) ** - fixed BUFRDY flag handling (YM2608) ** ** 14-06-2003 Jarek Burczynski: ** - implemented all of the YM2608 status register flags ** - implemented support for external memory read/write via YM2608 ** - implemented support for deltat memory limit register in YM2608 emulation ** ** 22-05-2003 Jarek Burczynski: ** - fixed LFO PM calculations (copy&paste bugfix) ** ** 08-05-2003 Jarek Burczynski: ** - fixed SSG support ** ** 22-04-2003 Jarek Burczynski: ** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) ** ** 15-04-2003 Jarek Burczynski: ** - added support for YM2608's register 0x110 - status mask ** ** 01-12-2002 Jarek Burczynski: ** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) ** The addressing patch used for early Neo-Geo games can be removed now. ** ** 26-11-2002 Jarek Burczynski, Nicola Salmoria: ** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: ** - added emulation of YM2608 drums. ** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) ** ** 16-08-2002 Jarek Burczynski: ** - binary exact Envelope Generator (verified on real YM2203); ** identical to YM2151 ** - corrected 'off by one' error in feedback calculations (when feedback is off) ** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) ** ** 18-12-2001 Jarek Burczynski: ** - added SSG-EG support (verified on real YM2203) ** ** 12-08-2001 Jarek Burczynski: ** - corrected sin_tab and tl_tab data (verified on real chip) ** - corrected feedback calculations (verified on real chip) ** - corrected phase generator calculations (verified on real chip) ** - corrected envelope generator calculations (verified on real chip) ** - corrected FM volume level (YM2610 and YM2610B). ** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : ** this was needed to calculate YM2610 FM channels output correctly. ** (Each FM channel is calculated as in other chips, but the output of the channel ** gets shifted right by one *before* sending to accumulator. That was impossible to do ** with previous implementation). ** ** 23-07-2001 Jarek Burczynski, Nicola Salmoria: ** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) ** ** 11-06-2001 Jarek Burczynski: ** - corrected end of sample bug in ADPCMA_calc_cha(). ** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). ** ** 08-12-98 hiro-shi: ** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA ** move ROM limit check.(CALC_CH? -> 2610Write1/2) ** test program (ADPCMB_TEST) ** move ADPCM A/B end check. ** ADPCMB repeat flag(no check) ** change ADPCM volume rate (8->16) (32->48). ** ** 09-12-98 hiro-shi: ** change ADPCM volume. (8->16, 48->64) ** replace ym2610 ch0/3 (YM-2610B) ** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. ** add ADPCM_SHIFT_MASK ** change ADPCMA_DECODE_MIN/MAX. */ /************************************************************************/ /* comment of hiro-shi(Hiromitsu Shioya) */ /* YM2610(B) = OPN-B */ /* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /************************************************************************/ #include #include #include #include #include #include "mamedef.h" //#ifndef __RAINE__ //#include "sndintrf.h" /* use M.A.M.E. */ //#else //#include "deftypes.h" /* use RAINE */ //#include "support.h" /* use RAINE */ //#endif #include "fm.h" /* include external DELTA-T unit (when needed) */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) #include "ymdeltat.h" #endif /* shared function building option */ #define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) #define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) /* globals */ #define TYPE_SSG 0x01 /* SSG support */ #define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ #define TYPE_6CH 0x04 /* FM 6CH / 3CH */ #define TYPE_DAC 0x08 /* YM2612's DAC device */ #define TYPE_ADPCM 0x10 /* two ADPCM units */ #define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ #define TYPE_YM2203 (TYPE_SSG) #define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) #define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>3) /* sin waveform table in 'decibel' scale */ static unsigned int sin_tab[SIN_LEN]; /* sustain level table (3dB per step) */ /* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ /* 1, 2, 4, 8, 16, 32, 64 (value)*/ /* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ #define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) static const UINT32 sl_table[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; #undef SC #define RATE_STEPS (8) static const UINT8 eg_inc[19*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ /*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ /*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ /*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ /*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ /*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ /*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(17) in this table - it's directly in the code */ static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), /* rates 00-11 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 12 */ O( 4),O( 5),O( 6),O( 7), /* rate 13 */ O( 8),O( 9),O(10),O(11), /* rate 14 */ O(12),O(13),O(14),O(15), /* rate 15 */ O(16),O(16),O(16),O(16), /* 32 dummy rates (same as 15 3) */ O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ /*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ /*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ #define O(a) (a*1) static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-11 */ O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 12 */ O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 32 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) }; #undef O static const UINT8 dt_tab[4 * 32]={ /* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ /* FD=0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* FD=1 */ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, /* FD=2 */ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, /* FD=3 */ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 }; /* OPN key frequency number -> key code follow table */ /* fnum higher 4bit -> keycode lower 2bit */ static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; /* 8 LFO speed parameters */ /* each value represents number of samples that one LFO level will last for */ static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; /*There are 4 different LFO AM depths available, they are: 0 dB, 1.4 dB, 5.9 dB, 11.8 dB Here is how it is generated (in EG steps): 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 (1.4 dB is losing precision as you can see) It's implemented as generator from 0..126 with step 2 then a shift right N times, where N is: 8 for 0 dB 3 for 1.4 dB 1 for 5.9 dB 0 for 11.8 dB */ static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; /*There are 8 different LFO PM depths available, they are: 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 (bits 8,9,10 = FNUM MSB from OCT/FNUM register) Here we store only first quarter (positive one) of full waveform. Full table (lfo_pm_table) containing all 128 waveforms is build at run (init) time. One value in table below represents 4 (four) basic LFO steps (1 PM step = 4 AM steps). For example: at LFO SPEED=0 (which is 108 samples per basic LFO step) one value from "lfo_pm_output" table lasts for 432 consecutive samples (4*108=432) and one full LFO waveform cycle lasts for 13824 samples (32*432=13824; 32 because we store only a quarter of whole waveform in the table below) */ static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ /* FNUM BIT 4: 000 0001xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* FNUM BIT 5: 000 0010xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* FNUM BIT 6: 000 0100xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, /* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* FNUM BIT 7: 000 1000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, /* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, /* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* FNUM BIT 8: 001 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, /* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, /* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, /* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* FNUM BIT 9: 010 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, /* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, /* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, /* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, /* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* FNUM BIT10: 100 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, /* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, /* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, /* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, /* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, }; /* all 128 LFO PM waveforms */ static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ /* register number to channel number , slot offset */ #define OPN_CHAN(N) (N&3) #define OPN_SLOT(N) ((N>>2)&3) /* slot number */ #define SLOT1 0 #define SLOT2 2 #define SLOT3 1 #define SLOT4 3 /* bit0 = Right enable , bit1 = Left enable */ #define OUTD_RIGHT 1 #define OUTD_LEFT 2 #define OUTD_CENTER 3 /* save output as raw 16-bit sample */ /* #define SAVE_SAMPLE */ #ifdef SAVE_SAMPLE static FILE *sample[1]; #if 1 /*save to MONO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = rt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif /* struct describing a single operator (SLOT) */ typedef struct { INT32 *DT; /* detune :dt_tab[DT] */ UINT8 KSR; /* key scale rate :3-KSR */ UINT32 ar; /* attack rate */ UINT32 d1r; /* decay rate */ UINT32 d2r; /* sustain rate */ UINT32 rr; /* release rate */ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ UINT32 mul; /* multiple :ML_TABLE[ML] */ /* Phase Generator */ UINT32 phase; /* phase counter */ INT32 Incr; /* phase step */ /* Envelope Generator */ UINT8 state; /* phase type */ UINT32 tl; /* total level: TL << 3 */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level:sl_table[SL] */ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT8 eg_sh_d1r; /* (decay state) */ UINT8 eg_sel_d1r; /* (decay state) */ UINT8 eg_sh_d2r; /* (sustain state) */ UINT8 eg_sel_d2r; /* (sustain state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT8 ssg; /* SSG-EG waveform */ UINT8 ssgn; /* SSG-EG negated output */ UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON */ /* LFO */ UINT32 AMmask; /* AM enable flag */ } FM_SLOT; typedef struct { FM_SLOT SLOT[4]; /* four SLOTs (operators) */ UINT8 ALGO; /* algorithm */ UINT8 FB; /* feedback shift */ INT32 op1_out[2]; /* op1 output for feedback */ INT32 *connect1; /* SLOT1 output pointer */ INT32 *connect3; /* SLOT3 output pointer */ INT32 *connect2; /* SLOT2 output pointer */ INT32 *connect4; /* SLOT4 output pointer */ INT32 *mem_connect;/* where to put the delayed sample (MEM) */ INT32 mem_value; /* delayed sample (MEM) value */ INT32 pms; /* channel PMS */ UINT8 ams; /* channel AMS */ UINT32 fc; /* fnum,blk:adjusted to sample rate */ UINT8 kcode; /* key code: */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 Muted; } FM_CH; typedef struct { //const device_config *device; void * param; /* this chip parameter */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ int timer_prescaler; /* timer prescaler */ #if FM_BUSY_FLAG_SUPPORT TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ #endif UINT8 address; /* address register */ UINT8 irq; /* interrupt level */ UINT8 irqmask; /* irq mask */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ UINT8 prescaler_sel; /* prescaler selector */ UINT8 fn_h; /* freq latch */ INT32 TA; /* timer a */ INT32 TAC; /* timer a counter */ UINT8 TB; /* timer b */ INT32 TBC; /* timer b counter */ /* local time tables */ INT32 dt_tab[8][32]; /* DeTune table */ /* Extention Timer and IRQ handler */ FM_TIMERHANDLER timer_handler; FM_IRQHANDLER IRQ_Handler; const ssg_callbacks *SSG; } FM_ST; /***********************************************************/ /* OPN unit */ /***********************************************************/ /* OPN 3slot struct */ typedef struct { UINT32 fc[3]; /* fnum3,blk3: calculated */ UINT8 fn_h; /* freq3 latch */ UINT8 kcode[3]; /* key code */ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ } FM_3SLOT; /* OPN/A/B common state */ typedef struct { UINT8 type; /* chip type */ FM_ST ST; /* general state */ FM_3SLOT SL3; /* 3 slot mode state */ FM_CH *P_CH; /* pointer of CH */ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ UINT32 fn_table[4096]; /* fnumber->increment counter */ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ /* LFO */ UINT32 LFO_AM; /* runtime LFO calculations helper */ INT32 LFO_PM; /* runtime LFO calculations helper */ UINT32 lfo_cnt; UINT32 lfo_inc; UINT32 lfo_freq[8]; /* LFO FREQ table */ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ INT32 mem; /* one sample delay memory */ INT32 out_fm[8]; /* outputs of working channels */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) INT32 out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 ADPCM */ INT32 out_delta[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 DELTAT*/ #endif } FM_OPN; /* log output level */ #define LOG_ERR 3 /* ERROR */ #define LOG_WAR 2 /* WARNING */ #define LOG_INF 1 /* INFORMATION */ #define LOG_LEVEL LOG_INF #ifndef __RAINE__ #define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) #endif /* limitter */ #define Limit(val, max,min) { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \ } /* status set and IRQ handling */ INLINE void FM_STATUS_SET(FM_ST *ST,int flag) { /* set status flag */ ST->status |= flag; if ( !(ST->irq) && (ST->status & ST->irqmask) ) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); } } /* status reset and IRQ handling */ INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) { /* reset status flag */ ST->status &=~flag; if ( (ST->irq) && !(ST->status & ST->irqmask) ) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); } } /* IRQ mask set */ INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) { ST->irqmask = flag; /* IRQ handling check */ FM_STATUS_SET(ST,0); FM_STATUS_RESET(ST,0); } /* OPN Mode Register Write */ INLINE void set_timers( FM_ST *ST, void *n, int v ) { /* b7 = CSM MODE */ /* b6 = 3 slot mode */ /* b5 = reset b */ /* b4 = reset a */ /* b3 = timer enable b */ /* b2 = timer enable a */ /* b1 = load b */ /* b0 = load a */ ST->mode = v; /* reset Timer b flag */ if( v & 0x20 ) FM_STATUS_RESET(ST,0x02); /* reset Timer a flag */ if( v & 0x10 ) FM_STATUS_RESET(ST,0x01); /* load b */ if( v & 0x02 ) { if( ST->TBC == 0 ) { ST->TBC = ( 256-ST->TB)<<4; /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); } } else { /* stop timer b */ if( ST->TBC != 0 ) { ST->TBC = 0; if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock); } } /* load a */ if( v & 0x01 ) { if( ST->TAC == 0 ) { ST->TAC = (1024-ST->TA); /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); } } else { /* stop timer a */ if( ST->TAC != 0 ) { ST->TAC = 0; if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock); } } } /* Timer A Overflow */ INLINE void TimerAOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); /* clear or reload the counter */ ST->TAC = (1024-ST->TA); if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock); } /* Timer B Overflow */ INLINE void TimerBOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); /* clear or reload the counter */ ST->TBC = ( 256-ST->TB)<<4; if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock); } #if FM_INTERNAL_TIMER /* ----- internal timer mode , update timer */ /* ---------- calculate timer A ---------- */ #define INTERNAL_TIMER_A(ST,CSM_CH) \ { \ if( (ST)->TAC && ((ST)->timer_handler==0) ) \ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ /* CSM mode total level latch and auto key on */ \ if( (ST)->mode & 0x80 ) \ CSMKeyControll( OPN->type, CSM_CH ); \ } \ } /* ---------- calculate timer B ---------- */ #define INTERNAL_TIMER_B(ST,step) \ { \ if( (ST)->TBC && ((ST)->timer_handler==0) ) \ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \ } #else /* FM_INTERNAL_TIMER */ /* external timer mode */ #define INTERNAL_TIMER_A(ST,CSM_CH) #define INTERNAL_TIMER_B(ST,step) #endif /* FM_INTERNAL_TIMER */ #if FM_BUSY_FLAG_SUPPORT #define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) { if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) { if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) return ST->status | 0x80; /* with busy */ /* expire */ FM_BUSY_CLEAR(ST); } return ST->status; } INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) { TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); } #else #define FM_STATUS_FLAG(ST) ((ST)->status) #define FM_BUSY_SET(ST,bclock) {} #define FM_BUSY_CLEAR(ST) {} #endif INLINE void FM_KEYON(UINT8 type, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( !SLOT->key ) { SLOT->key = 1; SLOT->phase = 0; /* restart Phase Generator */ SLOT->ssgn = (SLOT->ssg & 0x04) >> 1; SLOT->state = EG_ATT; } } INLINE void FM_KEYOFF(FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( SLOT->key ) { SLOT->key = 0; if (SLOT->state>EG_REL) SLOT->state = EG_REL;/* phase -> Release */ } } /* set algorithm connection */ static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) { INT32 *carrier = &OPN->out_fm[ch]; INT32 **om1 = &CH->connect1; INT32 **om2 = &CH->connect3; INT32 **oc1 = &CH->connect2; INT32 **memc = &CH->mem_connect; switch( CH->ALGO ) { case 0: /* M1---C1---MEM---M2---C2---OUT */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 1: /* M1------+-MEM---M2---C2---OUT */ /* C1-+ */ *om1 = &OPN->mem; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 2: /* M1-----------------+-C2---OUT */ /* C1---MEM---M2-+ */ *om1 = &OPN->c2; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 3: /* M1---C1---MEM------+-C2---OUT */ /* M2-+ */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->c2; break; case 4: /* M1---C1-+-OUT */ /* M2---C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = &OPN->c2; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 5: /* +----C1----+ */ /* M1-+-MEM---M2-+-OUT */ /* +----C2----+ */ *om1 = 0; /* special mark */ *oc1 = carrier; *om2 = carrier; *memc= &OPN->m2; break; case 6: /* M1---C1-+ */ /* M2-+-OUT */ /* C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 7: /* M1-+ */ /* C1-+-OUT */ /* M2-+ */ /* C2-+ */ /* MEM: not used*/ *om1 = carrier; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; } CH->connect4 = carrier; } /* set detune & multiple */ INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) { SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; SLOT->DT = ST->dt_tab[(v>>4)&7]; CH->SLOT[SLOT1].Incr=-1; } /* set total level */ INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) { SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ } /* set attack rate & key scale */ INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) { UINT8 old_KSR = SLOT->KSR; SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->KSR = 3-(v>>6); if (SLOT->KSR != old_KSR) { CH->SLOT[SLOT1].Incr=-1; } /* refresh Attack rate */ if ((SLOT->ar + SLOT->ksr) < 32+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 17*RATE_STEPS; } } /* set decay rate */ INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; } /* set sustain rate */ INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; } /* set release rate */ INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->sl = sl_table[ v>>4 ]; SLOT->rr = 34 + ((v&0x0f)<<2); SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; } INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } /* advance LFO to next sample */ INLINE void advance_lfo(FM_OPN *OPN) { UINT8 pos; if (OPN->lfo_inc) /* LFO enabled ? */ { OPN->lfo_cnt += OPN->lfo_inc; pos = (OPN->lfo_cnt >> LFO_SH) & 127; /* update AM when LFO output changes */ /* actually I can't optimize is this way without rewriting chan_calc() to use chip->lfo_am instead of global lfo_am */ { /* triangle */ /* AM: 0 to 126 step +2, 126 to 0 step -2 */ if (pos<64) OPN->LFO_AM = (pos&63) * 2; else OPN->LFO_AM = 126 - ((pos&63) * 2); } /* PM works with 4 times slower clock */ pos >>= 2; /* update PM when LFO output changes */ /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/ { OPN->LFO_PM = pos; } } else { OPN->LFO_AM = 0; OPN->LFO_PM = 0; } } /* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */ static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) { unsigned int out; unsigned int swap_flag = 0; unsigned int i; i = 4; /* four operators per channel */ do { /* reset SSG-EG swap flag */ swap_flag = 0; switch(SLOT->state) { case EG_ATT: /* attack phase */ if ( !(OPN->eg_cnt & ((1<eg_sh_ar)-1) ) ) { SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]) ) >>4; if (SLOT->volume <= MIN_ATT_INDEX) { SLOT->volume = MIN_ATT_INDEX; SLOT->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ { if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ { if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; if ( SLOT->volume >= (INT32)(SLOT->sl) ) SLOT->state = EG_SUS; } } else { if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) { SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; if ( SLOT->volume >= (INT32)(SLOT->sl) ) SLOT->state = EG_SUS; } } } break; case EG_SUS: /* sustain phase */ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ { if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; if ( SLOT->volume >= ENV_QUIET ) { SLOT->volume = MAX_ATT_INDEX; if (SLOT->ssg&0x01) /* bit 0 = hold */ { if (SLOT->ssgn&1) /* have we swapped once ??? */ { /* yes, so do nothing, just hold current level */ } else swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */ } else { /* same as KEY-ON operation */ /* restart of the Phase Generator should be here */ SLOT->phase = 0; { /* phase -> Attack */ SLOT->volume = 511; SLOT->state = EG_ATT; } swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */ } } } } else { if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) { SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; if ( SLOT->volume >= MAX_ATT_INDEX ) { SLOT->volume = MAX_ATT_INDEX; /* do not change SLOT->state (verified on real chip) */ } } } break; case EG_REL: /* release phase */ if ( !(OPN->eg_cnt & ((1<eg_sh_rr)-1) ) ) { /* SSG-EG affects Release phase also (Nemesis) */ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; if ( SLOT->volume >= MAX_ATT_INDEX ) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } } break; } out = ((UINT32)SLOT->volume); /* negate output (changes come from alternate bit, init comes from attack bit) */ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) out ^= MAX_ATT_INDEX; /* we need to store the result here because we are going to change ssgn in next instruction */ SLOT->vol_out = out + SLOT->tl; /* reverse SLOT inversion flag */ SLOT->ssgn ^= swap_flag; SLOT++; i--; }while (i); } #define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) { UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk; UINT32 fn; int kc, fc; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; blk = (block_fnum&0x7000) >> 12; fn = block_fnum & 0xfff; /* keyscale code */ kc = (blk<<2) | opn_fktable[fn >> 8]; /* phase increment counter */ fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; /* detects frequency overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* update phase */ SLOT->phase += (fc * SLOT->mul) >> 1; } else /* LFO phase modulation = zero */ { SLOT->phase += SLOT->Incr; } } INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) { UINT32 block_fnum = CH->block_fnum; UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk; UINT32 fn; int kc, fc, finc; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; blk = (block_fnum&0x7000) >> 12; fn = block_fnum & 0xfff; /* keyscale code */ kc = (blk<<2) | opn_fktable[fn >> 8]; /* phase increment counter */ fc = (OPN->fn_table[fn]>>(7-blk)); /* detects frequency overflow (credits to Nemesis) */ finc = fc + CH->SLOT[SLOT1].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; finc = fc + CH->SLOT[SLOT2].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; finc = fc + CH->SLOT[SLOT3].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; finc = fc + CH->SLOT[SLOT4].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; } else /* LFO phase modulation = zero */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } INLINE void chan_calc(FM_OPN *OPN, FM_CH *CH, int chnum) { unsigned int eg_out; UINT32 AM = OPN->LFO_AM >> CH->ams; if (CH->Muted) return; OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ eg_out = volume_calc(&CH->SLOT[SLOT1]); { INT32 out = CH->op1_out[0] + CH->op1_out[1]; CH->op1_out[0] = CH->op1_out[1]; if( !CH->connect1 ) { /* algorithm 5 */ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; } else { /* other algorithms */ *CH->connect1 += CH->op1_out[0]; } CH->op1_out[1] = 0; if( eg_out < ENV_QUIET ) /* SLOT 1 */ { if (!CH->FB) out=0; CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); } } eg_out = volume_calc(&CH->SLOT[SLOT3]); if( eg_out < ENV_QUIET ) /* SLOT 3 */ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); eg_out = volume_calc(&CH->SLOT[SLOT2]); if( eg_out < ENV_QUIET ) /* SLOT 2 */ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); eg_out = volume_calc(&CH->SLOT[SLOT4]); if( eg_out < ENV_QUIET ) /* SLOT 4 */ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); /* store current MEM */ CH->mem_value = OPN->mem; /* update phase counters AFTER output calculations */ if(CH->pms) { /* add support for 3 slot mode */ if ((OPN->ST.mode & 0xC0) && (chnum == 2)) { update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); } else update_phase_lfo_channel(OPN, CH); } else /* no LFO phase modulation */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } /* update phase increment and envelope generator */ INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) { int ksr = kc >> SLOT->KSR; fc += SLOT->DT[kc]; /* detects frequency overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* (frequency) phase increment counter */ SLOT->Incr = (fc * SLOT->mul) >> 1; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 32+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 17*RATE_STEPS; } SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; } } /* update phase increment counters */ /* Changed from INLINE to static to work around gcc 4.2.1 codegen bug */ static void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) { if( CH->SLOT[SLOT1].Incr==-1) { int fc = CH->fc; int kc = CH->kcode; refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); } } /* initialize time tables */ static void init_timetables( FM_ST *ST , const UINT8 *dttable ) { int i,d; double rate; #if 0 logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n", ST->rate, ST->clock, ST->freqbase ); #endif /* DeTune table */ for (d = 0;d <= 3;d++) { for (i = 0;i <= 31;i++) { rate = ((double)dttable[d*32 + i]) * SIN_LEN * ST->freqbase * (1<dt_tab[d][i] = (INT32) rate; ST->dt_tab[d+4][i] = -ST->dt_tab[d][i]; #if 0 logerror("FM.C: DT [%2i %2i] = %8x \n", d, i, ST->dt_tab[d][i] ); #endif } } } static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) { int c,s; ST->mode = 0; /* normal mode */ ST->TA = 0; ST->TAC = 0; ST->TB = 0; ST->TBC = 0; for( c = 0 ; c < num ; c++ ) { //memset(&CH[c], 0x00, sizeof(FM_CH)); CH[c].mem_value = 0; CH[c].op1_out[0] = 0; CH[c].op1_out[1] = 0; CH[c].fc = 0; for(s = 0 ; s < 4 ; s++ ) { //memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); CH[c].SLOT[s].Incr = -1; CH[c].SLOT[s].key = 0; CH[c].SLOT[s].phase = 0; CH[c].SLOT[s].ssg = 0; CH[c].SLOT[s].ssgn = 0; CH[c].SLOT[s].state= EG_OFF; CH[c].SLOT[s].volume = MAX_ATT_INDEX; CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; } } } /* initialize generic tables */ static int init_tables(void) { signed int i,x; signed int n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 2; /* 13 bits here (as in real chip) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; for (i=1; i<13; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } #if 0 logerror("tl %04i", x); for (i=0; i<13; i++) logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); logerror("\n"); #endif } /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ } /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/ /* build LFO PM modulation table */ for(i = 0; i < 8; i++) /* 8 PM depths */ { UINT8 fnum; for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ { UINT8 value; UINT8 step; UINT32 offset_depth = i; UINT32 offset_fnum_bit; UINT32 bit_tmp; for (step=0; step<8; step++) { value = 0; for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ { if (fnum & (1<SLOT[SLOT1].key) { FM_KEYON(type, CH,SLOT1); FM_KEYOFF(CH, SLOT1); } if (!CH->SLOT[SLOT2].key) { FM_KEYON(type, CH,SLOT2); FM_KEYOFF(CH, SLOT2); } if (!CH->SLOT[SLOT3].key) { FM_KEYON(type, CH,SLOT3); FM_KEYOFF(CH, SLOT3); } if (!CH->SLOT[SLOT4].key) { FM_KEYON(type, CH,SLOT4); FM_KEYOFF(CH, SLOT4); } } #ifdef __STATE_H__ /* FM channel save , internal state only */ static void FMsave_state_channel(const device_config *device,FM_CH *CH,int num_ch) { int slot , ch; for(ch=0;chop1_out); state_save_register_device_item(device, ch, CH->fc); /* slots */ for(slot=0;slot<4;slot++) { FM_SLOT *SLOT = &CH->SLOT[slot]; state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); state_save_register_device_item(device, ch * 4 + slot, SLOT->state); state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); } } } static void FMsave_state_st(const device_config *device,FM_ST *ST) { #if FM_BUSY_FLAG_SUPPORT state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); #endif state_save_register_device_item(device, 0, ST->address ); state_save_register_device_item(device, 0, ST->irq ); state_save_register_device_item(device, 0, ST->irqmask ); state_save_register_device_item(device, 0, ST->status ); state_save_register_device_item(device, 0, ST->mode ); state_save_register_device_item(device, 0, ST->prescaler_sel ); state_save_register_device_item(device, 0, ST->fn_h ); state_save_register_device_item(device, 0, ST->TA ); state_save_register_device_item(device, 0, ST->TAC ); state_save_register_device_item(device, 0, ST->TB ); state_save_register_device_item(device, 0, ST->TBC ); } #endif /* _STATE_H */ #if BUILD_OPN /* prescaler set (and make time tables) */ static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) { int i; /* frequency base */ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; #if 0 OPN->ST.rate = (double)OPN->ST.clock / pres; OPN->ST.freqbase = 1.0; #endif OPN->eg_timer_add = (1<ST.freqbase; OPN->eg_timer_overflow = ( 3 ) * (1<ST.timer_prescaler = timer_prescaler; /* SSG part prescaler set */ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); /* make time tables */ init_timetables( &OPN->ST, dt_tab ); /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ /* calculate fnumber -> increment counter table */ for(i = 0; i < 4096; i++) { /* freq table for octave 7 */ /* OPN phase increment counter = 20bit */ OPN->fn_table[i] = (UINT32)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ #if 0 logerror("FM.C: fn_table[%4i] = %08x (dec=%8i)\n", i, OPN->fn_table[i]>>6,OPN->fn_table[i]>>6 ); #endif } /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ OPN->fn_max = (UINT32)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* LFO freq. table */ for(i = 0; i < 8; i++) { /* Amplitude modulation: 64 output levels (triangle waveform); 1 level lasts for one of "lfo_samples_per_step" samples */ /* Phase modulation: one entry from lfo_pm_output lasts for one of 4 * "lfo_samples_per_step" samples */ OPN->lfo_freq[i] = (1.0 / lfo_samples_per_step[i]) * (1<ST.freqbase; #if 0 logerror("FM.C: lfo_freq[%i] = %08x (dec=%8i)\n", i, OPN->lfo_freq[i],OPN->lfo_freq[i] ); #endif } } /* write a OPN mode register 0x20-0x2f */ static void OPNWriteMode(FM_OPN *OPN, int r, int v) { UINT8 c; FM_CH *CH; switch(r) { case 0x21: /* Test */ break; case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ if( OPN->type & TYPE_LFOPAN ) { if (v&0x08) /* LFO enabled ? */ { OPN->lfo_inc = OPN->lfo_freq[v&7]; } else { OPN->lfo_inc = 0; } } break; case 0x24: /* timer A High 8*/ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); break; case 0x25: /* timer A Low 2*/ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); break; case 0x26: /* timer B */ OPN->ST.TB = v; break; case 0x27: /* mode, timer control */ set_timers( &(OPN->ST),OPN->ST.param,v ); break; case 0x28: /* key on / off */ c = v & 0x03; if( c == 3 ) break; if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; CH = OPN->P_CH; CH = &CH[c]; if(v&0x10) FM_KEYON(OPN->type,CH,SLOT1); else FM_KEYOFF(CH,SLOT1); if(v&0x20) FM_KEYON(OPN->type,CH,SLOT2); else FM_KEYOFF(CH,SLOT2); if(v&0x40) FM_KEYON(OPN->type,CH,SLOT3); else FM_KEYOFF(CH,SLOT3); if(v&0x80) FM_KEYON(OPN->type,CH,SLOT4); else FM_KEYOFF(CH,SLOT4); break; } } /* write a OPN register (0x30-0xff) */ static void OPNWriteReg(FM_OPN *OPN, int r, int v) { FM_CH *CH; FM_SLOT *SLOT; UINT8 c = OPN_CHAN(r); if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ if (r >= 0x100) c+=3; CH = OPN->P_CH; CH = &CH[c]; SLOT = &(CH->SLOT[OPN_SLOT(r)]); switch( r & 0xf0 ) { case 0x30: /* DET , MUL */ set_det_mul(&OPN->ST,CH,SLOT,v); break; case 0x40: /* TL */ set_tl(CH,SLOT,v); break; case 0x50: /* KS, AR */ set_ar_ksr(OPN->type,CH,SLOT,v); break; case 0x60: /* bit7 = AM ENABLE, DR */ set_dr(OPN->type, SLOT,v); if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ { SLOT->AMmask = (v&0x80) ? ~0 : 0; } break; case 0x70: /* SR */ set_sr(OPN->type,SLOT,v); break; case 0x80: /* SL, RR */ set_sl_rr(OPN->type,SLOT,v); break; case 0x90: /* SSG-EG */ SLOT->ssg = v&0x0f; SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */ /* SSG-EG envelope shapes : E AtAlH 1 0 0 0 \\\\ 1 0 0 1 \___ 1 0 1 0 \/\/ ___ 1 0 1 1 \ 1 1 0 0 //// ___ 1 1 0 1 / 1 1 1 0 /\/\ 1 1 1 1 /___ E = SSG-EG enable The shapes are generated using Attack, Decay and Sustain phases. Each single character in the diagrams above represents this whole sequence: - when KEY-ON = 1, normal Attack phase is generated (*without* any difference when compared to normal mode), - later, when envelope level reaches minimum level (max volume), the EG switches to Decay phase (which works with bigger steps when compared to normal mode - see below), - later when envelope level passes the SL level, the EG swithes to Sustain phase (which works with bigger steps when compared to normal mode - see below), - finally when envelope level reaches maximum level (min volume), the EG switches to Attack phase again (depends on actual waveform). Important is that when switch to Attack phase occurs, the phase counter of that operator will be zeroed-out (as in normal KEY-ON) but not always. (I havent found the rule for that - perhaps only when the output level is low) The difference (when compared to normal Envelope Generator mode) is that the resolution in Decay and Sustain phases is 4 times lower; this results in only 256 steps instead of normal 1024. In other words: when SSG-EG is disabled, the step inside of the EG is one, when SSG-EG is enabled, the step is four (in Decay and Sustain phases). Times between the level changes are the same in both modes. Important: Decay 1 Level (so called SL) is compared to actual SSG-EG output, so it is the same in both SSG and no-SSG modes, with this exception: when the SSG-EG is enabled and is generating raising levels (when the EG output is inverted) the SL will be found at wrong level !!! For example, when SL=02: 0 -6 = -6dB in non-inverted EG output 96-6 = -90dB in inverted EG output Which means that EG compares its level to SL as usual, and that the output is simply inverted afterall. The Yamaha's manuals say that AR should be set to 0x1f (max speed). That is not necessary, but then EG will be generating Attack phase. */ break; case 0xa0: switch( OPN_SLOT(r) ) { case 0: /* 0xa0-0xa2 : FNUM1 */ { UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; UINT8 blk = OPN->ST.fn_h>>3; /* keyscale code */ CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ CH->fc = OPN->fn_table[fn*2]>>(7-blk); /* store fnum in clear form for LFO PM calculations */ CH->block_fnum = (blk<<11) | fn; CH->SLOT[SLOT1].Incr=-1; } break; case 1: /* 0xa4-0xa6 : FNUM2,BLK */ OPN->ST.fn_h = v&0x3f; break; case 2: /* 0xa8-0xaa : 3CH FNUM1 */ if(r < 0x100) { UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; UINT8 blk = OPN->SL3.fn_h>>3; /* keyscale code */ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); OPN->SL3.block_fnum[c] = (blk<<11) | fn; (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; } break; case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ if(r < 0x100) OPN->SL3.fn_h = v&0x3f; break; } break; case 0xb0: switch( OPN_SLOT(r) ) { case 0: /* 0xb0-0xb2 : FB,ALGO */ { int feedback = (v>>3)&7; CH->ALGO = v&7; CH->FB = feedback ? feedback+6 : 0; setup_connection( OPN, CH, c ); } break; case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ if( OPN->type & TYPE_LFOPAN) { /* b0-2 PMS */ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ /* b4-5 AMS */ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; /* PAN : b7 = L, b6 = R */ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; } break; } break; } } #endif /* BUILD_OPN */ #if BUILD_OPN_PRESCALER /* prescaler circuit (best guess to verified chip behaviour) +--------------+ +-sel2-+ | +--|in20 | +---+ | +-sel1-+ | | M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK | +---+ | out|-|1/3|-|in21 | +----------|in11 | +---+ +------+ +------+ reg.2d : sel2 = in21 (select sel2) reg.2e : sel1 = in11 (select sel1) reg.2f : sel1 = in10 , sel2 = in20 (clear selector) reset : sel1 = in11 , sel2 = in21 (clear both) */ static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider) { static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 }; static const int ssg_pres[4] = { 1 , 1 , 4 , 2 }; int sel; switch(addr) { case 0: /* when reset */ OPN->ST.prescaler_sel = 2; break; case 1: /* when postload */ break; case 0x2d: /* divider sel : select 1/1 for 1/3line */ OPN->ST.prescaler_sel |= 0x02; break; case 0x2e: /* divider sel , select 1/3line for output */ OPN->ST.prescaler_sel |= 0x01; break; case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */ OPN->ST.prescaler_sel = 0; break; } sel = OPN->ST.prescaler_sel & 3; /* update prescaler */ OPNSetPres( OPN, opn_pres[sel]*pre_divider, opn_pres[sel]*pre_divider, ssg_pres[sel]*pre_divider ); } #endif /* BUILD_OPN_PRESCALER */ #if BUILD_YM2203 /*****************************************************************************/ /* YM2203 local section */ /*****************************************************************************/ /* here's the virtual YM2203(OPN) */ typedef struct { UINT8 REGS[256]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[3]; /* channel state */ } YM2203; /* Generate samples for one of the YM2203s */ void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; int i; FMSAMPLE *bufL = buffer[0]; FMSAMPLE *bufR = buffer[1]; FM_CH *cch[3]; cch[0] = &F2203->CH[0]; cch[1] = &F2203->CH[1]; cch[2] = &F2203->CH[2]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (F2203->OPN.ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); /* YM2203 doesn't have LFO so we must keep these globals at 0 level */ OPN->LFO_AM = 0; OPN->LFO_PM = 0; /* buffering */ for (i=0; i < length ; i++) { /* clear outputs */ OPN->out_fm[0] = 0; OPN->out_fm[1] = 0; OPN->out_fm[2] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); /* buffering */ { int lt; lt = OPN->out_fm[0] + OPN->out_fm[1] + OPN->out_fm[2]; lt >>= FINAL_SH; //Limit( lt , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = lt; } /* timer A control */ INTERNAL_TIMER_A( &F2203->OPN.ST , cch[2] ) } INTERNAL_TIMER_B(&F2203->OPN.ST,length) } /* ---------- reset one of chip ---------- */ void ym2203_reset_chip(void *chip) { int i; YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; /* Reset Prescaler */ OPNPrescaler_w(OPN, 0 , 1 ); /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); FM_BUSY_CLEAR(&OPN->ST); OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2203->CH , 3 ); /* reset OPerator paramater */ for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0); for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); } #ifdef __STATE_H__ void ym2203_postload(void *chip) { if (chip) { YM2203 *F2203 = (YM2203 *)chip; int r; /* prescaler */ OPNPrescaler_w(&F2203->OPN,1,1); /* SSG registers */ for(r=0;r<16;r++) { (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,0,r); (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,1,F2203->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); /* channels */ /*FM_channel_postload(F2203->CH,3);*/ } } static void YM2203_save_state(YM2203 *F2203, const device_config *device) { state_save_register_device_item_array(device, 0, F2203->REGS); FMsave_state_st(device,&F2203->OPN.ST); FMsave_state_channel(device,F2203->CH,3); /* 3slots */ state_save_register_device_item_array (device, 0, F2203->OPN.SL3.fc); state_save_register_device_item (device, 0, F2203->OPN.SL3.fn_h); state_save_register_device_item_array (device, 0, F2203->OPN.SL3.kcode); } #endif /* _STATE_H */ /* ---------- Initialize YM2203 emulator(s) ---------- 'num' is the number of virtual YM2203s to allocate 'clock' is the chip clock in Hz 'rate' is sampling rate */ //void * ym2203_init(void *param, const device_config *device, int clock, int rate, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) void * ym2203_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2203 *F2203; /* allocate ym2203 state space */ if( (F2203 = (YM2203 *)malloc(sizeof(YM2203)))==NULL) return NULL; /* clear */ memset(F2203,0,sizeof(YM2203)); if( !init_tables() ) { free( F2203 ); return NULL; } F2203->OPN.ST.param = param; F2203->OPN.type = TYPE_YM2203; F2203->OPN.P_CH = F2203->CH; //F2203->OPN.ST.device = device; F2203->OPN.ST.clock = clock; F2203->OPN.ST.rate = rate; F2203->OPN.ST.timer_handler = timer_handler; F2203->OPN.ST.IRQ_Handler = IRQHandler; F2203->OPN.ST.SSG = ssg; #ifdef __STATE_H__ YM2203_save_state(F2203, device); #endif return F2203; } /* shut down emulator */ void ym2203_shutdown(void *chip) { YM2203 *FM2203 = (YM2203 *)chip; FMCloseTable(); free(FM2203); } /* YM2203 I/O interface */ int ym2203_write(void *chip,int a,UINT8 v) { YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; if( !(a&1) ) { /* address port */ OPN->ST.address = (v &= 0xff); /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); /* prescaler select : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) OPNPrescaler_w(OPN , v , 1); } else { /* data port */ int addr = OPN->ST.address; F2203->REGS[addr] = v; switch( addr & 0xf0 ) { case 0x00: /* 0x00-0x0f : SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x20: /* 0x20-0x2f : Mode section */ ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteMode(OPN,addr,v); break; default: /* 0x30-0xff : OPN section */ ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } FM_BUSY_SET(&OPN->ST,1); } return OPN->ST.irq; } UINT8 ym2203_read(void *chip,int a) { YM2203 *F2203 = (YM2203 *)chip; int addr = F2203->OPN.ST.address; UINT8 ret = 0; if( !(a&1) ) { /* status port */ ret = FM_STATUS_FLAG(&F2203->OPN.ST); } else { /* data port (only SSG) */ if( addr < 16 ) ret = (*F2203->OPN.ST.SSG->read)(F2203->OPN.ST.param); } return ret; } int ym2203_timer_over(void *chip,int c) { YM2203 *F2203 = (YM2203 *)chip; if( c ) { /* Timer B */ TimerBOver( &(F2203->OPN.ST) ); } else { /* Timer A */ ym2203_update_req(F2203->OPN.ST.param); /* timer update */ TimerAOver( &(F2203->OPN.ST) ); /* CSM mode key,TL control */ if( F2203->OPN.ST.mode & 0x80 ) { /* CSM mode auto key on */ CSMKeyControll( F2203->OPN.type, &(F2203->CH[2]) ); } } return F2203->OPN.ST.irq; } void ym2203_set_mutemask(void *chip, UINT32 MuteMask) { YM2203 *F2203 = (YM2203 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 3; CurChn ++) F2203->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } #endif /* BUILD_YM2203 */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) /* ADPCM type A channel struct */ typedef struct { UINT8 flag; /* port state */ UINT8 flagMask; /* arrived flag mask */ UINT8 now_data; /* current ROM data */ UINT32 now_addr; /* current ROM address */ UINT32 now_step; UINT32 step; UINT32 start; /* sample data start address*/ UINT32 end; /* sample data end address */ UINT8 IL; /* Instrument Level */ INT32 adpcm_acc; /* accumulator */ INT32 adpcm_step; /* step */ INT32 adpcm_out; /* (speedup) hiro-shi!! */ INT8 vol_mul; /* volume in "0.75dB" steps */ UINT8 vol_shift; /* volume in "-6dB" steps */ INT32 *pan; /* &out_adpcm[OPN_xxxx] */ UINT8 Muted; } ADPCM_CH; /* here's the virtual YM2610 */ typedef struct { UINT8 REGS[512]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[6]; /* channel state */ UINT8 addr_A1; /* address line A1 */ /* ADPCM-A unit */ //const UINT8 *pcmbuf; /* pcm rom buffer */ UINT8 *pcmbuf; /* pcm rom buffer */ UINT32 pcm_size; /* size of pcm rom */ UINT8 adpcmTL; /* adpcmA total level */ ADPCM_CH adpcm[6]; /* adpcm channels */ UINT32 adpcmreg[0x30]; /* registers */ UINT8 adpcm_arrivedEndAddress; YM_DELTAT deltaT; /* Delta-T ADPCM unit */ UINT8 MuteDeltaT; UINT8 flagmask; /* YM2608 only */ UINT8 irqmask; /* YM2608 only */ } YM2610; /* here is the virtual YM2608 */ typedef YM2610 YM2608; /**** YM2610 ADPCM defines ****/ #define ADPCM_SHIFT (16) /* frequency step rate */ #define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift */ /* Algorithm and tables verified on real YM2608 and YM2610 */ /* usual ADPCM table (16 * 1.1^N) */ static const int steps[49] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 }; /* different from the usual ADPCM table */ static const int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 }; /* speedup purposes only */ static int jedi_table[ 49*16 ]; static void Init_ADPCMATable(void) { int step, nib; for (step = 0; step < 49; step++) { /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (2*(nib & 0x07) + 1) * steps[step] / 8; jedi_table[step*16 + nib] = (nib&0x08) ? -value : value; } } } /* ADPCM A (Non control type) : calculate one channel output */ INLINE void ADPCMA_calc_chan( YM2610 *F2610, ADPCM_CH *ch ) { UINT32 step; UINT8 data; if (ch->Muted) return; ch->now_step += ch->step; if ( ch->now_step >= (1<now_step >> ADPCM_SHIFT; ch->now_step &= (1< instead of == */ /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */ /* Here we use 1<<21 to compensate for nibble calculations */ if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) & ((1<<21)-1)) ) { ch->flag = 0; F2610->adpcm_arrivedEndAddress |= ch->flagMask; return; } #if 0 if ( ch->now_addr > (F2610->pcmsizeA<<1) ) { #ifdef _DEBUG LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" )); #endif return; } #endif if ( ch->now_addr&1 ) data = ch->now_data & 0x0f; else { ch->now_data = *(F2610->pcmbuf+(ch->now_addr>>1)); data = (ch->now_data >> 4) & 0x0f; } ch->now_addr++; ch->adpcm_acc += jedi_table[ch->adpcm_step + data]; /* extend 12-bit signed int */ if (ch->adpcm_acc & ~0x7ff) ch->adpcm_acc |= ~0xfff; else ch->adpcm_acc &= 0xfff; ch->adpcm_step += step_inc[data & 7]; Limit( ch->adpcm_step, 48*16, 0*16 ); }while(--step); /* calc pcm * volume data */ ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >> ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */ } /* output for work of output channels (out_adpcm[OPNxxxx])*/ *(ch->pan) += ch->adpcm_out; } /* ADPCM type A Write */ static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v) { ADPCM_CH *adpcm = F2610->adpcm; UINT8 c = r&0x07; F2610->adpcmreg[r] = v&0xff; /* stock data */ switch( r ) { case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */ if( !(v&0x80) ) { /* KEY ON */ for( c = 0; c < 6; c++ ) { if( (v>>c)&1 ) { /**** start adpcm ****/ // The .step variable is already set and for the YM2608 it is different on channels 4 and 5. //adpcm[c].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); adpcm[c].now_addr = adpcm[c].start<<1; adpcm[c].now_step = 0; adpcm[c].adpcm_acc = 0; adpcm[c].adpcm_step= 0; adpcm[c].adpcm_out = 0; adpcm[c].flag = 1; if(F2610->pcmbuf==NULL) { /* Check ROM Mapped */ #ifdef _DEBUG logerror("YM2608-YM2610: ADPCM-A rom not mapped\n"); #endif adpcm[c].flag = 0; } else { if(adpcm[c].end >= F2610->pcm_size) { /* Check End in Range */ #ifdef _DEBUG logerror("YM2610: ADPCM-A end out of range: $%08x\n",adpcm[c].end); #endif /*adpcm[c].end = F2610->pcm_size-1;*/ /* JB: DO NOT uncomment this, otherwise you will break the comparison in the ADPCM_CALC_CHA() */ } if(adpcm[c].start >= F2610->pcm_size) /* Check Start in Range */ { #ifdef _DEBUG logerror("YM2608-YM2610: ADPCM-A start out of range: $%08x\n",adpcm[c].start); #endif adpcm[c].flag = 0; } } } } } else { /* KEY OFF */ for( c = 0; c < 6; c++ ) if( (v>>c)&1 ) adpcm[c].flag = 0; } break; case 0x01: /* B0-5 = TL */ F2610->adpcmTL = (v & 0x3f) ^ 0x3f; for( c = 0; c < 6; c++ ) { int volume = F2610->adpcmTL + adpcm[c].IL; if ( volume >= 63 ) /* This is correct, 63 = quiet */ { adpcm[c].vol_mul = 0; adpcm[c].vol_shift = 0; } else { adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ } /* calc pcm * volume data */ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ } break; default: c = r&0x07; if( c >= 0x06 ) return; switch( r&0x38 ) { case 0x08: /* B7=L,B6=R, B4-0=IL */ { int volume; adpcm[c].IL = (v & 0x1f) ^ 0x1f; volume = F2610->adpcmTL + adpcm[c].IL; if ( volume >= 63 ) /* This is correct, 63 = quiet */ { adpcm[c].vol_mul = 0; adpcm[c].vol_shift = 0; } else { adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ } adpcm[c].pan = &F2610->OPN.out_adpcm[(v>>6)&0x03]; /* calc pcm * volume data */ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ } break; case 0x10: case 0x18: adpcm[c].start = ( (F2610->adpcmreg[0x18 + c]*0x0100 | F2610->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT); break; case 0x20: case 0x28: adpcm[c].end = ( (F2610->adpcmreg[0x28 + c]*0x0100 | F2610->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT); adpcm[c].end += (1<flag); state_save_register_device_item(device, ch, adpcm->now_data); state_save_register_device_item(device, ch, adpcm->now_addr); state_save_register_device_item(device, ch, adpcm->now_step); state_save_register_device_item(device, ch, adpcm->adpcm_acc); state_save_register_device_item(device, ch, adpcm->adpcm_step); state_save_register_device_item(device, ch, adpcm->adpcm_out); } } #endif /* _STATE_H */ #endif /* (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) */ #if BUILD_YM2608 /*****************************************************************************/ /* YM2608 local section */ /*****************************************************************************/ static const unsigned int YM2608_ADPCM_ROM_addr[2*6] = { 0x0000, 0x01bf, /* bass drum */ 0x01c0, 0x043f, /* snare drum */ 0x0440, 0x1b7f, /* top cymbal */ 0x1b80, 0x1cff, /* high hat */ 0x1d00, 0x1f7f, /* tom tom */ 0x1f80, 0x1fff /* rim shot */ }; /* This data is derived from the chip's output - internal ROM can't be read. It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal. */ static const unsigned char YM2608_ADPCM_ROM[0x2000] = { /* Source: 01BD.ROM */ /* Length: 448 / 0x000001C0 */ 0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54, 0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97, 0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0, 0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1, 0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24, 0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99, 0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99, 0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09, 0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8, 0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39, 0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9, 0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A, 0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40, 0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00, 0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12, 0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18, 0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52, 0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02, 0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B, 0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC, 0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A, 0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98, 0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A, 0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99, 0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99, 0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11, 0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31, 0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80, /* Source: 02SD.ROM */ /* Length: 640 / 0x00000280 */ 0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1, 0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41, 0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03, 0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18, 0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02, 0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92, 0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28, 0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62, 0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E, 0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09, 0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB, 0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83, 0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85, 0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21, 0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0, 0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80, 0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21, 0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8, 0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F, 0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A, 0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12, 0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36, 0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80, 0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90, 0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99, 0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03, 0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89, 0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18, 0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27, 0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9, 0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39, 0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23, 0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1, 0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3, 0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9, 0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71, 0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A, 0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88, 0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C, 0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10, /* Source: 04TOP.ROM */ /* Length: 5952 / 0x00001740 */ 0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C, 0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00, 0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E, 0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C, 0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38, 0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81, 0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A, 0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81, 0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4, 0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8, 0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10, 0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29, 0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98, 0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59, 0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3, 0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28, 0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B, 0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B, 0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11, 0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0, 0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90, 0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00, 0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8, 0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29, 0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F, 0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12, 0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8, 0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4, 0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82, 0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11, 0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09, 0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A, 0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3, 0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85, 0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88, 0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80, 0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C, 0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10, 0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81, 0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81, 0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8, 0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A, 0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79, 0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49, 0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA, 0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99, 0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24, 0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7, 0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8, 0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0, 0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15, 0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18, 0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B, 0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C, 0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B, 0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7, 0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14, 0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02, 0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98, 0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88, 0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01, 0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2, 0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D, 0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19, 0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28, 0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21, 0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A, 0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2, 0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87, 0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11, 0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C, 0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A, 0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0, 0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1, 0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11, 0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90, 0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00, 0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2, 0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3, 0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82, 0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00, 0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B, 0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28, 0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79, 0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90, 0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1, 0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05, 0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10, 0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19, 0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D, 0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5, 0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90, 0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49, 0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C, 0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C, 0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0, 0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3, 0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4, 0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4, 0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9, 0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01, 0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88, 0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12, 0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D, 0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02, 0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09, 0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89, 0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A, 0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2, 0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49, 0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50, 0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80, 0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C, 0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C, 0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13, 0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01, 0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18, 0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C, 0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1, 0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3, 0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B, 0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E, 0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90, 0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28, 0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28, 0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E, 0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91, 0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00, 0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71, 0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20, 0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8, 0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03, 0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7, 0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C, 0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09, 0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12, 0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2, 0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6, 0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02, 0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B, 0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC, 0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0, 0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00, 0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A, 0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2, 0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3, 0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F, 0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A, 0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0, 0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84, 0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10, 0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21, 0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F, 0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B, 0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24, 0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3, 0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00, 0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93, 0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10, 0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30, 0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0, 0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7, 0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1, 0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19, 0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B, 0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99, 0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A, 0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C, 0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03, 0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1, 0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A, 0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19, 0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8, 0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11, 0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A, 0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59, 0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F, 0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C, 0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80, 0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5, 0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08, 0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29, 0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1, 0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28, 0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D, 0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A, 0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D, 0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48, 0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A, 0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3, 0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1, 0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18, 0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C, 0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A, 0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19, 0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91, 0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04, 0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29, 0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10, 0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1, 0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0, 0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84, 0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95, 0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11, 0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B, 0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19, 0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10, 0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1, 0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17, 0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A, 0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08, 0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C, 0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1, 0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2, 0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09, 0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20, 0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A, 0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D, 0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82, 0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1, 0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2, 0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99, 0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18, 0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1, 0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1, 0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01, 0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40, 0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A, 0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C, 0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83, 0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12, 0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09, 0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68, 0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92, 0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A, 0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80, 0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82, 0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80, 0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B, 0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B, 0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18, 0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91, 0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1, 0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6, 0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B, 0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89, 0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90, 0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A, 0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01, 0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98, 0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29, 0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D, 0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92, 0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93, 0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88, 0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79, 0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20, 0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59, 0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9, 0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8, 0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07, 0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13, 0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80, 0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB, 0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A, 0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20, 0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0, 0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20, 0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39, 0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92, 0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1, 0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0, 0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41, 0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC, 0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30, 0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18, 0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B, 0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A, 0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09, 0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10, 0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1, 0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96, 0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09, 0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09, 0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A, 0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B, 0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90, 0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0, 0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1, 0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28, 0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18, 0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF, 0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0, 0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02, 0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08, 0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96, 0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88, 0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28, 0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D, 0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0, 0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83, 0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3, 0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04, 0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2, 0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21, 0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98, 0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8, 0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23, 0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39, 0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08, 0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E, 0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14, 0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4, 0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0, 0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88, 0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0, 0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69, 0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08, 0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13, 0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0, 0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA, 0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81, 0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01, 0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B, 0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B, 0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4, 0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18, 0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01, 0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0, 0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5, 0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94, 0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A, 0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A, 0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88, 0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B, 0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01, 0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12, 0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12, 0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D, 0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02, 0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1, 0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82, 0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1, 0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A, 0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A, 0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F, 0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98, 0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1, 0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3, 0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68, 0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91, 0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A, 0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C, 0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90, 0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6, 0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89, 0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B, 0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA, 0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1, 0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93, 0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93, 0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00, 0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B, 0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2, 0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92, 0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89, 0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48, 0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10, 0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29, 0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91, 0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3, 0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90, /* Source: 08HH.ROM */ /* Length: 384 / 0x00000180 */ 0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3, 0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7, 0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D, 0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80, 0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B, 0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2, 0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91, 0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D, 0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C, 0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5, 0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B, 0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01, 0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92, 0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93, 0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4, 0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3, 0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08, 0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3, 0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88, 0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92, 0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00, 0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4, 0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4, 0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0, /* Source: 10TOM.ROM */ /* Length: 640 / 0x00000280 */ 0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2, 0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11, 0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1, 0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32, 0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9, 0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01, 0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14, 0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA, 0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81, 0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00, 0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63, 0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB, 0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34, 0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB, 0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31, 0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99, 0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A, 0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30, 0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02, 0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98, 0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11, 0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11, 0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A, 0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35, 0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11, 0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB, 0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42, 0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99, 0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20, 0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81, 0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31, 0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC, 0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33, 0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB, 0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31, 0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99, 0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99, 0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88, 0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12, 0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* Source: 20RIM.ROM */ /* Length: 128 / 0x00000080 */ 0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A, 0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2, 0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C, 0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91, 0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59, 0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92, 0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6, 0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80, }; /* flag enable control 0x110 */ INLINE void YM2608IRQFlagWrite(FM_OPN *OPN, YM2608 *F2608, int v) { if( v & 0x80 ) { /* Reset IRQ flag */ FM_STATUS_RESET(&OPN->ST, 0xf7); /* don't touch BUFRDY flag otherwise we'd have to call ymdeltat module to set the flag back */ } else { /* Set status flag mask */ F2608->flagmask = (~(v&0x1f)); FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); } } /* compatible mode & IRQ enable control 0x29 */ INLINE void YM2608IRQMaskWrite(FM_OPN *OPN, YM2608 *F2608, int v) { /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ /* extend 3ch. enable/disable */ if(v&0x80) OPN->type |= TYPE_6CH; /* OPNA mode - 6 FM channels */ else OPN->type &= ~TYPE_6CH; /* OPN mode - 3 FM channels */ /* IRQ MASK store and set */ F2608->irqmask = v&0x1f; FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); } /* Generate samples for one of the YM2608s */ void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; YM_DELTAT *DELTAT = &F2608->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[6]; INT32 *out_fm = OPN->out_fm; /* set bufer */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2608->CH[0]; cch[1] = &F2608->CH[1]; cch[2] = &F2608->CH[2]; cch[3] = &F2608->CH[3]; cch[4] = &F2608->CH[4]; cch[5] = &F2608->CH[5]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); refresh_fc_eg_chan( OPN, cch[4] ); refresh_fc_eg_chan( OPN, cch[5] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[0] = 0; out_fm[1] = 0; out_fm[2] = 0; out_fm[3] = 0; out_fm[4] = 0; out_fm[5] = 0; /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); chan_calc(OPN, cch[3], 3 ); chan_calc(OPN, cch[4], 4 ); chan_calc(OPN, cch[5], 5 ); /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2608->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2608->adpcm[j].flag ) ADPCMA_calc_chan( F2608, &F2608->adpcm[j]); } /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[0]>>1) & OPN->pan[0]); // shift right verified on real YM2608 rt += ((out_fm[0]>>1) & OPN->pan[1]); lt += ((out_fm[1]>>1) & OPN->pan[2]); rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[3]>>1) & OPN->pan[6]); rt += ((out_fm[3]>>1) & OPN->pan[7]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ // this way it's louder (and more accurate) lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[0] & OPN->pan[0]); rt += (out_fm[0] & OPN->pan[1]); lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[3] & OPN->pan[6]); rt += (out_fm[3] & OPN->pan[7]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; //Limit( lt, MAXOUT, MINOUT ); //Limit( rt, MAXOUT, MINOUT ); /* buffering */ bufL[i] = lt; bufR[i] = rt; #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[2] ) } INTERNAL_TIMER_B(&OPN->ST,length) /* check IRQ for DELTA-T EOS */ FM_STATUS_SET(&OPN->ST, 0); } #ifdef __STATE_H__ void ym2608_postload(void *chip) { if (chip) { YM2608 *F2608 = (YM2608 *)chip; int r; /* prescaler */ OPNPrescaler_w(&F2608->OPN,1,2); F2608->deltaT.freqbase = F2608->OPN.ST.freqbase; /* IRQ mask / mode */ YM2608IRQMaskWrite(&F2608->OPN, F2608, F2608->REGS[0x29]); /* SSG registers */ for(r=0;r<16;r++) { (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,0,r); (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,1,F2608->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) { OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); } /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) { OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); } /* FM channels */ /*FM_channel_postload(F2608->CH,6);*/ /* rhythm(ADPCMA) */ FM_ADPCMAWrite(F2608,1,F2608->REGS[0x111]); for( r=0x08 ; r<0x0c ; r++) FM_ADPCMAWrite(F2608,r,F2608->REGS[r+0x110]); /* Delta-T ADPCM unit */ YM_DELTAT_postload(&F2608->deltaT , &F2608->REGS[0x100] ); } } static void YM2608_save_state(YM2608 *F2608, const device_config *device) { state_save_register_device_item_array(device, 0, F2608->REGS); FMsave_state_st(device,&F2608->OPN.ST); FMsave_state_channel(device,F2608->CH,6); /* 3slots */ state_save_register_device_item_array(device, 0, F2608->OPN.SL3.fc); state_save_register_device_item(device, 0, F2608->OPN.SL3.fn_h); state_save_register_device_item_array(device, 0, F2608->OPN.SL3.kcode); /* address register1 */ state_save_register_device_item(device, 0, F2608->addr_A1); /* rythm(ADPCMA) */ FMsave_state_adpcma(device,F2608->adpcm); /* Delta-T ADPCM unit */ YM_DELTAT_savestate(device,&F2608->deltaT); } #endif /* _STATE_H */ static void YM2608_deltat_status_set(void *chip, UINT8 changebits) { YM2608 *F2608 = (YM2608 *)chip; FM_STATUS_SET(&(F2608->OPN.ST), changebits); } static void YM2608_deltat_status_reset(void *chip, UINT8 changebits) { YM2608 *F2608 = (YM2608 *)chip; FM_STATUS_RESET(&(F2608->OPN.ST), changebits); } /* YM2608(OPNA) */ //void * ym2608_init(void *param, const device_config *device, int clock, int rate, // void *pcmrom,int pcmsize, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) void * ym2608_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2608 *F2608; /* allocate extend state space */ if( (F2608 = (YM2608 *)malloc(sizeof(YM2608)))==NULL) return NULL; /* clear */ memset(F2608,0,sizeof(YM2608)); /* allocate total level table (128kb space) */ if( !init_tables() ) { free( F2608 ); return NULL; } F2608->OPN.ST.param = param; F2608->OPN.type = TYPE_YM2608; F2608->OPN.P_CH = F2608->CH; //F2608->OPN.ST.device = device; F2608->OPN.ST.clock = clock; F2608->OPN.ST.rate = rate; /* External handlers */ F2608->OPN.ST.timer_handler = timer_handler; F2608->OPN.ST.IRQ_Handler = IRQHandler; F2608->OPN.ST.SSG = ssg; /* DELTA-T */ //F2608->deltaT.memory = (UINT8 *)pcmrom; //F2608->deltaT.memory_size = pcmsize; F2608->deltaT.memory = NULL; F2608->deltaT.memory_size = 0x00; F2608->deltaT.memory_mask = 0x00; /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */ /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */ F2608->deltaT.status_set_handler = YM2608_deltat_status_set; F2608->deltaT.status_reset_handler = YM2608_deltat_status_reset; F2608->deltaT.status_change_which_chip = F2608; F2608->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */ F2608->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */ F2608->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 miliseconds while recording the ADPCM */ /* ADPCM Rhythm */ F2608->pcmbuf = (UINT8*)YM2608_ADPCM_ROM; F2608->pcm_size = 0x2000; Init_ADPCMATable(); #ifdef __STATE_H__ YM2608_save_state(F2608, device); #endif return F2608; } /* shut down emulator */ void ym2608_shutdown(void *chip) { YM2608 *F2608 = (YM2608 *)chip; free(F2608->deltaT.memory); F2608->deltaT.memory = NULL; FMCloseTable(); free(F2608); } /* reset one of chips */ void ym2608_reset_chip(void *chip) { int i; YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; YM_DELTAT *DELTAT = &F2608->deltaT; /* Reset Prescaler */ OPNPrescaler_w(OPN , 0 , 2); F2608->deltaT.freqbase = OPN->ST.freqbase; /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_BUSY_CLEAR(&OPN->ST); /* register 0x29 - default value after reset is: enable only 3 FM channels and enable all the status flags */ YM2608IRQMaskWrite(OPN, F2608, 0x1f ); /* default value for D4-D0 is 1 */ /* register 0x10, A1=1 - default value is 1 for D4, D3, D2, 0 for the rest */ YM2608IRQFlagWrite(OPN, F2608, 0x1c ); /* default: enable timer A and B, disable EOS, BRDY and ZERO */ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2608->CH , 6 ); /* reset OPerator paramater */ for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); /* ADPCM - percussion sounds */ for( i = 0; i < 6; i++ ) { if (i<=3) /* channels 0,1,2,3 */ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); else /* channels 4 and 5 work with slower clock */ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/6.0); F2608->adpcm[i].start = YM2608_ADPCM_ROM_addr[i*2]; F2608->adpcm[i].end = YM2608_ADPCM_ROM_addr[i*2+1]; F2608->adpcm[i].now_addr = 0; F2608->adpcm[i].now_step = 0; /* F2608->adpcm[i].delta = 21866; */ F2608->adpcm[i].vol_mul = 0; F2608->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ F2608->adpcm[i].flagMask = 0; F2608->adpcm[i].flag = 0; F2608->adpcm[i].adpcm_acc = 0; F2608->adpcm[i].adpcm_step= 0; F2608->adpcm[i].adpcm_out = 0; } F2608->adpcmTL = 0x3f; F2608->adpcm_arrivedEndAddress = 0; /* not used */ /* DELTA-T unit */ DELTAT->freqbase = OPN->ST.freqbase; DELTAT->output_pointer = OPN->out_delta; DELTAT->portshift = 5; /* always 5bits shift */ /* ASG */ DELTAT->output_range = 1<<23; YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_NORMAL); } /* YM2608 write */ /* n = number */ /* a = address */ /* v = value */ int ym2608_write(void *chip, int a,UINT8 v) { YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; int addr; v &= 0xff; /*adjust to 8 bit bus */ switch(a&3) { case 0: /* address port 0 */ OPN->ST.address = v; F2608->addr_A1 = 0; /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); /* prescaler selecter : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) { OPNPrescaler_w(OPN , v , 2); //TODO: set ADPCM[c].step F2608->deltaT.freqbase = OPN->ST.freqbase; } break; case 1: /* data port 0 */ if (F2608->addr_A1 != 0) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2608->REGS[addr] = v; switch(addr & 0xf0) { case 0x00: /* SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* 0x10-0x1f : Rhythm section */ ym2608_update_req(OPN->ST.param); FM_ADPCMAWrite(F2608,addr-0x10,v); break; case 0x20: /* Mode Register */ switch(addr) { case 0x29: /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ YM2608IRQMaskWrite(OPN, F2608, v); break; default: ym2608_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); } break; default: /* OPN section */ ym2608_update_req(OPN->ST.param); OPNWriteReg(OPN,addr,v); } break; case 2: /* address port 1 */ OPN->ST.address = v; F2608->addr_A1 = 1; break; case 3: /* data port 1 */ if (F2608->addr_A1 != 1) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2608->REGS[addr | 0x100] = v; ym2608_update_req(OPN->ST.param); switch( addr & 0xf0 ) { case 0x00: /* DELTAT PORT */ switch( addr ) { case 0x0e: /* DAC data */ #ifdef _DEBUG logerror("YM2608: write to DAC data (unimplemented) value=%02x\n",v); #endif break; default: /* 0x00-0x0d */ YM_DELTAT_ADPCM_Write(&F2608->deltaT,addr,v); } break; case 0x10: /* IRQ Flag control */ if( addr == 0x10 ) { YM2608IRQFlagWrite(OPN, F2608, v); } break; default: OPNWriteReg(OPN,addr | 0x100,v); } } return OPN->ST.irq; } UINT8 ym2608_read(void *chip,int a) { YM2608 *F2608 = (YM2608 *)chip; int addr = F2608->OPN.ST.address; UINT8 ret = 0; switch( a&3 ) { case 0: /* status 0 : YM2203 compatible */ /* BUSY:x:x:x:x:x:FLAGB:FLAGA */ ret = FM_STATUS_FLAG(&F2608->OPN.ST) & 0x83; break; case 1: /* status 0, ID */ if( addr < 16 ) ret = (*F2608->OPN.ST.SSG->read)(F2608->OPN.ST.param); else if(addr == 0xff) ret = 0x01; /* ID code */ break; case 2: /* status 1 : status 0 + ADPCM status */ /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */ ret = (FM_STATUS_FLAG(&F2608->OPN.ST) & (F2608->flagmask|0x80)) | ((F2608->deltaT.PCM_BSY & 1)<<5) ; break; case 3: if(addr == 0x08) { ret = YM_DELTAT_ADPCM_Read(&F2608->deltaT); } else { if(addr == 0x0f) { #ifdef _DEBUG logerror("YM2608 A/D conversion is accessed but not implemented !\n"); #endif ret = 0x80; /* 2's complement PCM data - result from A/D conversion */ } } break; } return ret; } int ym2608_timer_over(void *chip,int c) { YM2608 *F2608 = (YM2608 *)chip; switch(c) { #if 0 case 2: { /* BUFRDY flag */ YM_DELTAT_BRDY_callback( &F2608->deltaT ); } break; #endif case 1: { /* Timer B */ TimerBOver( &(F2608->OPN.ST) ); } break; case 0: { /* Timer A */ ym2608_update_req(F2608->OPN.ST.param); /* timer update */ TimerAOver( &(F2608->OPN.ST) ); /* CSM mode key,TL controll */ if( F2608->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( F2608->OPN.type, &(F2608->CH[2]) ); } } break; default: break; } return F2608->OPN.ST.irq; } void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YM2608 *F2608 = (YM2608 *)chip; switch(rom_id) { case 0x01: // ADPCM // unused, it's constant break; case 0x02: // DELTA-T if (F2608->deltaT.memory_size != ROMSize) { F2608->deltaT.memory = (UINT8*)realloc(F2608->deltaT.memory, ROMSize); F2608->deltaT.memory_size = ROMSize; memset(F2608->deltaT.memory, 0xFF, ROMSize); YM_DELTAT_calc_mem_mask(&F2608->deltaT); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2608->deltaT.memory + DataStart, ROMData, DataLength); break; } return; } void ym2608_set_mutemask(void *chip, UINT32 MuteMask) { YM2608 *F2608 = (YM2608 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) F2608->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 6; CurChn ++) F2608->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01; F2608->MuteDeltaT = (MuteMask >> 12) & 0x01; return; } #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* YM2610(OPNB) */ /* Generate samples for one of the YM2610s */ void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[4]; INT32 *out_fm = OPN->out_fm; /* buffer setup */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2610->CH[1]; cch[1] = &F2610->CH[2]; cch[2] = &F2610->CH[4]; cch[3] = &F2610->CH[5]; #ifdef YM2610B_WARNING #define FM_KEY_IS(SLOT) ((SLOT)->key) #define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n" /* Check YM2610B warning message */ if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) ) { LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0)); FM_KEY_IS(&F2610->CH[0].SLOT[3]) = 0; } if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) ) { LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3)); FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0; } #endif /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[1]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT4] , cch[1]->fc , cch[1]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[1] ); refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[1] = 0; out_fm[2] = 0; out_fm[4] = 0; out_fm[5] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 1 ); /*remapped to 1*/ chan_calc(OPN, cch[1], 2 ); /*remapped to 2*/ chan_calc(OPN, cch[2], 4 ); /*remapped to 4*/ chan_calc(OPN, cch[3], 5 ); /*remapped to 5*/ /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2610->adpcm[j].flag ) ADPCMA_calc_chan( F2610, &F2610->adpcm[j]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[1]>>1) & OPN->pan[2]); // the shift right was verified on real chip rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; //Limit( lt, MAXOUT, MINOUT ); //Limit( rt, MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = rt; } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[1] ) } INTERNAL_TIMER_B(&OPN->ST,length) } #if BUILD_YM2610B /* Generate samples for one of the YM2610Bs */ void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[6]; INT32 *out_fm = OPN->out_fm; /* buffer setup */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2610->CH[0]; cch[1] = &F2610->CH[1]; cch[2] = &F2610->CH[2]; cch[3] = &F2610->CH[3]; cch[4] = &F2610->CH[4]; cch[5] = &F2610->CH[5]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); refresh_fc_eg_chan( OPN, cch[4] ); refresh_fc_eg_chan( OPN, cch[5] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[0] = 0; out_fm[1] = 0; out_fm[2] = 0; out_fm[3] = 0; out_fm[4] = 0; out_fm[5] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); chan_calc(OPN, cch[3], 3 ); chan_calc(OPN, cch[4], 4 ); chan_calc(OPN, cch[5], 5 ); /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2610->adpcm[j].flag ) ADPCMA_calc_chan( F2610, &F2610->adpcm[j]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[0]>>1) & OPN->pan[0]); // the shift right is verified on YM2610 rt += ((out_fm[0]>>1) & OPN->pan[1]); lt += ((out_fm[1]>>1) & OPN->pan[2]); rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[3]>>1) & OPN->pan[6]); rt += ((out_fm[3]>>1) & OPN->pan[7]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[0] & OPN->pan[0]); rt += (out_fm[0] & OPN->pan[1]); lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[3] & OPN->pan[6]); rt += (out_fm[3] & OPN->pan[7]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; //Limit( lt, MAXOUT, MINOUT ); //Limit( rt, MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = rt; } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[2] ) } INTERNAL_TIMER_B(&OPN->ST,length) } #endif /* BUILD_YM2610B */ #ifdef __STATE_H__ void ym2610_postload(void *chip) { if (chip) { YM2610 *F2610 = (YM2610 *)chip; int r; /* SSG registers */ for(r=0;r<16;r++) { (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,0,r); (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,1,F2610->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) { OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); } /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) { OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); } /* FM channels */ /*FM_channel_postload(F2610->CH,6);*/ /* rhythm(ADPCMA) */ FM_ADPCMAWrite(F2610,1,F2610->REGS[0x101]); for( r=0 ; r<6 ; r++) { FM_ADPCMAWrite(F2610,r+0x08,F2610->REGS[r+0x108]); FM_ADPCMAWrite(F2610,r+0x10,F2610->REGS[r+0x110]); FM_ADPCMAWrite(F2610,r+0x18,F2610->REGS[r+0x118]); FM_ADPCMAWrite(F2610,r+0x20,F2610->REGS[r+0x120]); FM_ADPCMAWrite(F2610,r+0x28,F2610->REGS[r+0x128]); } /* Delta-T ADPCM unit */ YM_DELTAT_postload(&F2610->deltaT , &F2610->REGS[0x010] ); } } static void YM2610_save_state(YM2610 *F2610, const device_config *device) { state_save_register_device_item_array(device, 0, F2610->REGS); FMsave_state_st(device,&F2610->OPN.ST); FMsave_state_channel(device,F2610->CH,6); /* 3slots */ state_save_register_device_item_array(device, 0, F2610->OPN.SL3.fc); state_save_register_device_item(device, 0, F2610->OPN.SL3.fn_h); state_save_register_device_item_array(device, 0, F2610->OPN.SL3.kcode); /* address register1 */ state_save_register_device_item(device, 0, F2610->addr_A1); state_save_register_device_item(device, 0, F2610->adpcm_arrivedEndAddress); /* rythm(ADPCMA) */ FMsave_state_adpcma(device,F2610->adpcm); /* Delta-T ADPCM unit */ YM_DELTAT_savestate(device,&F2610->deltaT); } #endif /* _STATE_H */ static void YM2610_deltat_status_set(void *chip, UINT8 changebits) { YM2610 *F2610 = (YM2610 *)chip; F2610->adpcm_arrivedEndAddress |= changebits; } static void YM2610_deltat_status_reset(void *chip, UINT8 changebits) { YM2610 *F2610 = (YM2610 *)chip; F2610->adpcm_arrivedEndAddress &= (~changebits); } //void *ym2610_init(void *param, const device_config *device, int clock, int rate, // void *pcmroma,int pcmsizea,void *pcmromb,int pcmsizeb, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) void *ym2610_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2610 *F2610; /* allocate extend state space */ if( (F2610 = (YM2610 *)malloc(sizeof(YM2610)))==NULL) return NULL; /* clear */ memset(F2610,0,sizeof(YM2610)); /* allocate total level table (128kb space) */ if( !init_tables() ) { free( F2610 ); return NULL; } /* FM */ F2610->OPN.ST.param = param; F2610->OPN.type = TYPE_YM2610; F2610->OPN.P_CH = F2610->CH; //F2610->OPN.ST.device = device; F2610->OPN.ST.clock = clock; F2610->OPN.ST.rate = rate; /* Extend handler */ F2610->OPN.ST.timer_handler = timer_handler; F2610->OPN.ST.IRQ_Handler = IRQHandler; F2610->OPN.ST.SSG = ssg; /* ADPCM */ //F2610->pcmbuf = (const UINT8 *)pcmroma; //F2610->pcm_size = pcmsizea; F2610->pcmbuf = NULL; F2610->pcm_size = 0x00; /* DELTA-T */ //F2610->deltaT.memory = (UINT8 *)pcmromb; //F2610->deltaT.memory_size = pcmsizeb; F2610->deltaT.memory = NULL; F2610->deltaT.memory_size = 0x00; F2610->deltaT.memory_mask = 0x00; F2610->deltaT.status_set_handler = YM2610_deltat_status_set; F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset; F2610->deltaT.status_change_which_chip = F2610; F2610->deltaT.status_change_EOS_bit = 0x80; /* status flag: set bit7 on End Of Sample */ Init_ADPCMATable(); #ifdef __STATE_H__ YM2610_save_state(F2610, device); #endif return F2610; } /* shut down emulator */ void ym2610_shutdown(void *chip) { YM2610 *F2610 = (YM2610 *)chip; free(F2610->pcmbuf); F2610->pcmbuf = NULL; free(F2610->deltaT.memory); F2610->deltaT.memory = NULL; FMCloseTable(); free(F2610); } /* reset one of chip */ void ym2610_reset_chip(void *chip) { int i; YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; /*astring name; device_t* dev = F2610->OPN.ST.device;*/ /* setup PCM buffers again */ /*name.printf("%s",dev->tag()); F2610->pcmbuf = (const UINT8 *)dev->machine->region(name)->base(); F2610->pcm_size = dev->machine->region(name)->bytes(); name.printf("%s.deltat",dev->tag()); F2610->deltaT.memory = (UINT8 *)dev->machine->region(name)->base(); if(F2610->deltaT.memory == NULL) { F2610->deltaT.memory = (UINT8*)F2610->pcmbuf; F2610->deltaT.memory_size = F2610->pcm_size; } else F2610->deltaT.memory_size = dev->machine->region(name)->bytes();*/ /* Reset Prescaler */ OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */ /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); FM_BUSY_CLEAR(&OPN->ST); OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2610->CH , 6 ); /* reset OPerator paramater */ for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); /**** ADPCM work initial ****/ for( i = 0; i < 6 ; i++ ) { F2610->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); F2610->adpcm[i].now_addr = 0; F2610->adpcm[i].now_step = 0; F2610->adpcm[i].start = 0; F2610->adpcm[i].end = 0; /* F2610->adpcm[i].delta = 21866; */ F2610->adpcm[i].vol_mul = 0; F2610->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ F2610->adpcm[i].flagMask = 1<adpcm[i].flag = 0; F2610->adpcm[i].adpcm_acc = 0; F2610->adpcm[i].adpcm_step= 0; F2610->adpcm[i].adpcm_out = 0; } F2610->adpcmTL = 0x3f; F2610->adpcm_arrivedEndAddress = 0; /* DELTA-T unit */ DELTAT->freqbase = OPN->ST.freqbase; DELTAT->output_pointer = OPN->out_delta; DELTAT->portshift = 8; /* allways 8bits shift */ DELTAT->output_range = 1<<23; YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_YM2610); } /* YM2610 write */ /* n = number */ /* a = address */ /* v = value */ int ym2610_write(void *chip, int a, UINT8 v) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; int addr; int ch; v &= 0xff; /* adjust to 8 bit bus */ switch( a&3 ) { case 0: /* address port 0 */ OPN->ST.address = v; F2610->addr_A1 = 0; /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); break; case 1: /* data port 0 */ if (F2610->addr_A1 != 0) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2610->REGS[addr] = v; switch(addr & 0xf0) { case 0x00: /* SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* DeltaT ADPCM */ ym2610_update_req(OPN->ST.param); switch(addr) { case 0x10: /* control 1 */ case 0x11: /* control 2 */ case 0x12: /* start address L */ case 0x13: /* start address H */ case 0x14: /* stop address L */ case 0x15: /* stop address H */ case 0x19: /* delta-n L */ case 0x1a: /* delta-n H */ case 0x1b: /* volume */ { YM_DELTAT_ADPCM_Write(&F2610->deltaT,addr-0x10,v); } break; case 0x1c: /* FLAG CONTROL : Extend Status Clear/Mask */ { UINT8 statusmask = ~v; /* set arrived flag mask */ for(ch=0;ch<6;ch++) F2610->adpcm[ch].flagMask = statusmask&(1<deltaT.status_change_EOS_bit = statusmask & 0x80; /* status flag: set bit7 on End Of Sample */ /* clear arrived flag */ F2610->adpcm_arrivedEndAddress &= statusmask; } break; default: #ifdef _DEBUG logerror("YM2610: write to unknown deltat register %02x val=%02x\n",addr,v); #endif break; } break; case 0x20: /* Mode Register */ ym2610_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); break; default: /* OPN section */ ym2610_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } break; case 2: /* address port 1 */ OPN->ST.address = v; F2610->addr_A1 = 1; break; case 3: /* data port 1 */ if (F2610->addr_A1 != 1) break; /* verified on real YM2608 */ ym2610_update_req(OPN->ST.param); addr = OPN->ST.address; F2610->REGS[addr | 0x100] = v; if( addr < 0x30 ) /* 100-12f : ADPCM A section */ FM_ADPCMAWrite(F2610,addr,v); else OPNWriteReg(OPN,addr | 0x100,v); } return OPN->ST.irq; } UINT8 ym2610_read(void *chip,int a) { YM2610 *F2610 = (YM2610 *)chip; int addr = F2610->OPN.ST.address; UINT8 ret = 0; switch( a&3) { case 0: /* status 0 : YM2203 compatible */ ret = FM_STATUS_FLAG(&F2610->OPN.ST) & 0x83; break; case 1: /* data 0 */ if( addr < 16 ) ret = (*F2610->OPN.ST.SSG->read)(F2610->OPN.ST.param); if( addr == 0xff ) ret = 0x01; break; case 2: /* status 1 : ADPCM status */ /* ADPCM STATUS (arrived End Address) */ /* B,--,A5,A4,A3,A2,A1,A0 */ /* B = ADPCM-B(DELTA-T) arrived end address */ /* A0-A5 = ADPCM-A arrived end address */ ret = F2610->adpcm_arrivedEndAddress; break; case 3: ret = 0; break; } return ret; } int ym2610_timer_over(void *chip,int c) { YM2610 *F2610 = (YM2610 *)chip; if( c ) { /* Timer B */ TimerBOver( &(F2610->OPN.ST) ); } else { /* Timer A */ ym2610_update_req(F2610->OPN.ST.param); /* timer update */ TimerAOver( &(F2610->OPN.ST) ); /* CSM mode key,TL controll */ if( F2610->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( F2610->OPN.type, &(F2610->CH[2]) ); } } return F2610->OPN.ST.irq; } void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YM2610 *F2610 = (YM2610 *)chip; switch(rom_id) { case 0x01: // ADPCM if (F2610->pcm_size != ROMSize) { F2610->pcmbuf = (UINT8*)realloc(F2610->pcmbuf, ROMSize); F2610->pcm_size = ROMSize; memset(F2610->pcmbuf, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2610->pcmbuf + DataStart, ROMData, DataLength); break; case 0x02: // DELTA-T if (F2610->deltaT.memory_size != ROMSize) { F2610->deltaT.memory = (UINT8*)realloc(F2610->deltaT.memory, ROMSize); F2610->deltaT.memory_size = ROMSize; memset(F2610->deltaT.memory, 0xFF, ROMSize); YM_DELTAT_calc_mem_mask(&F2610->deltaT); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2610->deltaT.memory + DataStart, ROMData, DataLength); break; } return; } void ym2610_set_mutemask(void *chip, UINT32 MuteMask) { YM2610 *F2610 = (YM2610 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) F2610->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 6; CurChn ++) F2610->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01; F2610->MuteDeltaT = (MuteMask >> 12) & 0x01; return; } #endif /* (BUILD_YM2610||BUILD_YM2610B) */ ================================================ FILE: VGMPlay/chips/fm.h ================================================ /* File: fm.h -- header file for software emulation for FM sound generator */ #pragma once /* --- select emulation chips --- */ /* #define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator #define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator #define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator #define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator #define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator #define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator */ #define BUILD_YM2203 1 #define BUILD_YM2608 1 #define BUILD_YM2610 1 #define BUILD_YM2610B 1 #define BUILD_YM2612 1 #define BUILD_YM3438 1 /* select bit size of output : 8 or 16 */ #define FM_SAMPLE_BITS 16 /* select timer system internal or external */ #define FM_INTERNAL_TIMER 1 /* --- speedup optimize --- */ /* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ //#define FM_BUSY_FLAG_SUPPORT 1 /* --- external SSG(YM2149/AY-3-8910)emulator interface port */ /* used by YM2203,YM2608,and YM2610 */ typedef struct _ssg_callbacks ssg_callbacks; struct _ssg_callbacks { void (*set_clock)(void *param, int clock); void (*write)(void *param, int address, int data); int (*read)(void *param); void (*reset)(void *param); }; /* --- external callback funstions for realtime update --- */ #if FM_BUSY_FLAG_SUPPORT #define TIME_TYPE attotime #define UNDEFINED_TIME attotime_zero #define FM_GET_TIME_NOW(machine) timer_get_time(machine) #define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) #define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) #define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) #endif #if BUILD_YM2203 /* in 2203intf.c */ void ym2203_update_request(void *param); #define ym2203_update_req(chip) ym2203_update_request(chip) #endif /* BUILD_YM2203 */ #if BUILD_YM2608 /* in 2608intf.c */ void ym2608_update_request(void *param); #define ym2608_update_req(chip) ym2608_update_request(chip); #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* in 2610intf.c */ void ym2610_update_request(void *param); #define ym2610_update_req(chip) ym2610_update_request(chip); #endif /* (BUILD_YM2610||BUILD_YM2610B) */ #if (BUILD_YM2612||BUILD_YM3438) /* in 2612intf.c */ void ym2612_update_request(void *param); #define ym2612_update_req(chip) ym2612_update_request(chip); #endif /* (BUILD_YM2612||BUILD_YM3438) */ /* compiler dependence */ #if 0 #ifndef OSD_CPU_H #define OSD_CPU_H typedef unsigned char UINT8; /* unsigned 8bit */ typedef unsigned short UINT16; /* unsigned 16bit */ typedef unsigned int UINT32; /* unsigned 32bit */ typedef signed char INT8; /* signed 8bit */ typedef signed short INT16; /* signed 16bit */ typedef signed int INT32; /* signed 32bit */ #endif /* OSD_CPU_H */ #endif typedef stream_sample_t FMSAMPLE; /* #if (FM_SAMPLE_BITS==16) typedef INT16 FMSAMPLE; #endif #if (FM_SAMPLE_BITS==8) typedef unsigned char FMSAMPLE; #endif */ typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); typedef void (*FM_IRQHANDLER)(void *param,int irq); /* FM_TIMERHANDLER : Stop or Start timer */ /* int n = chip number */ /* int c = Channel 0=TimerA,1=TimerB */ /* int count = timer count (0=stop) */ /* doube stepTime = step time of one count (sec.)*/ /* FM_IRQHHANDLER : IRQ level changing sense */ /* int n = chip number */ /* int irq = IRQ level 0=OFF,1=ON */ #if BUILD_YM2203 /* -------------------- YM2203(OPN) Interface -------------------- */ /* ** Initialize YM2203 emulator(s). ** ** 'num' is the number of virtual YM2203's to allocate ** 'baseclock' ** 'rate' is sampling rate ** 'TimerHandler' timer callback handler when timer start and clear ** 'IRQHandler' IRQ callback handler when changed IRQ level ** return 0 = success */ //void * ym2203_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void * ym2203_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); /* ** shutdown the YM2203 emulators */ void ym2203_shutdown(void *chip); /* ** reset all chip registers for YM2203 number 'num' */ void ym2203_reset_chip(void *chip); /* ** update one of chip */ void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length); /* ** Write ** return : InterruptLevel */ int ym2203_write(void *chip,int a,unsigned char v); /* ** Read ** return : InterruptLevel */ unsigned char ym2203_read(void *chip,int a); /* ** Timer OverFlow */ int ym2203_timer_over(void *chip, int c); /* ** State Save */ void ym2203_postload(void *chip); void ym2203_set_mutemask(void *chip, UINT32 MuteMask); #endif /* BUILD_YM2203 */ #if BUILD_YM2608 /* -------------------- YM2608(OPNA) Interface -------------------- */ //void * ym2608_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmsizea, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void * ym2608_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2608_shutdown(void *chip); void ym2608_reset_chip(void *chip); void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2608_write(void *chip, int a,unsigned char v); unsigned char ym2608_read(void *chip,int a); int ym2608_timer_over(void *chip, int c ); void ym2608_postload(void *chip); void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2608_set_mutemask(void *chip, UINT32 MuteMask); #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* -------------------- YM2610(OPNB) Interface -------------------- */ //void * ym2610_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmasize,void *pcmromb,int pcmbsize, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void * ym2610_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2610_shutdown(void *chip); void ym2610_reset_chip(void *chip); void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length); #if BUILD_YM2610B void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length); #endif /* BUILD_YM2610B */ int ym2610_write(void *chip, int a,unsigned char v); unsigned char ym2610_read(void *chip,int a); int ym2610_timer_over(void *chip, int c ); void ym2610_postload(void *chip); void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2610_set_mutemask(void *chip, UINT32 MuteMask); #endif /* (BUILD_YM2610||BUILD_YM2610B) */ #if (BUILD_YM2612||BUILD_YM3438) //void * ym2612_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); void * ym2612_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); void ym2612_shutdown(void *chip); void ym2612_reset_chip(void *chip); void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2612_write(void *chip, int a,unsigned char v); unsigned char ym2612_read(void *chip,int a); int ym2612_timer_over(void *chip, int c ); void ym2612_postload(void *chip); void ym2612_set_mutemask(void *chip, UINT32 MuteMask); void ym2612_setoptions(UINT8 Flags); #endif /* (BUILD_YM2612||BUILD_YM3438) */ ================================================ FILE: VGMPlay/chips/fm2612.c ================================================ /* ** ** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator ** Split from fm.c to keep 2612 fixes from infecting other OPN chips ** ** Copyright Jarek Burczynski (bujar at mame dot net) ** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** ** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368) ** */ /* ** History: ** ** 2006~2012 Eke-Eke (Genesis Plus GX): ** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware ** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 ** ** TODO: ** ** - core documentation ** - BUSY flag support ** ** CHANGELOG: ** ** - fixed LFO implementation: ** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) ** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) ** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. ** - improved internal timers emulation ** - adjusted lowest EG rates increment values ** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) ** - fixed EG behavior when Attack Rate is maximal ** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation ** - implemented EG output immediate changes on register writes ** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B ** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) ** - implemented accurate CSM mode emulation ** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) ** - implemented accurate address/data ports behavior ** ** 06-23-2007 Zsolt Vasvari: ** - changed the timing not to require the use of floating point calculations ** ** 03-08-2003 Jarek Burczynski: ** - fixed YM2608 initial values (after the reset) ** - fixed flag and irqmask handling (YM2608) ** - fixed BUFRDY flag handling (YM2608) ** ** 14-06-2003 Jarek Burczynski: ** - implemented all of the YM2608 status register flags ** - implemented support for external memory read/write via YM2608 ** - implemented support for deltat memory limit register in YM2608 emulation ** ** 22-05-2003 Jarek Burczynski: ** - fixed LFO PM calculations (copy&paste bugfix) ** ** 08-05-2003 Jarek Burczynski: ** - fixed SSG support ** ** 22-04-2003 Jarek Burczynski: ** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) ** ** 15-04-2003 Jarek Burczynski: ** - added support for YM2608's register 0x110 - status mask ** ** 01-12-2002 Jarek Burczynski: ** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) ** The addressing patch used for early Neo-Geo games can be removed now. ** ** 26-11-2002 Jarek Burczynski, Nicola Salmoria: ** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: ** - added emulation of YM2608 drums. ** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) ** ** 16-08-2002 Jarek Burczynski: ** - binary exact Envelope Generator (verified on real YM2203); ** identical to YM2151 ** - corrected 'off by one' error in feedback calculations (when feedback is off) ** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) ** ** 18-12-2001 Jarek Burczynski: ** - added SSG-EG support (verified on real YM2203) ** ** 12-08-2001 Jarek Burczynski: ** - corrected sin_tab and tl_tab data (verified on real chip) ** - corrected feedback calculations (verified on real chip) ** - corrected phase generator calculations (verified on real chip) ** - corrected envelope generator calculations (verified on real chip) ** - corrected FM volume level (YM2610 and YM2610B). ** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : ** this was needed to calculate YM2610 FM channels output correctly. ** (Each FM channel is calculated as in other chips, but the output of the channel ** gets shifted right by one *before* sending to accumulator. That was impossible to do ** with previous implementation). ** ** 23-07-2001 Jarek Burczynski, Nicola Salmoria: ** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) ** ** 11-06-2001 Jarek Burczynski: ** - corrected end of sample bug in ADPCMA_calc_cha(). ** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). ** ** 08-12-98 hiro-shi: ** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA ** move ROM limit check.(CALC_CH? -> 2610Write1/2) ** test program (ADPCMB_TEST) ** move ADPCM A/B end check. ** ADPCMB repeat flag(no check) ** change ADPCM volume rate (8->16) (32->48). ** ** 09-12-98 hiro-shi: ** change ADPCM volume. (8->16, 48->64) ** replace ym2610 ch0/3 (YM-2610B) ** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. ** add ADPCM_SHIFT_MASK ** change ADPCMA_DECODE_MIN/MAX. */ /************************************************************************/ /* comment of hiro-shi(Hiromitsu Shioya) */ /* YM2610(B) = OPN-B */ /* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /************************************************************************/ //#include "emu.h" #include #include // for memset #include // for NULL #include #include "mamedef.h" #include "fm.h" /* shared function building option */ #define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) #define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) /* globals */ #define TYPE_SSG 0x01 /* SSG support */ #define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ #define TYPE_6CH 0x04 /* FM 6CH / 3CH */ #define TYPE_DAC 0x08 /* YM2612's DAC device */ #define TYPE_ADPCM 0x10 /* two ADPCM units */ #define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ #define TYPE_YM2203 (TYPE_SSG) #define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) #define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) #define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH) /* globals */ #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>3) /* sin waveform table in 'decibel' scale */ static unsigned int sin_tab[SIN_LEN]; /* sustain level table (3dB per step) */ /* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ /* 1, 2, 4, 8, 16, 32, 64 (value)*/ /* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ /* attenuation value (10 bits) = (SL << 2) << 3 */ #define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) static const UINT32 sl_table[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; #undef SC #define RATE_STEPS (8) static const UINT8 eg_inc[19*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ /*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ /*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ /*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ /*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ /*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ /*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(17) in this table - it's directly in the code */ static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates (same as Rate 0) */ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), /* rates 00-11 */ /* O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), */ O(18),O(18),O( 0),O( 0), O( 0),O( 0),O( 2),O( 2), // Nemesis's tests O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 12 */ O( 4),O( 5),O( 6),O( 7), /* rate 13 */ O( 8),O( 9),O(10),O(11), /* rate 14 */ O(12),O(13),O(14),O(15), /* rate 15 */ O(16),O(16),O(16),O(16), /* 32 dummy rates (same as 15 3) */ O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ /*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ /*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ #define O(a) (a*1) static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ /* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ /* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), /* rates 00-11 */ O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 12 */ O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 32 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) }; #undef O static const UINT8 dt_tab[4 * 32]={ /* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ /* FD=0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* FD=1 */ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, /* FD=2 */ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, /* FD=3 */ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 }; /* OPN key frequency number -> key code follow table */ /* fnum higher 4bit -> keycode lower 2bit */ static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; /* 8 LFO speed parameters */ /* each value represents number of samples that one LFO level will last for */ static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; /*There are 4 different LFO AM depths available, they are: 0 dB, 1.4 dB, 5.9 dB, 11.8 dB Here is how it is generated (in EG steps): 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 (1.4 dB is losing precision as you can see) It's implemented as generator from 0..126 with step 2 then a shift right N times, where N is: 8 for 0 dB 3 for 1.4 dB 1 for 5.9 dB 0 for 11.8 dB */ static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; /*There are 8 different LFO PM depths available, they are: 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 (bits 8,9,10 = FNUM MSB from OCT/FNUM register) Here we store only first quarter (positive one) of full waveform. Full table (lfo_pm_table) containing all 128 waveforms is build at run (init) time. One value in table below represents 4 (four) basic LFO steps (1 PM step = 4 AM steps). For example: at LFO SPEED=0 (which is 108 samples per basic LFO step) one value from "lfo_pm_output" table lasts for 432 consecutive samples (4*108=432) and one full LFO waveform cycle lasts for 13824 samples (32*432=13824; 32 because we store only a quarter of whole waveform in the table below) */ static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ /* FNUM BIT 4: 000 0001xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* FNUM BIT 5: 000 0010xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* FNUM BIT 6: 000 0100xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, /* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* FNUM BIT 7: 000 1000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, /* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, /* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* FNUM BIT 8: 001 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, /* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, /* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, /* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* FNUM BIT 9: 010 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, /* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, /* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, /* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, /* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* FNUM BIT10: 100 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, /* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, /* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, /* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, /* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, }; /* all 128 LFO PM waveforms */ static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ /* register number to channel number , slot offset */ #define OPN_CHAN(N) (N&3) #define OPN_SLOT(N) ((N>>2)&3) /* slot number */ #define SLOT1 0 #define SLOT2 2 #define SLOT3 1 #define SLOT4 3 /* bit0 = Right enable , bit1 = Left enable */ #define OUTD_RIGHT 1 #define OUTD_LEFT 2 #define OUTD_CENTER 3 /* save output as raw 16-bit sample */ /* #define SAVE_SAMPLE */ #ifdef SAVE_SAMPLE static FILE *sample[1]; #if 1 /*save to MONO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = rt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif /* struct describing a single operator (SLOT) */ typedef struct { INT32 *DT; /* detune :dt_tab[DT] */ UINT8 KSR; /* key scale rate :3-KSR */ UINT32 ar; /* attack rate */ UINT32 d1r; /* decay rate */ UINT32 d2r; /* sustain rate */ UINT32 rr; /* release rate */ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ UINT32 mul; /* multiple :ML_TABLE[ML] */ /* Phase Generator */ UINT32 phase; /* phase counter */ INT32 Incr; /* phase step */ /* Envelope Generator */ UINT8 state; /* phase type */ UINT32 tl; /* total level: TL << 3 */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level:sl_table[SL] */ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT8 eg_sh_d1r; /* (decay state) */ UINT8 eg_sel_d1r; /* (decay state) */ UINT8 eg_sh_d2r; /* (sustain state) */ UINT8 eg_sel_d2r; /* (sustain state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT8 ssg; /* SSG-EG waveform */ UINT8 ssgn; /* SSG-EG negated output */ UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ /* LFO */ UINT32 AMmask; /* AM enable flag */ } FM_SLOT; typedef struct { FM_SLOT SLOT[4]; /* four SLOTs (operators) */ UINT8 ALGO; /* algorithm */ UINT8 FB; /* feedback shift */ INT32 op1_out[2]; /* op1 output for feedback */ INT32 *connect1; /* SLOT1 output pointer */ INT32 *connect3; /* SLOT3 output pointer */ INT32 *connect2; /* SLOT2 output pointer */ INT32 *connect4; /* SLOT4 output pointer */ INT32 *mem_connect;/* where to put the delayed sample (MEM) */ INT32 mem_value; /* delayed sample (MEM) value */ INT32 pms; /* channel PMS */ UINT8 ams; /* channel AMS */ UINT32 fc; /* fnum,blk:adjusted to sample rate */ UINT8 kcode; /* key code: */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 Muted; } FM_CH; typedef struct { //running_device *device; void * param; /* this chip parameter */ double freqbase; /* frequency base */ int timer_prescaler; /* timer prescaler */ UINT8 irq; /* interrupt level */ UINT8 irqmask; /* irq mask */ #if FM_BUSY_FLAG_SUPPORT TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ #endif UINT32 clock; /* master clock (Hz) */ UINT32 rate; /* sampling rate (Hz) */ UINT8 address; /* address register */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ UINT8 fn_h; /* freq latch */ UINT8 prescaler_sel; /* prescaler selector */ INT32 TA; /* timer a */ INT32 TAC; /* timer a counter */ UINT8 TB; /* timer b */ INT32 TBC; /* timer b counter */ /* local time tables */ INT32 dt_tab[8][32]; /* DeTune table */ /* Extention Timer and IRQ handler */ FM_TIMERHANDLER timer_handler; FM_IRQHANDLER IRQ_Handler; const ssg_callbacks *SSG; } FM_ST; /***********************************************************/ /* OPN unit */ /***********************************************************/ /* OPN 3slot struct */ typedef struct { UINT32 fc[3]; /* fnum3,blk3: calculated */ UINT8 fn_h; /* freq3 latch */ UINT8 kcode[3]; /* key code */ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 key_csm; /* CSM mode Key-ON flag */ } FM_3SLOT; /* OPN/A/B common state */ typedef struct { UINT8 type; /* chip type */ FM_ST ST; /* general state */ FM_3SLOT SL3; /* 3 slot mode state */ FM_CH *P_CH; /* pointer of CH */ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ UINT32 fn_table[4096]; /* fnumber->increment counter */ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ /* LFO */ UINT8 lfo_cnt; /* current LFO phase (out of 128) */ UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ UINT32 lfo_timer_add; /* step of lfo_timer */ UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ UINT32 LFO_AM; /* current LFO AM step */ UINT32 LFO_PM; /* current LFO PM step */ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ INT32 mem; /* one sample delay memory */ INT32 out_fm[6]; /* outputs of working channels */ } FM_OPN; /* here's the virtual YM2612 */ typedef struct { UINT8 REGS[512]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[6]; /* channel state */ UINT8 addr_A1; /* address line A1 */ /* dac output (YM2612) */ //int dacen; UINT8 dacen; UINT8 dac_test; INT32 dacout; UINT8 MuteDAC; UINT8 WaveOutMode; INT32 WaveL; INT32 WaveR; } YM2612; /* log output level */ #define LOG_ERR 3 /* ERROR */ #define LOG_WAR 2 /* WARNING */ #define LOG_INF 1 /* INFORMATION */ #define LOG_LEVEL LOG_INF #ifndef __RAINE__ #define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) #endif /* limitter */ #define Limit(val, max,min) { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \ } extern UINT8 IsVGMInit; static UINT8 PseudoSt = 0x00; /*#include static FILE* hFile; static UINT32 FileSample;*/ /* status set and IRQ handling */ INLINE void FM_STATUS_SET(FM_ST *ST,int flag) { /* set status flag */ ST->status |= flag; if ( !(ST->irq) && (ST->status & ST->irqmask) ) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); } } /* status reset and IRQ handling */ INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) { /* reset status flag */ ST->status &=~flag; if ( (ST->irq) && !(ST->status & ST->irqmask) ) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); } } /* IRQ mask set */ INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) { ST->irqmask = flag; /* IRQ handling check */ FM_STATUS_SET(ST,0); FM_STATUS_RESET(ST,0); } INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; // Note by Valley Bell: // I assume that the CSM mode shouldn't affect channels // other than FM3, so I added a check for it here. if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) { /* restart Phase Generator */ SLOT->phase = 0; /* reset SSG-EG inversion flag */ SLOT->ssgn = 0; if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) { SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; } else { /* force attenuation level to 0 */ SLOT->volume = MIN_ATT_INDEX; /* directly switch to Decay (or Sustain) */ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; } /* recalculate EG output */ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } SLOT->key = 1; } INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) { if (IsVGMInit) // workaround for VGMs trimmed with VGMTool { SLOT->state = EG_OFF; SLOT->volume = MAX_ATT_INDEX; SLOT->vol_out= MAX_ATT_INDEX; } else if (SLOT->state>EG_REL) { SLOT->state = EG_REL; /* phase -> Release */ /* SSG-EG specific update */ if (SLOT->ssg&0x08) { /* convert EG attenuation level */ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) SLOT->volume = (0x200 - SLOT->volume); /* force EG attenuation level */ if (SLOT->volume >= 0x200) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } /* recalculate EG output */ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } } SLOT->key = 0; } INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( !SLOT->key && !OPN->SL3.key_csm) { /* restart Phase Generator */ SLOT->phase = 0; /* reset SSG-EG inversion flag */ SLOT->ssgn = 0; if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) { SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; } else { /* force attenuation level to 0 */ SLOT->volume = MIN_ATT_INDEX; /* directly switch to Decay (or Sustain) */ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; } /* recalculate EG output */ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if (!SLOT->key) { if (IsVGMInit) { SLOT->state = EG_OFF; SLOT->volume = MAX_ATT_INDEX; SLOT->vol_out= MAX_ATT_INDEX; } else if (SLOT->state>EG_REL) { SLOT->state = EG_REL; /* phase -> Release */ /* SSG-EG specific update */ if (SLOT->ssg&0x08) { /* convert EG attenuation level */ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) SLOT->volume = (0x200 - SLOT->volume); /* force EG attenuation level */ if (SLOT->volume >= 0x200) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } /* recalculate EG output */ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } } } /* OPN Mode Register Write */ INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) { /* b7 = CSM MODE */ /* b6 = 3 slot mode */ /* b5 = reset b */ /* b4 = reset a */ /* b3 = timer enable b */ /* b2 = timer enable a */ /* b1 = load b */ /* b0 = load a */ if ((OPN->ST.mode ^ v) & 0xC0) { /* phase increment need to be recalculated */ OPN->P_CH[2].SLOT[SLOT1].Incr=-1; /* CSM mode disabled and CSM key ON active*/ if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm) { /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1); FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2); FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3); FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4); OPN->SL3.key_csm = 0; } } // reset Timer b flag if( v & 0x20 ) FM_STATUS_RESET(ST,0x02); // reset Timer a flag if( v & 0x10 ) FM_STATUS_RESET(ST,0x01); // load b if ((v&2) && !(ST->mode&2)) { ST->TBC = ( 256-ST->TB)<<4; /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); } // load a if ((v&1) && !(ST->mode&1)) { ST->TAC = (1024-ST->TA); /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); ST->TAC *= 4096; } ST->mode = v; } /* Timer A Overflow */ INLINE void TimerAOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); /* clear or reload the counter */ ST->TAC = (1024-ST->TA); if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock); ST->TAC *= 4096; } /* Timer B Overflow */ INLINE void TimerBOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); /* clear or reload the counter */ ST->TBC = ( 256-ST->TB)<<4; if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock); } #if FM_INTERNAL_TIMER /* ----- internal timer mode , update timer */ // Valley Bell: defines fixed /* ---------- calculate timer A ---------- */ #define INTERNAL_TIMER_A(ST,CSM_CH) \ { \ if( (ST)->TAC && ((ST)->timer_handler==0) ) \ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ /* CSM mode total level latch and auto key on */ \ if( (ST)->mode & 0x80 ) \ CSMKeyControll( OPN, CSM_CH ); \ } \ } /* ---------- calculate timer B ---------- */ #define INTERNAL_TIMER_B(ST,step) \ { \ if( (ST)->TBC && ((ST)->timer_handler==0) ) \ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \ } #else /* FM_INTERNAL_TIMER */ /* external timer mode */ #define INTERNAL_TIMER_A(ST,CSM_CH) #define INTERNAL_TIMER_B(ST,step) #endif /* FM_INTERNAL_TIMER */ #if FM_BUSY_FLAG_SUPPORT #define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) { if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) { if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) return ST->status | 0x80; /* with busy */ /* expire */ FM_BUSY_CLEAR(ST); } return ST->status; } INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) { TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); } #else #define FM_STATUS_FLAG(ST) ((ST)->status) #define FM_BUSY_SET(ST,bclock) {} #define FM_BUSY_CLEAR(ST) {} #endif /* set algorithm connection */ INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) { INT32 *carrier = &OPN->out_fm[ch]; INT32 **om1 = &CH->connect1; INT32 **om2 = &CH->connect3; INT32 **oc1 = &CH->connect2; INT32 **memc = &CH->mem_connect; switch( CH->ALGO ) { case 0: /* M1---C1---MEM---M2---C2---OUT */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 1: /* M1------+-MEM---M2---C2---OUT */ /* C1-+ */ *om1 = &OPN->mem; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 2: /* M1-----------------+-C2---OUT */ /* C1---MEM---M2-+ */ *om1 = &OPN->c2; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 3: /* M1---C1---MEM------+-C2---OUT */ /* M2-+ */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->c2; break; case 4: /* M1---C1-+-OUT */ /* M2---C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = &OPN->c2; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 5: /* +----C1----+ */ /* M1-+-MEM---M2-+-OUT */ /* +----C2----+ */ *om1 = 0; /* special mark */ *oc1 = carrier; *om2 = carrier; *memc= &OPN->m2; break; case 6: /* M1---C1-+ */ /* M2-+-OUT */ /* C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 7: /* M1-+ */ /* C1-+-OUT */ /* M2-+ */ /* C2-+ */ /* MEM: not used*/ *om1 = carrier; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; } CH->connect4 = carrier; } /* set detune & multiple */ INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) { SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; SLOT->DT = ST->dt_tab[(v>>4)&7]; CH->SLOT[SLOT1].Incr=-1; } /* set total level */ INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) { SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ /* recalculate EG output */ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } /* set attack rate & key scale */ INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) { UINT8 old_KSR = SLOT->KSR; SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->KSR = 3-(v>>6); if (SLOT->KSR != old_KSR) { CH->SLOT[SLOT1].Incr=-1; } /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ /* and could result in SLOT->kc remaining unchanged. */ /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */ } } /* set decay rate */ INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; } /* set sustain rate */ INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; } /* set release rate */ INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) { SLOT->sl = sl_table[ v>>4 ]; /* check EG state changes */ if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) SLOT->state = EG_SUS; SLOT->rr = 34 + ((v&0x0f)<<2); SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; } /* advance LFO to next sample */ INLINE void advance_lfo(FM_OPN *OPN) { if (OPN->lfo_timer_overflow) /* LFO enabled ? */ { /* increment LFO timer */ OPN->lfo_timer += OPN->lfo_timer_add; /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ while (OPN->lfo_timer >= OPN->lfo_timer_overflow) { OPN->lfo_timer -= OPN->lfo_timer_overflow; /* There are 128 LFO steps */ OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; // Valley Bell: Replaced old code (non-inverted triangle) with // the one from Genesis Plus GX 1.71. /* triangle (inverted) */ /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ if (OPN->lfo_cnt<64) OPN->LFO_AM = (OPN->lfo_cnt ^ 63) << 1; else OPN->LFO_AM = (OPN->lfo_cnt & 63) << 1; /* PM works with 4 times slower clock */ OPN->LFO_PM = OPN->lfo_cnt >> 2; } } } INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) { //unsigned int out; unsigned int i = 4; /* four operators per channel */ do { switch(SLOT->state) { case EG_ATT: /* attack phase */ if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1))) { /* update attenuation level */ SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; /* check phase transition*/ if (SLOT->volume <= MIN_ATT_INDEX) { SLOT->volume = MIN_ATT_INDEX; SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ } /* recalculate EG output */ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } break; case EG_DEC: /* decay phase */ if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1))) { /* SSG EG type */ if (SLOT->ssg&0x08) { /* update attenuation level */ if (SLOT->volume < 0x200) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; /* recalculate EG output */ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } else { /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; /* recalculate EG output */ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } /* check phase transition*/ if (SLOT->volume >= (INT32)(SLOT->sl)) SLOT->state = EG_SUS; } break; case EG_SUS: /* sustain phase */ if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1))) { /* SSG EG type */ if (SLOT->ssg&0x08) { /* update attenuation level */ if (SLOT->volume < 0x200) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; /* recalculate EG output */ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } else { /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; /* check phase transition*/ if ( SLOT->volume >= MAX_ATT_INDEX ) SLOT->volume = MAX_ATT_INDEX; /* do not change SLOT->state (verified on real chip) */ /* recalculate EG output */ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } break; case EG_REL: /* release phase */ if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1))) { /* SSG EG type */ if (SLOT->ssg&0x08) { /* update attenuation level */ if (SLOT->volume < 0x200) SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; /* check phase transition */ if (SLOT->volume >= 0x200) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } } else { /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; /* check phase transition*/ if (SLOT->volume >= MAX_ATT_INDEX) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } } /* recalculate EG output */ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } break; } // Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file. // Disabling them fixes the SSG-EG. // Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines, // but less accurate. /*out = ((UINT32)SLOT->volume); // negate output (changes come from alternate bit, init comes from attack bit) if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) out ^= MAX_ATT_INDEX; // we need to store the result here because we are going to change ssgn // in next instruction SLOT->vol_out = out + SLOT->tl;*/ SLOT++; i--; }while (i); } /* SSG-EG update process */ /* The behavior is based upon Nemesis tests on real hardware */ /* This is actually executed before each samples */ INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) { unsigned int i = 4; /* four operators per channel */ do { /* detect SSG-EG transition */ /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ /* if an Attack Phase is programmed, inversion can occur on each sample */ if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) { if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ { /* set inversion flag */ if (SLOT->ssg & 0x02) SLOT->ssgn = 4; /* force attenuation level during decay phases */ if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) SLOT->volume = MAX_ATT_INDEX; } else /* loop SSG-EG */ { /* toggle output inversion flag or reset Phase Generator */ if (SLOT->ssg & 0x02) SLOT->ssgn ^= 4; else SLOT->phase = 0; /* same as Key ON */ if (SLOT->state != EG_ATT) { if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) { SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; } else { /* Attack Rate is maximal: directly switch to Decay or Substain */ SLOT->volume = MIN_ATT_INDEX; SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; } } } /* recalculate EG output */ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } /* next slot */ SLOT++; i--; } while (i); } INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) { UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk = (block_fnum&0x7000) >> 12; UINT32 fn = block_fnum & 0xfff; /* recalculate keyscale code */ /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ /* This really stupid bug caused a read outside of the array [size 0x10] and returned invalid values. This caused an annoying vibrato for some notes. (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0) Why are MAME cores always SOO buggy ?! */ /* Oh, and before I forget: it's correct in fm.c */ int kc = (blk<<2) | opn_fktable[fn >> 8]; /* Thanks to Blargg - his patch that helped me to find this bug */ /* recalculate (frequency) phase increment counter */ int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; /* (frequency) phase overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* update phase */ SLOT->phase += (fc * SLOT->mul) >> 1; } else /* LFO phase modulation = zero */ { SLOT->phase += SLOT->Incr; } } INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) { UINT32 block_fnum = CH->block_fnum; UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk = (block_fnum&0x7000) >> 12; UINT32 fn = block_fnum & 0xfff; /* recalculate keyscale code */ /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ /* the same stupid bug as above */ int kc = (blk<<2) | opn_fktable[fn >> 8]; /* recalculate (frequency) phase increment counter */ int fc = (OPN->fn_table[fn]>>(7-blk)); /* (frequency) phase overflow (credits to Nemesis) */ int finc = fc + CH->SLOT[SLOT1].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; finc = fc + CH->SLOT[SLOT2].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; finc = fc + CH->SLOT[SLOT3].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; finc = fc + CH->SLOT[SLOT4].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; } else /* LFO phase modulation = zero */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } /* update phase increment and envelope generator */ INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) { int ksr = kc >> SLOT->KSR; fc += SLOT->DT[kc]; /* detects frequency overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* (frequency) phase increment counter */ SLOT->Incr = (fc * SLOT->mul) >> 1; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 32+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ } SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; } } /* update phase increment counters */ INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) { if( CH->SLOT[SLOT1].Incr==-1) { int fc = CH->fc; int kc = CH->kcode; refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); } } #define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH) { UINT32 AM = OPN->LFO_AM >> CH->ams; unsigned int eg_out; if (CH->Muted) return; OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ eg_out = volume_calc(&CH->SLOT[SLOT1]); { INT32 out = CH->op1_out[0] + CH->op1_out[1]; CH->op1_out[0] = CH->op1_out[1]; if( !CH->connect1 ) { /* algorithm 5 */ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; } else { /* other algorithms */ *CH->connect1 += CH->op1_out[0]; } CH->op1_out[1] = 0; if( eg_out < ENV_QUIET ) /* SLOT 1 */ { if (!CH->FB) out=0; CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); } } eg_out = volume_calc(&CH->SLOT[SLOT3]); if( eg_out < ENV_QUIET ) /* SLOT 3 */ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); eg_out = volume_calc(&CH->SLOT[SLOT2]); if( eg_out < ENV_QUIET ) /* SLOT 2 */ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); eg_out = volume_calc(&CH->SLOT[SLOT4]); if( eg_out < ENV_QUIET ) /* SLOT 4 */ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); /* store current MEM */ CH->mem_value = OPN->mem; /* update phase counters AFTER output calculations */ if(CH->pms) { /* add support for 3 slot mode */ if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2])) { update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); } else update_phase_lfo_channel(OPN, CH); } else /* no LFO phase modulation */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } static void FMCloseTable( void ) { #ifdef SAVE_SAMPLE fclose(sample[0]); #endif return; } /* CSM Key Controll */ INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH) { /* all key ON (verified by Nemesis on real hardware) */ FM_KEYON_CSM(OPN,CH,SLOT1); FM_KEYON_CSM(OPN,CH,SLOT2); FM_KEYON_CSM(OPN,CH,SLOT3); FM_KEYON_CSM(OPN,CH,SLOT4); OPN->SL3.key_csm = 1; } #ifdef __STATE_H__ /* FM channel save , internal state only */ static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch) { int slot , ch; for(ch=0;chop1_out); state_save_register_device_item(device, ch, CH->fc); /* slots */ for(slot=0;slot<4;slot++) { FM_SLOT *SLOT = &CH->SLOT[slot]; state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); state_save_register_device_item(device, ch * 4 + slot, SLOT->state); state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); } } } static void FMsave_state_st(running_device *device,FM_ST *ST) { #if FM_BUSY_FLAG_SUPPORT state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); #endif state_save_register_device_item(device, 0, ST->address ); state_save_register_device_item(device, 0, ST->irq ); state_save_register_device_item(device, 0, ST->irqmask ); state_save_register_device_item(device, 0, ST->status ); state_save_register_device_item(device, 0, ST->mode ); state_save_register_device_item(device, 0, ST->prescaler_sel ); state_save_register_device_item(device, 0, ST->fn_h ); state_save_register_device_item(device, 0, ST->TA ); state_save_register_device_item(device, 0, ST->TAC ); state_save_register_device_item(device, 0, ST->TB ); state_save_register_device_item(device, 0, ST->TBC ); } #endif /* _STATE_H */ #if BUILD_OPN /* write a OPN mode register 0x20-0x2f */ static void OPNWriteMode(FM_OPN *OPN, int r, int v) { UINT8 c; FM_CH *CH; switch(r) { case 0x21: /* Test */ break; case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ if (v&8) /* LFO enabled ? */ { /*if (!OPN->lfo_timer_overflow) { // restart LFO OPN->lfo_cnt = 0; OPN->lfo_timer = 0; OPN->LFO_AM = 0; OPN->LFO_PM = 0; }*/ OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; } else { // Valley Bell: Ported from Genesis Plus GX 1.71 // hold LFO waveform in reset state OPN->lfo_timer_overflow = 0; OPN->lfo_timer = 0; OPN->lfo_cnt = 0; OPN->LFO_PM = 0; OPN->LFO_AM = 126; //OPN->lfo_timer_overflow = 0; } break; case 0x24: /* timer A High 8*/ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); break; case 0x25: /* timer A Low 2*/ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); break; case 0x26: /* timer B */ OPN->ST.TB = v; break; case 0x27: /* mode, timer control */ set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); break; case 0x28: /* key on / off */ c = v & 0x03; if( c == 3 ) break; if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; CH = OPN->P_CH; CH = &CH[c]; if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1); if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2); if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3); if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4); break; } } /* write a OPN register (0x30-0xff) */ static void OPNWriteReg(FM_OPN *OPN, int r, int v) { FM_CH *CH; FM_SLOT *SLOT; UINT8 c = OPN_CHAN(r); if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ if (r >= 0x100) c+=3; CH = OPN->P_CH; CH = &CH[c]; SLOT = &(CH->SLOT[OPN_SLOT(r)]); switch( r & 0xf0 ) { case 0x30: /* DET , MUL */ set_det_mul(&OPN->ST,CH,SLOT,v); break; case 0x40: /* TL */ set_tl(CH,SLOT,v); break; case 0x50: /* KS, AR */ set_ar_ksr(OPN->type,CH,SLOT,v); break; case 0x60: /* bit7 = AM ENABLE, DR */ set_dr(OPN->type, SLOT,v); if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ { SLOT->AMmask = (v&0x80) ? ~0 : 0; } break; case 0x70: /* SR */ set_sr(OPN->type,SLOT,v); break; case 0x80: /* SL, RR */ set_sl_rr(OPN->type,SLOT,v); break; case 0x90: /* SSG-EG */ SLOT->ssg = v&0x0f; /* recalculate EG output */ if (SLOT->state > EG_REL) { if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; else SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } /* SSG-EG envelope shapes : E AtAlH 1 0 0 0 \\\\ 1 0 0 1 \___ 1 0 1 0 \/\/ ___ 1 0 1 1 \ 1 1 0 0 //// ___ 1 1 0 1 / 1 1 1 0 /\/\ 1 1 1 1 /___ E = SSG-EG enable The shapes are generated using Attack, Decay and Sustain phases. Each single character in the diagrams above represents this whole sequence: - when KEY-ON = 1, normal Attack phase is generated (*without* any difference when compared to normal mode), - later, when envelope level reaches minimum level (max volume), the EG switches to Decay phase (which works with bigger steps when compared to normal mode - see below), - later when envelope level passes the SL level, the EG swithes to Sustain phase (which works with bigger steps when compared to normal mode - see below), - finally when envelope level reaches maximum level (min volume), the EG switches to Attack phase again (depends on actual waveform). Important is that when switch to Attack phase occurs, the phase counter of that operator will be zeroed-out (as in normal KEY-ON) but not always. (I havent found the rule for that - perhaps only when the output level is low) The difference (when compared to normal Envelope Generator mode) is that the resolution in Decay and Sustain phases is 4 times lower; this results in only 256 steps instead of normal 1024. In other words: when SSG-EG is disabled, the step inside of the EG is one, when SSG-EG is enabled, the step is four (in Decay and Sustain phases). Times between the level changes are the same in both modes. Important: Decay 1 Level (so called SL) is compared to actual SSG-EG output, so it is the same in both SSG and no-SSG modes, with this exception: when the SSG-EG is enabled and is generating raising levels (when the EG output is inverted) the SL will be found at wrong level !!! For example, when SL=02: 0 -6 = -6dB in non-inverted EG output 96-6 = -90dB in inverted EG output Which means that EG compares its level to SL as usual, and that the output is simply inverted afterall. The Yamaha's manuals say that AR should be set to 0x1f (max speed). That is not necessary, but then EG will be generating Attack phase. */ break; case 0xa0: switch( OPN_SLOT(r) ) { case 0: /* 0xa0-0xa2 : FNUM1 */ if (IsVGMInit) OPN->ST.fn_h = CH->block_fnum >> 8; { UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; UINT8 blk = OPN->ST.fn_h>>3; /* keyscale code */ CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ CH->fc = OPN->fn_table[fn*2]>>(7-blk); /* store fnum in clear form for LFO PM calculations */ CH->block_fnum = (blk<<11) | fn; CH->SLOT[SLOT1].Incr=-1; } break; case 1: /* 0xa4-0xa6 : FNUM2,BLK */ OPN->ST.fn_h = v&0x3f; if (IsVGMInit) // workaround for stupid Kega Fusion init block CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF); break; case 2: /* 0xa8-0xaa : 3CH FNUM1 */ if (IsVGMInit) OPN->SL3.fn_h = OPN->SL3.block_fnum[c] >> 8; if(r < 0x100) { UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; UINT8 blk = OPN->SL3.fn_h>>3; /* keyscale code */ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); OPN->SL3.block_fnum[c] = (blk<<11) | fn; (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; } break; case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ if(r < 0x100) { OPN->SL3.fn_h = v&0x3f; if (IsVGMInit) OPN->SL3.block_fnum[c] = (OPN->SL3.fn_h << 8) | (OPN->SL3.block_fnum[c] & 0xFF); } break; } break; case 0xb0: switch( OPN_SLOT(r) ) { case 0: /* 0xb0-0xb2 : FB,ALGO */ { int feedback = (v>>3)&7; CH->ALGO = v&7; CH->FB = feedback ? feedback+6 : 0; setup_connection( OPN, CH, c ); } break; case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ if( OPN->type & TYPE_LFOPAN) { /* b0-2 PMS */ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ /* b4-5 AMS */ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; /* PAN : b7 = L, b6 = R */ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; } break; } break; } } /* initialize time tables */ static void init_timetables(FM_OPN *OPN, double freqbase) { int i,d; double rate; /* DeTune table */ for (d = 0;d <= 3;d++) { for (i = 0;i <= 31;i++) { rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ OPN->ST.dt_tab[d][i] = (INT32) rate; OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i]; } } /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ /* calculate fnumber -> increment counter table */ for(i = 0; i < 4096; i++) { /* freq table for octave 7 */ /* OPN phase increment counter = 20bit */ /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ /* where sample clock is M/144 */ /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ } /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) ); } /* prescaler set (and make time tables) */ static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) { /* frequency base */ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; /* EG is updated every 3 samples */ OPN->eg_timer_add = (UINT32)((1<ST.freqbase); OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase); /* Timer base time */ OPN->ST.timer_prescaler = timer_prescaler; /* SSG part prescaler set */ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); /* make time tables */ init_timetables(OPN, OPN->ST.freqbase); } static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) { int c,s; for( c = 0 ; c < num ; c++ ) { //memset(&CH[c], 0x00, sizeof(FM_CH)); CH[c].mem_value = 0; CH[c].op1_out[0] = 0; CH[c].op1_out[1] = 0; CH[c].fc = 0; for(s = 0 ; s < 4 ; s++ ) { //memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); CH[c].SLOT[s].Incr = -1; CH[c].SLOT[s].key = 0; CH[c].SLOT[s].phase = 0; CH[c].SLOT[s].ssg = 0; CH[c].SLOT[s].ssgn = 0; CH[c].SLOT[s].state= EG_OFF; CH[c].SLOT[s].volume = MAX_ATT_INDEX; CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; } } } /* initialize generic tables */ static void init_tables(void) { signed int i,x; signed int n; double o,m; /* build Linear Power Table */ for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 2; /* 13 bits here (as in real chip) */ /* 14 bits (with sign bit) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ /* s = sign bit */ /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ /* any value above 13 (included) would be discarded. */ for (i=1; i<13; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } } /* build Logarithmic Sinus table */ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 13-bits (8.5) value is formatted for above 'Power' table */ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); } /* build LFO PM modulation table */ for(i = 0; i < 8; i++) /* 8 PM depths */ { UINT8 fnum; for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ { UINT8 value; UINT8 step; UINT32 offset_depth = i; UINT32 offset_fnum_bit; UINT32 bit_tmp; for (step=0; step<8; step++) { value = 0; for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ { if (fnum & (1<OPN; INT32 *out_fm = OPN->out_fm; int i; FMSAMPLE *bufL,*bufR; INT32 dacout; FM_CH *cch[6]; int lt,rt; /* set bufer */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2612->CH[0]; cch[1] = &F2612->CH[1]; cch[2] = &F2612->CH[2]; cch[3] = &F2612->CH[3]; cch[4] = &F2612->CH[4]; cch[5] = &F2612->CH[5]; if (! F2612->MuteDAC) dacout = F2612->dacout; else dacout = 0; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } }else refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); refresh_fc_eg_chan( OPN, cch[4] ); refresh_fc_eg_chan( OPN, cch[5] ); if (! length) { update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]); } /* buffering */ for(i=0; i < length ; i++) { /* clear outputs */ out_fm[0] = 0; out_fm[1] = 0; out_fm[2] = 0; out_fm[3] = 0; out_fm[4] = 0; out_fm[5] = 0; /* update SSG-EG output */ update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]); update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]); /* calculate FM */ if (! F2612->dac_test) { chan_calc(F2612, OPN, cch[0]); chan_calc(F2612, OPN, cch[1]); chan_calc(F2612, OPN, cch[2]); chan_calc(F2612, OPN, cch[3]); chan_calc(F2612, OPN, cch[4]); if( F2612->dacen ) *cch[5]->connect4 += dacout; else chan_calc(F2612, OPN, cch[5]); } else { out_fm[0] = out_fm[1] = dacout; out_fm[2] = out_fm[3] = dacout; out_fm[5] = dacout; } /* advance LFO */ advance_lfo(OPN); /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); } /*fprintf(hFile, "%u", FileSample, out_fm[0]); for (lt = 0; lt < 6; lt ++) fprintf(hFile, "\t%d", out_fm[lt]); fprintf(hFile, "\n"); FileSample ++;*/ if (out_fm[0] > 8192) out_fm[0] = 8192; else if (out_fm[0] < -8192) out_fm[0] = -8192; if (out_fm[1] > 8192) out_fm[1] = 8192; else if (out_fm[1] < -8192) out_fm[1] = -8192; if (out_fm[2] > 8192) out_fm[2] = 8192; else if (out_fm[2] < -8192) out_fm[2] = -8192; if (out_fm[3] > 8192) out_fm[3] = 8192; else if (out_fm[3] < -8192) out_fm[3] = -8192; if (out_fm[4] > 8192) out_fm[4] = 8192; else if (out_fm[4] < -8192) out_fm[4] = -8192; if (out_fm[5] > 8192) out_fm[5] = 8192; else if (out_fm[5] < -8192) out_fm[5] = -8192; /* 6-channels mixing */ lt = ((out_fm[0]>>0) & OPN->pan[0]); rt = ((out_fm[0]>>0) & OPN->pan[1]); lt += ((out_fm[1]>>0) & OPN->pan[2]); rt += ((out_fm[1]>>0) & OPN->pan[3]); lt += ((out_fm[2]>>0) & OPN->pan[4]); rt += ((out_fm[2]>>0) & OPN->pan[5]); lt += ((out_fm[3]>>0) & OPN->pan[6]); rt += ((out_fm[3]>>0) & OPN->pan[7]); if (! F2612->dac_test) { lt += ((out_fm[4]>>0) & OPN->pan[8]); rt += ((out_fm[4]>>0) & OPN->pan[9]); } else { lt += dacout; lt += dacout; } lt += ((out_fm[5]>>0) & OPN->pan[10]); rt += ((out_fm[5]>>0) & OPN->pan[11]); // Limit( lt, MAXOUT, MINOUT ); // Limit( rt, MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ if (F2612->WaveOutMode & 0x01) F2612->WaveL = lt; if (F2612->WaveOutMode & 0x02) F2612->WaveR = rt; if (F2612->WaveOutMode ^ 0x03) F2612->WaveOutMode ^= 0x03; bufL[i] = F2612->WaveL; bufR[i] = F2612->WaveR; /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ OPN->SL3.key_csm <<= 1; /* timer A control */ //INTERNAL_TIMER_A( &OPN->ST , cch[2] ) { if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) { TimerAOver( &OPN->ST ); // CSM mode total level latch and auto key on if( OPN->ST.mode & 0x80 ) CSMKeyControll( OPN, cch[2] ); } } /* CSM Mode Key ON still disabled */ if (OPN->SL3.key_csm & 2) { /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ FM_KEYOFF_CSM(cch[2],SLOT1); FM_KEYOFF_CSM(cch[2],SLOT2); FM_KEYOFF_CSM(cch[2],SLOT3); FM_KEYOFF_CSM(cch[2],SLOT4); OPN->SL3.key_csm = 0; } } /* timer B control */ // INTERNAL_TIMER_B(&OPN->ST,length) } #ifdef __STATE_H__ void ym2612_postload(void *chip) { if (chip) { YM2612 *F2612 = (YM2612 *)chip; int r; /* DAC data & port */ F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ F2612->dacen = F2612->REGS[0x2d] & 0x80; /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) { OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); } /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) { OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); } /* channels */ /*FM_channel_postload(F2612->CH,6);*/ } } static void YM2612_save_state(YM2612 *F2612, running_device *device) { state_save_register_device_item_array(device, 0, F2612->REGS); FMsave_state_st(device,&F2612->OPN.ST); FMsave_state_channel(device,F2612->CH,6); /* 3slots */ state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); /* address register1 */ state_save_register_device_item(device, 0, F2612->addr_A1); } #endif /* _STATE_H */ /* initialize YM2612 emulator(s) */ //void * ym2612_init(void *param, running_device *device, int clock, int rate, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) void * ym2612_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) { YM2612 *F2612; /* allocate extend state space */ //F2612 = auto_alloc_clear(device->machine, YM2612); F2612 = (YM2612 *)malloc(sizeof(YM2612)); if (F2612 == NULL) return NULL; memset(F2612, 0x00, sizeof(YM2612)); /* allocate total level table (128kb space) */ init_tables(); F2612->OPN.ST.param = param; F2612->OPN.type = TYPE_YM2612; F2612->OPN.P_CH = F2612->CH; //F2612->OPN.ST.device = device; F2612->OPN.ST.clock = clock; F2612->OPN.ST.rate = rate; /* F2612->OPN.ST.irq = 0; */ /* F2612->OPN.ST.status = 0; */ /* Extend handler */ F2612->OPN.ST.timer_handler = timer_handler; F2612->OPN.ST.IRQ_Handler = IRQHandler; if (PseudoSt) F2612->WaveOutMode = 0x01; else F2612->WaveOutMode = 0x03; /*hFile = fopen("YM2612.log", "wt"); fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); FileSample = 0;*/ #ifdef __STATE_H__ YM2612_save_state(F2612, device); #endif return F2612; } /* shut down emulator */ void ym2612_shutdown(void *chip) { YM2612 *F2612 = (YM2612 *)chip; //fclose(hFile); FMCloseTable(); //auto_free(F2612->OPN.ST.device->machine, F2612); free(F2612); } /* reset one of chip */ void ym2612_reset_chip(void *chip) { int i; YM2612 *F2612 = (YM2612 *)chip; FM_OPN *OPN = &F2612->OPN; OPNSetPres( OPN, 6*24, 6*24, 0); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); FM_BUSY_CLEAR(&OPN->ST); //OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; OPN->lfo_timer = 0; OPN->lfo_cnt = 0; OPN->LFO_AM = 126; OPN->LFO_PM = 0; OPN->ST.TAC = 0; OPN->ST.TBC = 0; OPN->SL3.key_csm = 0; OPN->ST.status = 0; OPN->ST.mode = 0; memset(F2612->REGS, 0x00, sizeof(UINT8) * 512); OPNWriteMode(OPN,0x22,0x00); OPNWriteMode(OPN,0x27,0x30); OPNWriteMode(OPN,0x26,0x00); OPNWriteMode(OPN,0x25,0x00); OPNWriteMode(OPN,0x24,0x00); reset_channels( &OPN->ST , &F2612->CH[0] , 6 ); for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } /* DAC mode clear */ F2612->dacen = 0; F2612->dac_test = 0; F2612->dacout = 0; if (F2612->WaveOutMode == 0x02) F2612->WaveOutMode >>= 1; } /* YM2612 write */ /* n = number */ /* a = address */ /* v = value */ int ym2612_write(void *chip, int a, UINT8 v) { YM2612 *F2612 = (YM2612 *)chip; int addr; v &= 0xff; /* adjust to 8 bit bus */ switch( a&3) { case 0: /* address port 0 */ F2612->OPN.ST.address = v; F2612->addr_A1 = 0; break; case 1: /* data port 0 */ if (F2612->addr_A1 != 0) break; /* verified on real YM2608 */ addr = F2612->OPN.ST.address; F2612->REGS[addr] = v; switch( addr & 0xf0 ) { case 0x20: /* 0x20-0x2f Mode */ switch( addr ) { case 0x2a: /* DAC data (YM2612) */ ym2612_update_req(F2612->OPN.ST.param); F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ break; case 0x2b: /* DAC Sel (YM2612) */ /* b7 = dac enable */ F2612->dacen = v & 0x80; break; case 0x2C: // undocumented: DAC Test Reg // b5 = volume enable F2612->dac_test = v & 0x20; break; default: /* OPN section */ ym2612_update_req(F2612->OPN.ST.param); /* write register */ OPNWriteMode(&(F2612->OPN),addr,v); } break; default: /* 0x30-0xff OPN section */ ym2612_update_req(F2612->OPN.ST.param); /* write register */ OPNWriteReg(&(F2612->OPN),addr,v); } break; case 2: /* address port 1 */ F2612->OPN.ST.address = v; F2612->addr_A1 = 1; break; case 3: /* data port 1 */ if (F2612->addr_A1 != 1) break; /* verified on real YM2608 */ addr = F2612->OPN.ST.address; F2612->REGS[addr | 0x100] = v; ym2612_update_req(F2612->OPN.ST.param); OPNWriteReg(&(F2612->OPN),addr | 0x100,v); break; } return F2612->OPN.ST.irq; } UINT8 ym2612_read(void *chip,int a) { YM2612 *F2612 = (YM2612 *)chip; switch( a&3) { case 0: /* status 0 */ return FM_STATUS_FLAG(&F2612->OPN.ST); case 1: case 2: case 3: //LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a)); return FM_STATUS_FLAG(&F2612->OPN.ST); } return 0; } int ym2612_timer_over(void *chip,int c) { YM2612 *F2612 = (YM2612 *)chip; if( c ) { /* Timer B */ TimerBOver( &(F2612->OPN.ST) ); } else { /* Timer A */ ym2612_update_req(F2612->OPN.ST.param); /* timer update */ TimerAOver( &(F2612->OPN.ST) ); /* CSM mode key,TL controll */ if ((F2612->OPN.ST.mode & 0xc0) == 0x80) { /* CSM mode total level latch and auto key on */ CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) ); } } return F2612->OPN.ST.irq; } void ym2612_set_mutemask(void *chip, UINT32 MuteMask) { YM2612 *F2612 = (YM2612 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; F2612->MuteDAC = (MuteMask >> 6) & 0x01; return; } void ym2612_setoptions(UINT8 Flags) { PseudoSt = (Flags >> 2) & 0x01; return; } #endif /* (BUILD_YM2612||BUILD_YM3238) */ ================================================ FILE: VGMPlay/chips/fmopl.c ================================================ /* ** ** File: fmopl.c - software implementation of FM sound generator ** types OPL and OPL2 ** ** Copyright Jarek Burczynski (bujar at mame dot net) ** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** ** Version 0.72 ** Revision History: 04-08-2003 Jarek Burczynski: - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip handles memory read/write or during the adpcm synthesis when the chip requests another byte of ADPCM data. 24-07-2003 Jarek Burczynski: - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after some (unknown) delay). Right now it's always set. 14-06-2003 Jarek Burczynski: - implemented all of the status register flags in Y8950 emulation - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since they can be either RAM or ROM 08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) - corrected ym3526_read() to always set bit 2 and bit 1 to HIGH state - identical to ym3812_read (verified on real YM3526) 04-28-2002 Jarek Burczynski: - binary exact Envelope Generator (verified on real YM3812); compared to YM2151: the EG clock is equal to internal_clock, rates are 2 times slower and volume resolution is one bit less - modified interface functions (they no longer return pointer - that's internal to the emulator now): - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() - corrected 'off by one' error in feedback calculations (when feedback is off) - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) - speeded up noise generator calculations (Nicola Salmoria) 03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) Complete rewrite (all verified on real YM3812): - corrected sin_tab and tl_tab data - corrected operator output calculations - corrected waveform_select_enable register; simply: ignore all writes to waveform_select register when waveform_select_enable == 0 and do not change the waveform previously selected. - corrected KSR handling - corrected Envelope Generator: attack shape, Sustain mode and Percussive/Non-percussive modes handling - Envelope Generator rates are two times slower now - LFO amplitude (tremolo) and phase modulation (vibrato) - rhythm sounds phase generation - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) 12-28-2001 Acho A. Tang - reflected Delta-T EOS status on Y8950 status port. - fixed subscription range of attack/decay tables To do: add delay before key off in CSM mode (see CSMKeyControll) verify volume of the FM part on the Y8950 */ #include #include "mamedef.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL //#include "sndintrf.h" #include "fmopl.h" #if BUILD_Y8950 #include "ymdeltat.h" #endif /* output final shift */ #if (OPL_SAMPLE_BITS==16) #define FINAL_SH (0) #define MAXOUT (+32767) #define MINOUT (-32768) #else #define FINAL_SH (8) #define MAXOUT (+127) #define MINOUT (-128) #endif #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (EG timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<=0) { if (value < 0x0200) return (value & ~0); if (value < 0x0400) return (value & ~1); if (value < 0x0800) return (value & ~3); if (value < 0x1000) return (value & ~7); if (value < 0x2000) return (value & ~15); if (value < 0x4000) return (value & ~31); return (value & ~63); } /*else value < 0*/ if (value > -0x0200) return (~abs(value) & ~0); if (value > -0x0400) return (~abs(value) & ~1); if (value > -0x0800) return (~abs(value) & ~3); if (value > -0x1000) return (~abs(value) & ~7); if (value > -0x2000) return (~abs(value) & ~15); if (value > -0x4000) return (~abs(value) & ~31); return (~abs(value) & ~63); } static FILE *sample[1]; #if 1 /*save to MONO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = acc_calc(lt); \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = rt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif //#define LOG_CYM_FILE 0 //static FILE * cymfile = NULL; #define OPL_TYPE_WAVESEL 0x01 /* waveform select */ #define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ #define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ #define OPL_TYPE_IO 0x08 /* I/O port */ /* ---------- Generic interface section ---------- */ #define OPL_TYPE_YM3526 (0) #define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) #define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) typedef struct { UINT32 ar; /* attack rate: AR<<2 */ UINT32 dr; /* decay rate: DR<<2 */ UINT32 rr; /* release rate:RR<<2 */ UINT8 KSR; /* key scale rate */ UINT8 ksl; /* keyscale level */ UINT8 ksr; /* key scale rate: kcode>>KSR */ UINT8 mul; /* multiple: mul_tab[ML] */ /* Phase Generator */ UINT32 Cnt; /* frequency counter */ UINT32 Incr; /* frequency counter step */ UINT8 FB; /* feedback shift value */ INT32 *connect1; /* slot1 output pointer */ INT32 op1_out[2]; /* slot1 output for feedback */ UINT8 CON; /* connection (algorithm) type */ /* Envelope Generator */ UINT8 eg_type; /* percussive/non-percussive mode */ UINT8 state; /* phase type */ UINT32 TL; /* total level: TL << 2 */ INT32 TLL; /* adjusted now TL */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level: sl_tab[SL] */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT8 eg_sh_dr; /* (decay state) */ UINT8 eg_sel_dr; /* (decay state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ /* LFO */ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ /* waveform select */ UINT16 wavetable; } OPL_SLOT; typedef struct { OPL_SLOT SLOT[2]; /* phase generator state */ UINT32 block_fnum; /* block+fnum */ UINT32 fc; /* Freq. Increment base */ UINT32 ksl_base; /* KeyScaleLevel Base step */ UINT8 kcode; /* key code (for key scaling) */ UINT8 Muted; } OPL_CH; /* OPL state */ typedef struct fm_opl_f { /* FM channel slots */ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ UINT8 MuteSpc[6]; /* Mute Special: 5 Rhythm + 1 DELTA-T Channel */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ UINT8 rhythm; /* Rhythm mode */ UINT32 fn_tab[1024]; /* fnumber->increment counter */ /* LFO */ UINT32 LFO_AM; INT32 LFO_PM; UINT8 lfo_am_depth; UINT8 lfo_pm_depth_range; UINT32 lfo_am_cnt; UINT32 lfo_am_inc; UINT32 lfo_pm_cnt; UINT32 lfo_pm_inc; UINT32 noise_rng; /* 23 bit noise shift register */ UINT32 noise_p; /* current noise 'phase' */ UINT32 noise_f; /* current noise period */ UINT8 wavesel; /* waveform select enable flag */ UINT32 T[2]; /* timer counters */ UINT8 st[2]; /* timer enable */ #if BUILD_Y8950 /* Delta-T ADPCM unit (Y8950) */ YM_DELTAT *deltat; /* Keyboard and I/O ports interface */ UINT8 portDirection; UINT8 portLatch; OPL_PORTHANDLER_R porthandler_r; OPL_PORTHANDLER_W porthandler_w; void * port_param; OPL_PORTHANDLER_R keyboardhandler_r; OPL_PORTHANDLER_W keyboardhandler_w; void * keyboard_param; #endif /* external event callback handlers */ OPL_TIMERHANDLER timer_handler; /* TIMER handler */ void *TimerParam; /* TIMER parameter */ OPL_IRQHANDLER IRQHandler; /* IRQ handler */ void *IRQParam; /* IRQ parameter */ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ void *UpdateParam; /* stream update parameter */ UINT8 type; /* chip type */ UINT8 address; /* address register */ UINT8 status; /* status flag */ UINT8 statusmask; /* status mask */ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ UINT32 clock; /* master clock (Hz) */ UINT32 rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ //attotime TimerBase; /* Timer base time (==sampling time)*/ signed int phase_modulation; /* phase modulation input (SLOT 2) */ signed int output[1]; #if BUILD_Y8950 INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ #endif } FM_OPL; /* mapping of register number (offset) to slot number used by the emulator */ static const int slot_array[32]= { 0, 2, 4, 1, 3, 5,-1,-1, 6, 8,10, 7, 9,11,-1,-1, 12,14,16,13,15,17,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1 }; /* key scale level */ /* table is 3dB/octave , DV converts this into 6dB/octave */ /* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ #define DV (0.1875/2.0) static const UINT32 ksl_tab[8*16]= { /* OCT 0 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, /* OCT 1 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, /* OCT 2 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, /* OCT 3 */ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, /* OCT 4 */ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, 10.875/DV,11.250/DV,11.625/DV,12.000/DV, /* OCT 5 */ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, 9.000/DV,10.125/DV,10.875/DV,11.625/DV, 12.000/DV,12.750/DV,13.125/DV,13.500/DV, 13.875/DV,14.250/DV,14.625/DV,15.000/DV, /* OCT 6 */ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, 12.000/DV,13.125/DV,13.875/DV,14.625/DV, 15.000/DV,15.750/DV,16.125/DV,16.500/DV, 16.875/DV,17.250/DV,17.625/DV,18.000/DV, /* OCT 7 */ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, 15.000/DV,16.125/DV,16.875/DV,17.625/DV, 18.000/DV,18.750/DV,19.125/DV,19.500/DV, 19.875/DV,20.250/DV,20.625/DV,21.000/DV }; #undef DV /* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 }; /* sustain level table (3dB per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ #define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) static const UINT32 sl_tab[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; #undef SC #define RATE_STEPS (8) static const unsigned char eg_inc[15*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ /*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ /*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(13) in this table - it's directly in the code */ static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), /* rates 00-12 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 13 */ O( 4),O( 5),O( 6),O( 7), /* rate 14 */ O( 8),O( 9),O(10),O(11), /* rate 15 */ O(12),O(12),O(12),O(12), /* 16 dummy rates (same as 15 3) */ O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ /*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ /*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ #define O(a) (a*1) static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-12 */ O(12),O(12),O(12),O(12), O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 16 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), }; #undef O /* multiple table */ #define ML 2 static const UINT8 mul_tab[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML }; #undef ML /* TL_TAB_LEN is calculated as: * 12 - sinus amplitude bits (Y axis) * 2 - sinus sign bit (Y axis) * TL_RES_LEN - sinus resolution (X axis) */ #define TL_TAB_LEN (12*2*TL_RES_LEN) static signed int tl_tab[TL_TAB_LEN]; #define ENV_QUIET (TL_TAB_LEN>>4) /* sin waveform table in 'decibel' scale */ /* four waveforms on OPL2 type chips */ static unsigned int sin_tab[SIN_LEN * 4]; /* LFO Amplitude Modulation table (verified on real YM3812) 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples Length: 210 elements. Each of the elements has to be repeated exactly 64 times (on 64 consecutive samples). The whole table takes: 64 * 210 = 13440 samples. When AM = 1 data is used directly When AM = 0 data is divided by 4 before being used (losing precision is important) */ #define LFO_AM_TAB_ELEMENTS 210 static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; /* LFO Phase Modulation table (verified on real YM3812) */ static const INT8 lfo_pm_table[8*8*2] = { /* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ 4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ 5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ 6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ 7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ }; /* lock level of common table */ static int num_lock = 0; #define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1]) #define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2]) #define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1]) #define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2]) /*INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) val = min; return val; }*/ /* status set and IRQ handling */ INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) { /* set status flag */ OPL->status |= flag; if(!(OPL->status & 0x80)) { if(OPL->status & OPL->statusmask) { /* IRQ on */ OPL->status |= 0x80; /* callback user interrupt handler (IRQ is OFF to ON) */ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); } } } /* status reset and IRQ handling */ INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) { /* reset status flag */ OPL->status &=~flag; if((OPL->status & 0x80)) { if (!(OPL->status & OPL->statusmask) ) { OPL->status &= 0x7f; /* callback user interrupt handler (IRQ is ON to OFF) */ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); } } } /* IRQ mask set */ INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) { OPL->statusmask = flag; /* IRQ handling check */ OPL_STATUS_SET(OPL,0); OPL_STATUS_RESET(OPL,0); } /* advance LFO to next sample */ INLINE void advance_lfo(FM_OPL *OPL) { UINT8 tmp; /* LFO */ OPL->lfo_am_cnt += OPL->lfo_am_inc; if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; if (OPL->lfo_am_depth) OPL->LFO_AM = tmp; else OPL->LFO_AM = tmp>>2; OPL->lfo_pm_cnt += OPL->lfo_pm_inc; OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; } INLINE void refresh_eg(FM_OPL* OPL) { OPL_CH *CH; OPL_SLOT *op; int i; int new_vol; for (i=0; i<9*2; i++) { CH = &OPL->P_CH[i/2]; op = &CH->SLOT[i&1]; // Envelope Generator switch(op->state) { case EG_ATT: // attack phase if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) { new_vol = op->volume + ((~op->volume * (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) ) >> 3); if (new_vol <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } break; /*case EG_DEC: // decay phase if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) { new_vol = op->volume + eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; if ( new_vol >= op->sl ) op->state = EG_SUS; } break; case EG_SUS: // sustain phase if ( !op->eg_type) percussive mode { new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) { if ( new_vol >= MAX_ATT_INDEX ) op->volume = MAX_ATT_INDEX; } } break; case EG_REL: // release phase if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) { new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; if ( new_vol >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } break; default: break;*/ } } return; } /* advance to next sample */ INLINE void advance(FM_OPL *OPL) { OPL_CH *CH; OPL_SLOT *op; int i; OPL->eg_timer += OPL->eg_timer_add; while (OPL->eg_timer >= OPL->eg_timer_overflow) { OPL->eg_timer -= OPL->eg_timer_overflow; OPL->eg_cnt++; for (i=0; i<9*2; i++) { CH = &OPL->P_CH[i/2]; op = &CH->SLOT[i&1]; /* Envelope Generator */ switch(op->state) { case EG_ATT: /* attack phase */ if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) { op->volume += (~op->volume * (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) ) >>3; if (op->volume <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) { op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; if ( op->volume >= op->sl ) op->state = EG_SUS; } break; case EG_SUS: /* sustain phase */ /* this is important behaviour: one can change percusive/non-percussive modes on the fly and the chip will remain in sustain phase - verified on real YM3812 */ if(op->eg_type) /* non-percussive mode */ { /* do nothing */ } else /* percussive mode */ { /* during sustain phase chip adds Release Rate (in percussive mode) */ if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) { op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) op->volume = MAX_ATT_INDEX; } /* else do nothing in sustain phase */ } break; case EG_REL: /* release phase */ if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) { op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } break; default: break; } } } for (i=0; i<9*2; i++) { CH = &OPL->P_CH[i/2]; op = &CH->SLOT[i&1]; /* Phase Generator */ if(op->vib) { UINT8 block; unsigned int block_fnum = CH->block_fnum; unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { block_fnum += lfo_fn_table_index_offset; block = (block_fnum&0x1c00) >> 10; op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; } else /* LFO phase modulation = zero */ { op->Cnt += op->Incr; } } else /* LFO phase modulation disabled for this operator */ { op->Cnt += op->Incr; } } /* The Noise Generator of the YM3812 is 23-bit shift register. * Period is equal to 2^23-2 samples. * Register works at sampling frequency of the chip, so output * can change on every sample. * * Output of the register and input to the bit 22 is: * bit0 XOR bit14 XOR bit15 XOR bit22 * * Simply use bit 22 as the noise output. */ OPL->noise_p += OPL->noise_f; i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ OPL->noise_p &= FREQ_MASK; while (i) { /* UINT32 j; j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); */ /* Instead of doing all the logic operations above, we use a trick here (and use bit 0 as the noise output). The difference is only that the noise bit changes one step ahead. This doesn't matter since we don't know what is real state of the noise_rng after the reset. */ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; OPL->noise_rng >>= 1; i--; } } INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } #define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask)) /* calculate output */ INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH ) { OPL_SLOT *SLOT; unsigned int env; signed int out; if (CH->Muted) return; OPL->phase_modulation = 0; /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; *SLOT->connect1 += SLOT->op1_out[0]; SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->FB) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); } /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable); } /* operators used in the rhythm sounds generation process: Envelope Generator: channel operator register number Bass High Snare Tom Top / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal 6 / 0 12 50 70 90 f0 + 6 / 1 15 53 73 93 f3 + 7 / 0 13 51 71 91 f1 + 7 / 1 16 54 74 94 f4 + 8 / 0 14 52 72 92 f2 + 8 / 1 17 55 75 95 f5 + Phase Generator: channel operator register number Bass High Snare Tom Top / slot number MULTIPLE Drum Hat Drum Tom Cymbal 6 / 0 12 30 + 6 / 1 15 33 + 7 / 0 13 31 + + + 7 / 1 16 34 ----- n o t u s e d ----- 8 / 0 14 32 + 8 / 1 17 35 + + channel operator register number Bass High Snare Tom Top number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal 6 12,15 B6 A6 + 7 13,16 B7 A7 + + + 8 14,17 B8 A8 + + + */ /* calculate rhythm */ INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise ) { OPL_SLOT *SLOT; signed int out; unsigned int env; /* Bass Drum (verified on real YM3812): - depends on the channel 6 'connect' register: when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - output sample always is multiplied by 2 */ OPL->phase_modulation = 0; /* SLOT 1 */ SLOT = &CH[6].SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; if (!SLOT->CON) OPL->phase_modulation = SLOT->op1_out[0]; /* else ignore output of operator 1 */ SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->FB) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); } /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET && ! OPL->MuteSpc[0] ) OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2; /* Phase generation is based on: */ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ /* SD (16) channel 7->slot 1 */ /* TOM (14) channel 8->slot 1 */ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ /* Envelope generation based on: */ /* HH channel 7->slot1 */ /* SD channel 7->slot2 */ /* TOM channel 8->slot1 */ /* TOP channel 8->slot2 */ /* The following formulas can be well optimized. I leave them in direct form for now (in case I've missed something). */ /* High Hat (verified on real YM3812) */ env = volume_calc(SLOT7_1); if( env < ENV_QUIET && ! OPL->MuteSpc[4] ) { /* high hat phase generation: phase = d0 or 234 (based on frequency only) phase = 34 or 2d0 (based on noise) */ /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0xd0; */ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e ^ bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ if (res2) phase = (0x200|(0xd0>>2)); /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ if (phase&0x200) { if (noise) phase = 0x200|0xd0; } else /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ { if (noise) phase = 0xd0>>2; } OPL->output[0] += op_calc(phase<wavetable) * 2; } /* Snare Drum (verified on real YM3812) */ env = volume_calc(SLOT7_2); if( env < ENV_QUIET && ! OPL->MuteSpc[1] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; /* when bit8 = 0 phase = 0x100; */ /* when bit8 = 1 phase = 0x200; */ UINT32 phase = bit8 ? 0x200 : 0x100; /* Noise bit XOR'es phase by 0x100 */ /* when noisebit = 0 pass the phase from calculation above */ /* when noisebit = 1 phase ^= 0x100; */ /* in other words: phase ^= (noisebit<<8); */ if (noise) phase ^= 0x100; OPL->output[0] += op_calc(phase<wavetable) * 2; } /* Tom Tom (verified on real YM3812) */ env = volume_calc(SLOT8_1); if( env < ENV_QUIET && ! OPL->MuteSpc[2] ) OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; /* Top Cymbal (verified on real YM3812) */ env = volume_calc(SLOT8_2); if( env < ENV_QUIET && ! OPL->MuteSpc[3] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0x100; */ /* when res1 = 1 phase = 0x200 | 0x100; */ UINT32 phase = res1 ? 0x300 : 0x100; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e ^ bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | 0x100; */ if (res2) phase = 0x300; OPL->output[0] += op_calc(phase<wavetable) * 2; } } /* generic table initialize */ static int init_tables(void) { signed int i,x; signed int n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 1; /* 12 bits here (as in real chip) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; for (i=1; i<12; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } #if 0 logerror("tl %04i", x*2); for (i=0; i<12; i++) logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); logerror("\n"); #endif } /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ } for (i=0; i>1) ]; /* waveform 3: _ _ _ _ */ /* / |_/ |_/ |_/ |_*/ /* abs(output only first quarter of the sinus waveform) */ if (i & (1<<(SIN_BITS-2)) ) sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; else sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ } /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ #ifdef SAVE_SAMPLE sample[0]=fopen("sampsum.pcm","wb"); #endif return 1; } static void OPLCloseTable( void ) { #ifdef SAVE_SAMPLE fclose(sample[0]); #endif } static void OPL_initalize(FM_OPL *OPL) { int i; /* frequency base */ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0; #if 0 OPL->rate = (double)OPL->clock / 72.0; OPL->freqbase = 1.0; #endif /*logerror("freqbase=%f\n", OPL->freqbase);*/ /* Timer base time */ //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72); /* make fnumber -> increment counter table */ for( i=0 ; i < 1024 ; i++ ) { /* opn phase increment counter = 20bit */ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ #if 0 logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 ); #endif } #if 0 for( i=0 ; i < 16 ; i++ ) { logerror("FMOPL.C: sl_tab[%i] = %08x\n", i, sl_tab[i] ); } for( i=0 ; i < 8 ; i++ ) { int j; logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); for (j=0; j<16; j++) { logerror("%08x ", ksl_tab[i*16+j] ); } logerror("\n"); } #endif for(i = 0; i < 9; i ++) OPL->P_CH[i].Muted = 0x00; for(i = 0; i < 6; i ++) OPL->MuteSpc[i] = 0x00; /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ /* One entry from LFO_AM_TABLE lasts for 64 samples */ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/ /* Noise generator: a step takes 1 sample */ OPL->noise_f = (1.0 / 1.0) * (1<freqbase; OPL->eg_timer_add = (1<freqbase; OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/ } INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set) { if( !SLOT->key ) { /* restart Phase Generator */ SLOT->Cnt = 0; /* phase -> Attack */ SLOT->state = EG_ATT; } SLOT->key |= key_set; } INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr) { if( SLOT->key ) { SLOT->key &= key_clr; if( !SLOT->key ) { /* phase -> Release */ if (SLOT->state>EG_REL) SLOT->state = EG_REL; } } } /* update phase increment counter of operator (also update the EG rates if necessary) */ INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) { int ksr; /* (frequency) phase increment counter */ SLOT->Incr = CH->fc * SLOT->mul; ksr = CH->kcode >> SLOT->KSR; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 16+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } } /* set multi,am,vib,EG-TYP,KSR,mul */ INLINE void set_mul(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->mul = mul_tab[v&0x0f]; SLOT->KSR = (v&0x10) ? 0 : 2; SLOT->eg_type = (v&0x20); SLOT->vib = (v&0x40); SLOT->AMmask = (v&0x80) ? ~0 : 0; CALC_FCSLOT(CH,SLOT); } /* set ksl & tl */ INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->ksl = ksl_shift[v >> 6]; SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } /* set attack rate & decay rate */ INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; if ((SLOT->ar + SLOT->ksr) < 16+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; } /* set sustain level & release rate */ INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->sl = sl_tab[ v>>4 ]; SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } /* write a value v to register r on OPL chip */ static void OPLWriteReg(FM_OPL *OPL, int r, int v) { OPL_CH *CH; int slot; int block_fnum; /* adjust bus to 8 bits */ r &= 0xff; v &= 0xff; /*if (LOG_CYM_FILE && (cymfile) && (r!=0) ) { fputc( (unsigned char)r, cymfile ); fputc( (unsigned char)v, cymfile ); }*/ switch(r&0xe0) { case 0x00: /* 00-1f:control */ switch(r&0x1f) { case 0x01: /* waveform select enable */ if(OPL->type&OPL_TYPE_WAVESEL) { OPL->wavesel = v&0x20; /* do not change the waveform previously selected */ } break; case 0x02: /* Timer 1 */ OPL->T[0] = (256-v)*4; break; case 0x03: /* Timer 2 */ OPL->T[1] = (256-v)*16; break; case 0x04: /* IRQ clear / mask and Timer enable */ if(v&0x80) { /* IRQ flag clear */ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */ } else { /* set IRQ mask ,timer enable*/ UINT8 st1 = v&1; UINT8 st2 = (v>>1)&1; /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ OPL_STATUS_RESET(OPL, v & (0x78-0x08) ); OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); /* timer 2 */ if(OPL->st[1] != st2) { //attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero; OPL->st[1] = st2; //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period); } /* timer 1 */ if(OPL->st[0] != st1) { //attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero; OPL->st[0] = st1; //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period); } } break; #if BUILD_Y8950 case 0x06: /* Key Board OUT */ if(OPL->type&OPL_TYPE_KEYBOARD) { if(OPL->keyboardhandler_w) OPL->keyboardhandler_w(OPL->keyboard_param,v); #ifdef _DEBUG else logerror("Y8950: write unmapped KEYBOARD port\n"); #endif } break; case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ if(OPL->type&OPL_TYPE_ADPCM) YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); break; #endif case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ OPL->mode = v; #if BUILD_Y8950 if(OPL->type&OPL_TYPE_ADPCM) YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ #endif break; #if BUILD_Y8950 case 0x09: /* START ADD */ case 0x0a: case 0x0b: /* STOP ADD */ case 0x0c: case 0x0d: /* PRESCALE */ case 0x0e: case 0x0f: /* ADPCM data write */ case 0x10: /* DELTA-N */ case 0x11: /* DELTA-N */ case 0x12: /* ADPCM volume */ if(OPL->type&OPL_TYPE_ADPCM) YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); break; case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ #ifdef _DEBUG logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); #endif break; case 0x18: /* I/O CTRL (Direction) */ if(OPL->type&OPL_TYPE_IO) OPL->portDirection = v&0x0f; break; case 0x19: /* I/O DATA */ if(OPL->type&OPL_TYPE_IO) { OPL->portLatch = v; if(OPL->porthandler_w) OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); } break; #endif default: #ifdef _DEBUG logerror("FMOPL.C: write to unknown register: %02x\n",r); #endif break; } break; case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ slot = slot_array[r&0x1f]; if(slot < 0) return; set_mul(OPL,slot,v); break; case 0x40: slot = slot_array[r&0x1f]; if(slot < 0) return; set_ksl_tl(OPL,slot,v); break; case 0x60: slot = slot_array[r&0x1f]; if(slot < 0) return; set_ar_dr(OPL,slot,v); break; case 0x80: slot = slot_array[r&0x1f]; if(slot < 0) return; set_sl_rr(OPL,slot,v); break; case 0xa0: if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ { OPL->lfo_am_depth = v & 0x80; OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; OPL->rhythm = v&0x3f; if(OPL->rhythm&0x20) { /* BD key on/off */ if(v&0x10) { FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); } else { FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); } /* HH key on/off */ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); /* SD key on/off */ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); /* TOM key on/off */ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY key on/off */ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); } else { /* BD key off */ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); /* HH key off */ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); /* SD key off */ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); /* TOM key off */ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY off */ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); } return; } /* keyon,block,fnum */ if( (r&0x0f) > 8) return; CH = &OPL->P_CH[r&0x0f]; if(!(r&0x10)) { /* a0-a8 */ block_fnum = (CH->block_fnum&0x1f00) | v; } else { /* b0-b8 */ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); } } /* update */ if(CH->block_fnum != block_fnum) { UINT8 block = block_fnum >> 10; CH->block_fnum = block_fnum; CH->ksl_base = ksl_tab[block_fnum>>6]; CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ CH->kcode = (CH->block_fnum&0x1c00)>>9; /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ if (OPL->mode&0x40) CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ else CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); } break; case 0xc0: /* FB,C */ if( (r&0x0f) > 8) return; CH = &OPL->P_CH[r&0x0f]; CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; CH->SLOT[SLOT1].CON = v&1; CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation; break; case 0xe0: /* waveform select */ /* simply ignore write to the waveform select register if selecting not enabled in test register */ if(OPL->wavesel) { slot = slot_array[r&0x1f]; if(slot < 0) return; CH = &OPL->P_CH[slot/2]; CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; } break; } } /*static TIMER_CALLBACK( cymfile_callback ) { if (cymfile) { fputc( (unsigned char)0, cymfile ); } }*/ /* lock/unlock for common table */ static int OPL_LockTable(void) { num_lock++; if(num_lock>1) return 0; /* first time */ /* allocate total level table (128kb space) */ if( !init_tables() ) { num_lock--; return -1; } /*if (LOG_CYM_FILE) { cymfile = fopen("3812_.cym","wb"); if (cymfile) timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer else logerror("Could not create file 3812_.cym\n"); }*/ return 0; } static void OPL_UnLockTable(void) { if(num_lock) num_lock--; if(num_lock) return; /* last time */ OPLCloseTable(); /*if (cymfile) fclose (cymfile); cymfile = NULL;*/ } static void OPLResetChip(FM_OPL *OPL) { int c,s; int i; OPL->eg_timer = 0; OPL->eg_cnt = 0; OPL->noise_rng = 1; /* noise shift register */ OPL->mode = 0; /* normal mode */ OPL_STATUS_RESET(OPL,0x7f); /* reset with register write */ OPLWriteReg(OPL,0x01,0); /* wavesel disable */ OPLWriteReg(OPL,0x02,0); /* Timer1 */ OPLWriteReg(OPL,0x03,0); /* Timer2 */ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); /* reset operator parameters */ for( c = 0 ; c < 9 ; c++ ) { OPL_CH *CH = &OPL->P_CH[c]; for(s = 0 ; s < 2 ; s++ ) { /* wave table */ CH->SLOT[s].wavetable = 0; CH->SLOT[s].state = EG_OFF; CH->SLOT[s].volume = MAX_ATT_INDEX; } } #if BUILD_Y8950 if(OPL->type&OPL_TYPE_ADPCM) { YM_DELTAT *DELTAT = OPL->deltat; DELTAT->freqbase = OPL->freqbase; DELTAT->output_pointer = &OPL->output_deltat[0]; DELTAT->portshift = 5; DELTAT->output_range = 1<<23; YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL); } #endif } #if 0 //static STATE_POSTLOAD( OPL_postload ) static void OPL_postload(void* param) { FM_OPL *OPL = (FM_OPL *)param; int slot, ch; for( ch=0 ; ch < 9 ; ch++ ) { OPL_CH *CH = &OPL->P_CH[ch]; /* Look up key scale level */ UINT32 block_fnum = CH->block_fnum; CH->ksl_base = ksl_tab[block_fnum >> 6]; CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); for( slot=0 ; slot < 2 ; slot++ ) { OPL_SLOT *SLOT = &CH->SLOT[slot]; /* Calculate key scale rate */ SLOT->ksr = CH->kcode >> SLOT->KSR; /* Calculate attack, decay and release rates */ if ((SLOT->ar + SLOT->ksr) < 16+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; /* Calculate phase increment */ SLOT->Incr = CH->fc * SLOT->mul; /* Total level */ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl); /* Connect output */ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation; } } #if BUILD_Y8950 if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) { // We really should call the postlod function for the YM_DELTAT, but it's hard without registers // (see the way the YM2610 does it) //YM_DELTAT_postload(OPL->deltat, REGS); } #endif } static void OPLsave_state_channel(OPL_CH *CH) { int slot, ch; for( ch=0 ; ch < 9 ; ch++, CH++ ) { // channel state_save_register_device_item(device, ch, CH->block_fnum); state_save_register_device_item(device, ch, CH->kcode); // slots for( slot=0 ; slot < 2 ; slot++ ) { OPL_SLOT *SLOT = &CH->SLOT[slot]; state_save_register_device_item(device, ch * 2 + slot, SLOT->ar); state_save_register_device_item(device, ch * 2 + slot, SLOT->dr); state_save_register_device_item(device, ch * 2 + slot, SLOT->rr); state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR); state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl); state_save_register_device_item(device, ch * 2 + slot, SLOT->mul); state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt); state_save_register_device_item(device, ch * 2 + slot, SLOT->FB); state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out); state_save_register_device_item(device, ch * 2 + slot, SLOT->CON); state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type); state_save_register_device_item(device, ch * 2 + slot, SLOT->state); state_save_register_device_item(device, ch * 2 + slot, SLOT->TL); state_save_register_device_item(device, ch * 2 + slot, SLOT->volume); state_save_register_device_item(device, ch * 2 + slot, SLOT->sl); state_save_register_device_item(device, ch * 2 + slot, SLOT->key); state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask); state_save_register_device_item(device, ch * 2 + slot, SLOT->vib); state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable); } } } #endif /* Register savestate for a virtual YM3812/YM3526/Y8950 */ /*static void OPL_save_state(FM_OPL *OPL) { OPLsave_state_channel(device, OPL->P_CH); state_save_register_device_item(device, 0, OPL->eg_cnt); state_save_register_device_item(device, 0, OPL->eg_timer); state_save_register_device_item(device, 0, OPL->rhythm); state_save_register_device_item(device, 0, OPL->lfo_am_depth); state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range); state_save_register_device_item(device, 0, OPL->lfo_am_cnt); state_save_register_device_item(device, 0, OPL->lfo_pm_cnt); state_save_register_device_item(device, 0, OPL->noise_rng); state_save_register_device_item(device, 0, OPL->noise_p); if( OPL->type & OPL_TYPE_WAVESEL ) { state_save_register_device_item(device, 0, OPL->wavesel); } state_save_register_device_item_array(device, 0, OPL->T); state_save_register_device_item_array(device, 0, OPL->st); #if BUILD_Y8950 if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) { YM_DELTAT_savestate(device, OPL->deltat); } if ( OPL->type & OPL_TYPE_IO ) { state_save_register_device_item(device, 0, OPL->portDirection); state_save_register_device_item(device, 0, OPL->portLatch); } #endif state_save_register_device_item(device, 0, OPL->address); state_save_register_device_item(device, 0, OPL->status); state_save_register_device_item(device, 0, OPL->statusmask); state_save_register_device_item(device, 0, OPL->mode); state_save_register_postload(device->machine, OPL_postload, OPL); }*/ /* Create one of virtual YM3812/YM3526/Y8950 */ /* 'clock' is chip clock in Hz */ /* 'rate' is sampling rate */ static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type) { char *ptr; FM_OPL *OPL; int state_size; if (OPL_LockTable() == -1) return NULL; /* calculate OPL state size */ state_size = sizeof(FM_OPL); #if BUILD_Y8950 if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); #endif /* allocate memory block */ ptr = (char *)malloc(state_size); if (ptr==NULL) return NULL; /* clear */ memset(ptr,0,state_size); OPL = (FM_OPL *)ptr; ptr += sizeof(FM_OPL); #if BUILD_Y8950 if (type&OPL_TYPE_ADPCM) { OPL->deltat = (YM_DELTAT *)ptr; } ptr += sizeof(YM_DELTAT); #endif OPL->type = type; OPL->clock = clock; OPL->rate = rate; /* init global tables */ OPL_initalize(OPL); return OPL; } /* Destroy one of virtual YM3812 */ static void OPLDestroy(FM_OPL *OPL) { OPL_UnLockTable(); free(OPL); } /* Optional handlers */ static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param) { OPL->timer_handler = timer_handler; OPL->TimerParam = param; } static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param) { OPL->IRQHandler = IRQHandler; OPL->IRQParam = param; } static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param) { OPL->UpdateHandler = UpdateHandler; OPL->UpdateParam = param; } static int OPLWrite(FM_OPL *OPL,int a,int v) { if( !(a&1) ) { /* address port */ OPL->address = v & 0xff; } else { /* data port */ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/); OPLWriteReg(OPL,OPL->address,v); } return OPL->status>>7; } static unsigned char OPLRead(FM_OPL *OPL,int a) { if( !(a&1) ) { /* status port */ #if BUILD_Y8950 if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ { return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); } #endif /* OPL and OPL2 */ return OPL->status & (OPL->statusmask|0x80); } #if BUILD_Y8950 /* data port */ switch(OPL->address) { case 0x05: /* KeyBoard IN */ if(OPL->type&OPL_TYPE_KEYBOARD) { if(OPL->keyboardhandler_r) return OPL->keyboardhandler_r(OPL->keyboard_param); #ifdef _DEBUG else logerror("Y8950: read unmapped KEYBOARD port\n"); #endif } return 0; case 0x0f: /* ADPCM-DATA */ if(OPL->type&OPL_TYPE_ADPCM) { UINT8 val; val = YM_DELTAT_ADPCM_Read(OPL->deltat); /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ return val; } return 0; case 0x19: /* I/O DATA */ if(OPL->type&OPL_TYPE_IO) { if(OPL->porthandler_r) return OPL->porthandler_r(OPL->port_param); #ifdef _DEBUG else logerror("Y8950:read unmapped I/O port\n"); #endif } return 0; case 0x1a: /* PCM-DATA */ if(OPL->type&OPL_TYPE_ADPCM) { #ifdef _DEBUG logerror("Y8950 A/D conversion is accessed but not implemented !\n"); #endif return 0x80; /* 2's complement PCM data - result from A/D conversion */ } return 0; } #endif return 0xff; } /* CSM Key Controll */ INLINE void CSMKeyControll(OPL_CH *CH) { FM_KEYON (&CH->SLOT[SLOT1], 4); FM_KEYON (&CH->SLOT[SLOT2], 4); /* The key off should happen exactly one sample later - not implemented correctly yet */ FM_KEYOFF(&CH->SLOT[SLOT1], ~4); FM_KEYOFF(&CH->SLOT[SLOT2], ~4); } static int OPLTimerOver(FM_OPL *OPL,int c) { if( c ) { /* Timer B */ OPL_STATUS_SET(OPL,0x20); } else { /* Timer A */ OPL_STATUS_SET(OPL,0x40); /* CSM mode key,TL controll */ if( OPL->mode & 0x80 ) { /* CSM mode total level latch and auto key on */ int ch; if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/); for(ch=0; ch<9; ch++) CSMKeyControll( &OPL->P_CH[ch] ); } } /* reload timer */ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c])); return OPL->status>>7; } #define MAX_OPL_CHIPS 2 #if (BUILD_YM3812) void * ym3812_init(UINT32 clock, UINT32 rate) { /* emulator create */ FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812); if (YM3812) { //OPL_save_state(YM3812); ym3812_reset_chip(YM3812); } return YM3812; } void ym3812_shutdown(void *chip) { FM_OPL *YM3812 = (FM_OPL *)chip; /* emulator shutdown */ OPLDestroy(YM3812); } void ym3812_reset_chip(void *chip) { FM_OPL *YM3812 = (FM_OPL *)chip; OPLResetChip(YM3812); } int ym3812_write(void *chip, int a, int v) { FM_OPL *YM3812 = (FM_OPL *)chip; return OPLWrite(YM3812, a, v); } unsigned char ym3812_read(void *chip, int a) { FM_OPL *YM3812 = (FM_OPL *)chip; /* YM3812 always returns bit2 and bit1 in HIGH state */ return OPLRead(YM3812, a) | 0x06 ; } int ym3812_timer_over(void *chip, int c) { FM_OPL *YM3812 = (FM_OPL *)chip; return OPLTimerOver(YM3812, c); } void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) { FM_OPL *YM3812 = (FM_OPL *)chip; OPLSetTimerHandler(YM3812, timer_handler, param); } void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) { FM_OPL *YM3812 = (FM_OPL *)chip; OPLSetIRQHandler(YM3812, IRQHandler, param); } void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) { FM_OPL *YM3812 = (FM_OPL *)chip; OPLSetUpdateHandler(YM3812, UpdateHandler, param); } /* ** Generate samples for one of the YM3812's ** ** 'which' is the virtual YM3812 number ** '*buffer' is the output buffer pointer ** 'length' is the number of samples that should be generated */ void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length) { FM_OPL *OPL = (FM_OPL *)chip; UINT8 rhythm = OPL->rhythm&0x20; OPLSAMPLE *bufL = buffer[0]; OPLSAMPLE *bufR = buffer[1]; int i; if (! length) { refresh_eg(OPL); return; } for( i=0; i < length ; i++ ) { int lt; OPL->output[0] = 0; advance_lfo(OPL); /* FM part */ OPL_CALC_CH(OPL, &OPL->P_CH[0]); OPL_CALC_CH(OPL, &OPL->P_CH[1]); OPL_CALC_CH(OPL, &OPL->P_CH[2]); OPL_CALC_CH(OPL, &OPL->P_CH[3]); OPL_CALC_CH(OPL, &OPL->P_CH[4]); OPL_CALC_CH(OPL, &OPL->P_CH[5]); if(!rhythm) { OPL_CALC_CH(OPL, &OPL->P_CH[6]); OPL_CALC_CH(OPL, &OPL->P_CH[7]); OPL_CALC_CH(OPL, &OPL->P_CH[8]); } else /* Rhythm part */ { OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); } lt = OPL->output[0]; lt >>= FINAL_SH; /* limit check */ //lt = limit( lt , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE if (which==0) { SAVE_ALL_CHANNELS } #endif /* store to sound buffer */ bufL[i] = lt; bufR[i] = lt; advance(OPL); } } #endif /* BUILD_YM3812 */ #if (BUILD_YM3526) void *ym3526_init(UINT32 clock, UINT32 rate) { /* emulator create */ FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526); if (YM3526) { //OPL_save_state(YM3526); ym3526_reset_chip(YM3526); } return YM3526; } void ym3526_shutdown(void *chip) { FM_OPL *YM3526 = (FM_OPL *)chip; /* emulator shutdown */ OPLDestroy(YM3526); } void ym3526_reset_chip(void *chip) { FM_OPL *YM3526 = (FM_OPL *)chip; OPLResetChip(YM3526); } int ym3526_write(void *chip, int a, int v) { FM_OPL *YM3526 = (FM_OPL *)chip; return OPLWrite(YM3526, a, v); } unsigned char ym3526_read(void *chip, int a) { FM_OPL *YM3526 = (FM_OPL *)chip; /* YM3526 always returns bit2 and bit1 in HIGH state */ return OPLRead(YM3526, a) | 0x06 ; } int ym3526_timer_over(void *chip, int c) { FM_OPL *YM3526 = (FM_OPL *)chip; return OPLTimerOver(YM3526, c); } void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) { FM_OPL *YM3526 = (FM_OPL *)chip; OPLSetTimerHandler(YM3526, timer_handler, param); } void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) { FM_OPL *YM3526 = (FM_OPL *)chip; OPLSetIRQHandler(YM3526, IRQHandler, param); } void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) { FM_OPL *YM3526 = (FM_OPL *)chip; OPLSetUpdateHandler(YM3526, UpdateHandler, param); } /* ** Generate samples for one of the YM3526's ** ** 'which' is the virtual YM3526 number ** '*buffer' is the output buffer pointer ** 'length' is the number of samples that should be generated */ void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length) { FM_OPL *OPL = (FM_OPL *)chip; UINT8 rhythm = OPL->rhythm&0x20; OPLSAMPLE *bufL = buffer[0]; OPLSAMPLE *bufR = buffer[1]; int i; for( i=0; i < length ; i++ ) { int lt; OPL->output[0] = 0; advance_lfo(OPL); /* FM part */ OPL_CALC_CH(OPL, &OPL->P_CH[0]); OPL_CALC_CH(OPL, &OPL->P_CH[1]); OPL_CALC_CH(OPL, &OPL->P_CH[2]); OPL_CALC_CH(OPL, &OPL->P_CH[3]); OPL_CALC_CH(OPL, &OPL->P_CH[4]); OPL_CALC_CH(OPL, &OPL->P_CH[5]); if(!rhythm) { OPL_CALC_CH(OPL, &OPL->P_CH[6]); OPL_CALC_CH(OPL, &OPL->P_CH[7]); OPL_CALC_CH(OPL, &OPL->P_CH[8]); } else /* Rhythm part */ { OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); } lt = OPL->output[0]; lt >>= FINAL_SH; /* limit check */ //lt = limit( lt , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE if (which==0) { SAVE_ALL_CHANNELS } #endif /* store to sound buffer */ bufL[i] = lt; bufR[i] = lt; advance(OPL); } } #endif /* BUILD_YM3526 */ #if BUILD_Y8950 static void Y8950_deltat_status_set(void *chip, UINT8 changebits) { FM_OPL *Y8950 = (FM_OPL *)chip; OPL_STATUS_SET(Y8950, changebits); } static void Y8950_deltat_status_reset(void *chip, UINT8 changebits) { FM_OPL *Y8950 = (FM_OPL *)chip; OPL_STATUS_RESET(Y8950, changebits); } void *y8950_init(UINT32 clock, UINT32 rate) { /* emulator create */ FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950); if (Y8950) { Y8950->deltat->memory = NULL; Y8950->deltat->memory_size = 0x00; Y8950->deltat->memory_mask = 0x00; Y8950->deltat->status_set_handler = Y8950_deltat_status_set; Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; Y8950->deltat->status_change_which_chip = Y8950; Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ /* reset */ //OPL_save_state(Y8950); y8950_reset_chip(Y8950); } return Y8950; } void y8950_shutdown(void *chip) { FM_OPL *Y8950 = (FM_OPL *)chip; free(Y8950->deltat->memory); Y8950->deltat->memory = NULL; /* emulator shutdown */ OPLDestroy(Y8950); } void y8950_reset_chip(void *chip) { FM_OPL *Y8950 = (FM_OPL *)chip; OPLResetChip(Y8950); } int y8950_write(void *chip, int a, int v) { FM_OPL *Y8950 = (FM_OPL *)chip; return OPLWrite(Y8950, a, v); } unsigned char y8950_read(void *chip, int a) { FM_OPL *Y8950 = (FM_OPL *)chip; return OPLRead(Y8950, a); } int y8950_timer_over(void *chip, int c) { FM_OPL *Y8950 = (FM_OPL *)chip; return OPLTimerOver(Y8950, c); } void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) { FM_OPL *Y8950 = (FM_OPL *)chip; OPLSetTimerHandler(Y8950, timer_handler, param); } void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) { FM_OPL *Y8950 = (FM_OPL *)chip; OPLSetIRQHandler(Y8950, IRQHandler, param); } void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) { FM_OPL *Y8950 = (FM_OPL *)chip; OPLSetUpdateHandler(Y8950, UpdateHandler, param); } void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ) { FM_OPL *OPL = (FM_OPL *)chip; OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr); OPL->deltat->memory_size = deltat_mem_size; } void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { FM_OPL *Y8950 = (FM_OPL *)chip; if (Y8950->deltat->memory_size != ROMSize) { Y8950->deltat->memory = (UINT8*)realloc(Y8950->deltat->memory, ROMSize); Y8950->deltat->memory_size = ROMSize; memset(Y8950->deltat->memory, 0xFF, ROMSize); YM_DELTAT_calc_mem_mask(Y8950->deltat); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(Y8950->deltat->memory + DataStart, ROMData, DataLength); return; } /* ** Generate samples for one of the Y8950's ** ** 'which' is the virtual Y8950 number ** '*buffer' is the output buffer pointer ** 'length' is the number of samples that should be generated */ void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length) { int i; FM_OPL *OPL = (FM_OPL *)chip; UINT8 rhythm = OPL->rhythm&0x20; YM_DELTAT *DELTAT = OPL->deltat; OPLSAMPLE *bufL = buffer[0]; OPLSAMPLE *bufR = buffer[1]; for( i=0; i < length ; i++ ) { int lt; OPL->output[0] = 0; OPL->output_deltat[0] = 0; advance_lfo(OPL); /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! OPL->MuteSpc[5] ) YM_DELTAT_ADPCM_CALC(DELTAT); /* FM part */ OPL_CALC_CH(OPL, &OPL->P_CH[0]); OPL_CALC_CH(OPL, &OPL->P_CH[1]); OPL_CALC_CH(OPL, &OPL->P_CH[2]); OPL_CALC_CH(OPL, &OPL->P_CH[3]); OPL_CALC_CH(OPL, &OPL->P_CH[4]); OPL_CALC_CH(OPL, &OPL->P_CH[5]); if(!rhythm) { OPL_CALC_CH(OPL, &OPL->P_CH[6]); OPL_CALC_CH(OPL, &OPL->P_CH[7]); OPL_CALC_CH(OPL, &OPL->P_CH[8]); } else /* Rhythm part */ { OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); } lt = OPL->output[0] + (OPL->output_deltat[0]>>11); lt >>= FINAL_SH; /* limit check */ //lt = limit( lt , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE if (which==0) { SAVE_ALL_CHANNELS } #endif /* store to sound buffer */ bufL[i] = lt; bufR[i] = lt; advance(OPL); } } void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param) { FM_OPL *OPL = (FM_OPL *)chip; OPL->porthandler_w = PortHandler_w; OPL->porthandler_r = PortHandler_r; OPL->port_param = param; } void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param) { FM_OPL *OPL = (FM_OPL *)chip; OPL->keyboardhandler_w = KeyboardHandler_w; OPL->keyboardhandler_r = KeyboardHandler_r; OPL->keyboard_param = param; } #endif void opl_set_mute_mask(void *chip, UINT32 MuteMask) { FM_OPL *opl = (FM_OPL *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 9; CurChn ++) opl->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 6; CurChn ++) opl->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01; return; } ================================================ FILE: VGMPlay/chips/fmopl.h ================================================ #pragma once //#include "attotime.h" /* --- select emulation chips --- */ //#define BUILD_YM3812 (HAS_YM3812) //#define BUILD_YM3526 (HAS_YM3526) //#define BUILD_Y8950 (HAS_Y8950) #define BUILD_YM3812 1 #define BUILD_YM3526 1 #ifndef NO_Y8950 #define BUILD_Y8950 1 #else #define BUILD_Y8950 0 #endif /* select output bits size of output : 8 or 16 */ #define OPL_SAMPLE_BITS 16 /* compiler dependence */ /*#ifndef __OSDCOMM_H__ #define __OSDCOMM_H__ typedef unsigned char UINT8; // unsigned 8bit typedef unsigned short UINT16; // unsigned 16bit typedef unsigned int UINT32; // unsigned 32bit typedef signed char INT8; // signed 8bit typedef signed short INT16; // signed 16bit typedef signed int INT32; // signed 32bit #endif*/ /* __OSDCOMM_H__ */ typedef stream_sample_t OPLSAMPLE; /* #if (OPL_SAMPLE_BITS==16) typedef INT16 OPLSAMPLE; #endif #if (OPL_SAMPLE_BITS==8) typedef INT8 OPLSAMPLE; #endif */ //typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period); typedef void (*OPL_TIMERHANDLER)(void *param,int timer,int period); typedef void (*OPL_IRQHANDLER)(void *param,int irq); typedef void (*OPL_UPDATEHANDLER)(void *param/*,int min_interval_us*/); typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data); typedef unsigned char (*OPL_PORTHANDLER_R)(void *param); #if BUILD_YM3812 void *ym3812_init(UINT32 clock, UINT32 rate); void ym3812_shutdown(void *chip); void ym3812_reset_chip(void *chip); int ym3812_write(void *chip, int a, int v); unsigned char ym3812_read(void *chip, int a); int ym3812_timer_over(void *chip, int c); void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length); void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); #endif /* BUILD_YM3812 */ #if BUILD_YM3526 /* ** Initialize YM3526 emulator(s). ** ** 'num' is the number of virtual YM3526's to allocate ** 'clock' is the chip clock in Hz ** 'rate' is sampling rate */ void *ym3526_init(UINT32 clock, UINT32 rate); /* shutdown the YM3526 emulators*/ void ym3526_shutdown(void *chip); void ym3526_reset_chip(void *chip); int ym3526_write(void *chip, int a, int v); unsigned char ym3526_read(void *chip, int a); int ym3526_timer_over(void *chip, int c); /* ** Generate samples for one of the YM3526's ** ** 'which' is the virtual YM3526 number ** '*buffer' is the output buffer pointer ** 'length' is the number of samples that should be generated */ void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length); void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); #endif /* BUILD_YM3526 */ #if BUILD_Y8950 /* Y8950 port handlers */ void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param); void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param); void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ); void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void * y8950_init(UINT32 clock, UINT32 rate); void y8950_shutdown(void *chip); void y8950_reset_chip(void *chip); int y8950_write(void *chip, int a, int v); unsigned char y8950_read (void *chip, int a); int y8950_timer_over(void *chip, int c); void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length); void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); #endif /* BUILD_Y8950 */ void opl_set_mute_mask(void *chip, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/gb.c ================================================ // license:BSD-3-Clause // copyright-holders:Wilbert Pol, Anthony Kruize // thanks-to:Shay Green /************************************************************************************** * Game Boy sound emulation (c) Anthony Kruize (trandor@labyrinth.net.au) * * Anyways, sound on the Game Boy consists of 4 separate 'channels' * Sound1 = Quadrangular waves with SWEEP and ENVELOPE functions (NR10,11,12,13,14) * Sound2 = Quadrangular waves with ENVELOPE functions (NR21,22,23,24) * Sound3 = Wave patterns from WaveRAM (NR30,31,32,33,34) * Sound4 = White noise with an envelope (NR41,42,43,44) * * Each sound channel has 2 modes, namely ON and OFF... whoa * * These tend to be the two most important equations in * converting between Hertz and GB frequency registers: * (Sounds will have a 2.4% higher frequency on Super GB.) * gb = 2048 - (131072 / Hz) * Hz = 131072 / (2048 - gb) * * Changes: * * 10/2/2002 AK - Preliminary sound code. * 13/2/2002 AK - Added a hack for mode 4, other fixes. * 23/2/2002 AK - Use lookup tables, added sweep to mode 1. Re-wrote the square * wave generation. * 13/3/2002 AK - Added mode 3, better lookup tables, other adjustments. * 15/3/2002 AK - Mode 4 can now change frequencies. * 31/3/2002 AK - Accidently forgot to handle counter/consecutive for mode 1. * 3/4/2002 AK - Mode 1 sweep can still occur if shift is 0. Don't let frequency * go past the maximum allowed value. Fixed Mode 3 length table. * Slight adjustment to Mode 4's period table generation. * 5/4/2002 AK - Mode 4 is done correctly, using a polynomial counter instead * of being a total hack. * 6/4/2002 AK - Slight tweak to mode 3's frequency calculation. * 13/4/2002 AK - Reset envelope value when sound is initialized. * 21/4/2002 AK - Backed out the mode 3 frequency calculation change. * Merged init functions into gameboy_sound_w(). * 14/5/2002 AK - Removed magic numbers in the fixed point math. * 12/6/2002 AK - Merged SOUNDx structs into one SOUND struct. * 26/10/2002 AK - Finally fixed channel 3! * xx/4-5/2016 WP - Rewrote sound core. Most of the code is not optimized yet. TODO: - Implement different behavior of CGB-02. - Implement different behavior of CGB-05. - Perform more tests on real hardware to figure out when the frequency counters are reloaded. - Perform more tests on real hardware to understand when changes to the noise divisor and shift kick in. - Optimize the channel update methods. ***************************************************************************************/ #include "mamedef.h" #include // for rand #include // for memset //#include "emu.h" #include "gb.h" //#include "streams.h" typedef UINT8 bool; #define false 0x00 #define true 0x01 #define RC_SHIFT 16 typedef struct { UINT32 inc; // counter increment UINT32 val; // current value } RATIO_CNTR; INLINE void RC_SET_RATIO(RATIO_CNTR* rc, UINT32 mul, UINT32 div) { rc->inc = (UINT32)((((UINT64)mul << RC_SHIFT) + div / 2) / div); } /*************************************************************************** CONSTANTS ***************************************************************************/ #define NR10 0x00 #define NR11 0x01 #define NR12 0x02 #define NR13 0x03 #define NR14 0x04 // 0x05 #define NR21 0x06 #define NR22 0x07 #define NR23 0x08 #define NR24 0x09 #define NR30 0x0A #define NR31 0x0B #define NR32 0x0C #define NR33 0x0D #define NR34 0x0E // 0x0F #define NR41 0x10 #define NR42 0x11 #define NR43 0x12 #define NR44 0x13 #define NR50 0x14 #define NR51 0x15 #define NR52 0x16 // 0x17 - 0x1F #define AUD3W0 0x20 #define AUD3W1 0x21 #define AUD3W2 0x22 #define AUD3W3 0x23 #define AUD3W4 0x24 #define AUD3W5 0x25 #define AUD3W6 0x26 #define AUD3W7 0x27 #define AUD3W8 0x28 #define AUD3W9 0x29 #define AUD3WA 0x2A #define AUD3WB 0x2B #define AUD3WC 0x2C #define AUD3WD 0x2D #define AUD3WE 0x2E #define AUD3WF 0x2F #define FRAME_CYCLES 8192 /* Represents wave duties of 12.5%, 25%, 50% and 75% */ static const int wave_duty_table[4][8] = { { -1, -1, -1, -1, -1, -1, -1, 1}, { 1, -1, -1, -1, -1, -1, -1, 1}, { 1, -1, -1, -1, -1, 1, 1, 1}, { -1, 1, 1, 1, 1, 1, 1, -1} }; /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ struct SOUND { /* Common */ UINT8 reg[5]; bool on; UINT8 channel; UINT8 length; UINT8 length_mask; bool length_counting; bool length_enabled; /* Mode 1, 2, 3 */ UINT32 cycles_left; INT8 duty; /* Mode 1, 2, 4 */ bool envelope_enabled; INT8 envelope_value; INT8 envelope_direction; UINT8 envelope_time; UINT8 envelope_count; INT8 signal; /* Mode 1 */ UINT16 frequency; UINT16 frequency_counter; bool sweep_enabled; bool sweep_neg_mode_used; UINT8 sweep_shift; INT32 sweep_direction; UINT8 sweep_time; UINT8 sweep_count; /* Mode 3 */ UINT8 level; UINT8 offset; UINT32 duty_count; INT8 current_sample; bool sample_reading; /* Mode 4 */ bool noise_short; UINT16 noise_lfsr; UINT8 Muted; }; struct SOUNDC { UINT8 on; UINT8 vol_left; UINT8 vol_right; UINT8 mode1_left; UINT8 mode1_right; UINT8 mode2_left; UINT8 mode2_right; UINT8 mode3_left; UINT8 mode3_right; UINT8 mode4_left; UINT8 mode4_right; UINT32 cycles; bool wave_ram_locked; }; #define GBMODE_DMG 0x00 #define GBMODE_CGB04 0x01 typedef struct _gb_sound_t gb_sound_t; struct _gb_sound_t { UINT32 rate; struct SOUND snd_1; struct SOUND snd_2; struct SOUND snd_3; struct SOUND snd_4; struct SOUNDC snd_control; UINT8 snd_regs[0x30]; RATIO_CNTR cycleCntr; UINT8 gbMode; //UINT8 BoostWaveChn; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static gb_sound_t GBSoundData[MAX_CHIPS]; static UINT8 BoostWaveChn = 0x00; static void gb_corrupt_wave_ram(gb_sound_t *gb); static void gb_apu_power_off(gb_sound_t *gb); static void gb_tick_length(struct SOUND *snd); static INT32 gb_calculate_next_sweep(struct SOUND *snd); INLINE bool gb_dac_enabled(struct SOUND *snd); INLINE UINT32 gb_noise_period_cycles(gb_sound_t *gb); /*************************************************************************** IMPLEMENTATION ***************************************************************************/ UINT8 gb_wave_r(UINT8 ChipID, offs_t offset) { gb_sound_t *gb = &GBSoundData[ChipID]; //gb_update_state(gb, 0); if (gb->snd_3.on) { if (gb->gbMode == GBMODE_DMG) return gb->snd_3.sample_reading ? gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)] : 0xFF; else if (gb->gbMode == GBMODE_CGB04) return gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)]; } return gb->snd_regs[AUD3W0 + offset]; } void gb_wave_w(UINT8 ChipID, offs_t offset, UINT8 data) { gb_sound_t *gb = &GBSoundData[ChipID]; //gb_update_state(gb, 0); if (gb->snd_3.on) { if (gb->gbMode == GBMODE_DMG) { if (gb->snd_3.sample_reading) { gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)] = data; } } else if (gb->gbMode == GBMODE_CGB04) { gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)] = data; } } else { gb->snd_regs[AUD3W0 + offset] = data; } } UINT8 gb_sound_r(UINT8 ChipID, offs_t offset) { static const UINT8 read_mask[0x40] = { 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, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; gb_sound_t *gb = &GBSoundData[ChipID]; //gb_update_state(gb, 0); if (offset < AUD3W0) { if (gb->snd_control.on) { if (offset == NR52) { return (gb->snd_regs[NR52]&0xf0) | (gb->snd_1.on ? 1 : 0) | (gb->snd_2.on ? 2 : 0) | (gb->snd_3.on ? 4 : 0) | (gb->snd_4.on ? 8 : 0) | 0x70; } return gb->snd_regs[offset] | read_mask[offset & 0x3F]; } else { return read_mask[offset & 0x3F]; } } else if (offset <= AUD3WF) { return gb_wave_r(ChipID, offset - AUD3W0); } return 0xFF; } void gb_sound_w_internal(gb_sound_t *gb, UINT8 offset, UINT8 data) { /* Store the value */ UINT8 old_data = gb->snd_regs[offset]; if (gb->snd_control.on) { gb->snd_regs[offset] = data; } switch (offset) { /*MODE 1 */ case NR10: /* Sweep (R/W) */ gb->snd_1.reg[0] = data; gb->snd_1.sweep_shift = data & 0x7; gb->snd_1.sweep_direction = (data & 0x8) ? -1 : 1; gb->snd_1.sweep_time = (data & 0x70) >> 4; if ((old_data & 0x08) && !(data & 0x08) && gb->snd_1.sweep_neg_mode_used) { gb->snd_1.on = false; } break; case NR11: /* Sound length/Wave pattern duty (R/W) */ gb->snd_1.reg[1] = data; if (gb->snd_control.on) { gb->snd_1.duty = (data & 0xc0) >> 6; } gb->snd_1.length = data & 0x3f; gb->snd_1.length_counting = true; break; case NR12: /* Envelope (R/W) */ gb->snd_1.reg[2] = data; gb->snd_1.envelope_value = data >> 4; gb->snd_1.envelope_direction = (data & 0x8) ? 1 : -1; gb->snd_1.envelope_time = data & 0x07; if (!gb_dac_enabled(&gb->snd_1)) { gb->snd_1.on = false; } break; case NR13: /* Frequency lo (R/W) */ gb->snd_1.reg[3] = data; // Only enabling the frequency line breaks blarggs's sound test #5 // This condition may not be correct if (!gb->snd_1.sweep_enabled) { gb->snd_1.frequency = ((gb->snd_1.reg[4] & 0x7) << 8) | gb->snd_1.reg[3]; } break; case NR14: /* Frequency hi / Initialize (R/W) */ gb->snd_1.reg[4] = data; { bool length_was_enabled = gb->snd_1.length_enabled; gb->snd_1.length_enabled = (data & 0x40) ? true : false; gb->snd_1.frequency = ((gb->snd_regs[NR14] & 0x7) << 8) | gb->snd_1.reg[3]; if (!length_was_enabled && !(gb->snd_control.cycles & FRAME_CYCLES) && gb->snd_1.length_counting) { if (gb->snd_1.length_enabled) { gb_tick_length(&gb->snd_1); } } if (data & 0x80) { gb->snd_1.on = true; gb->snd_1.envelope_enabled = true; gb->snd_1.envelope_value = gb->snd_1.reg[2] >> 4; gb->snd_1.envelope_count = gb->snd_1.envelope_time; gb->snd_1.sweep_count = gb->snd_1.sweep_time; gb->snd_1.sweep_neg_mode_used = false; gb->snd_1.signal = 0; gb->snd_1.length = gb->snd_1.reg[1] & 0x3f; // VGM log fix -Valley Bell gb->snd_1.length_counting = true; gb->snd_1.frequency = ((gb->snd_1.reg[4] & 0x7) << 8) | gb->snd_1.reg[3]; gb->snd_1.frequency_counter = gb->snd_1.frequency; gb->snd_1.cycles_left = 0; gb->snd_1.duty_count = 0; gb->snd_1.sweep_enabled = (gb->snd_1.sweep_shift != 0) || (gb->snd_1.sweep_time != 0); if (!gb_dac_enabled(&gb->snd_1)) { gb->snd_1.on = false; } if (gb->snd_1.sweep_shift > 0) { gb_calculate_next_sweep(&gb->snd_1); } if (gb->snd_1.length == 0 && gb->snd_1.length_enabled && !(gb->snd_control.cycles & FRAME_CYCLES)) { gb_tick_length(&gb->snd_1); } } else { // This condition may not be correct if (!gb->snd_1.sweep_enabled) { gb->snd_1.frequency = ((gb->snd_1.reg[4] & 0x7) << 8) | gb->snd_1.reg[3]; } } } break; /*MODE 2 */ case NR21: /* Sound length/Wave pattern duty (R/W) */ gb->snd_2.reg[1] = data; if (gb->snd_control.on) { gb->snd_2.duty = (data & 0xc0) >> 6; } gb->snd_2.length = data & 0x3f; gb->snd_2.length_counting = true; break; case NR22: /* Envelope (R/W) */ gb->snd_2.reg[2] = data; gb->snd_2.envelope_value = data >> 4; gb->snd_2.envelope_direction = (data & 0x8) ? 1 : -1; gb->snd_2.envelope_time = data & 0x07; if (!gb_dac_enabled(&gb->snd_2)) { gb->snd_2.on = false; } break; case NR23: /* Frequency lo (R/W) */ gb->snd_2.reg[3] = data; gb->snd_2.frequency = ((gb->snd_2.reg[4] & 0x7) << 8) | gb->snd_2.reg[3]; break; case NR24: /* Frequency hi / Initialize (R/W) */ gb->snd_2.reg[4] = data; { bool length_was_enabled = gb->snd_2.length_enabled; gb->snd_2.length_enabled = (data & 0x40) ? true : false; if (!length_was_enabled && !(gb->snd_control.cycles & FRAME_CYCLES) && gb->snd_2.length_counting) { if (gb->snd_2.length_enabled) { gb_tick_length(&gb->snd_2); } } if (data & 0x80) { gb->snd_2.on = true; gb->snd_2.envelope_enabled = true; gb->snd_2.envelope_value = gb->snd_2.reg[2] >> 4; gb->snd_2.envelope_count = gb->snd_2.envelope_time; gb->snd_2.frequency = ((gb->snd_2.reg[4] & 0x7) << 8) | gb->snd_2.reg[3]; gb->snd_2.frequency_counter = gb->snd_2.frequency; gb->snd_2.cycles_left = 0; gb->snd_2.duty_count = 0; gb->snd_2.signal = 0; gb->snd_2.length = gb->snd_2.reg[1] & 0x3f; // VGM log fix -Valley Bell gb->snd_2.length_counting = true; if (!gb_dac_enabled(&gb->snd_2)) { gb->snd_2.on = false; } if (gb->snd_2.length == 0 && gb->snd_2.length_enabled && !(gb->snd_control.cycles & FRAME_CYCLES)) { gb_tick_length(&gb->snd_2); } } else { gb->snd_2.frequency = ((gb->snd_2.reg[4] & 0x7) << 8) | gb->snd_2.reg[3]; } } break; /*MODE 3 */ case NR30: /* Sound On/Off (R/W) */ gb->snd_3.reg[0] = data; if (!gb_dac_enabled(&gb->snd_3)) { gb->snd_3.on = false; } break; case NR31: /* Sound Length (R/W) */ gb->snd_3.reg[1] = data; gb->snd_3.length = data; gb->snd_3.length_counting = true; break; case NR32: /* Select Output Level */ gb->snd_3.reg[2] = data; gb->snd_3.level = (data & 0x60) >> 5; break; case NR33: /* Frequency lo (W) */ gb->snd_3.reg[3] = data; gb->snd_3.frequency = ((gb->snd_3.reg[4] & 0x7) << 8) | gb->snd_3.reg[3]; break; case NR34: /* Frequency hi / Initialize (W) */ gb->snd_3.reg[4] = data; { bool length_was_enabled = gb->snd_3.length_enabled; gb->snd_3.length_enabled = (data & 0x40) ? true : false; if (!length_was_enabled && !(gb->snd_control.cycles & FRAME_CYCLES) && gb->snd_3.length_counting) { if (gb->snd_3.length_enabled) { gb_tick_length(&gb->snd_3); } } if (data & 0x80) { if (gb->snd_3.on && gb->snd_3.frequency_counter == 0x7ff) { gb_corrupt_wave_ram(gb); } gb->snd_3.on = true; gb->snd_3.offset = 0; gb->snd_3.duty = 1; gb->snd_3.duty_count = 0; gb->snd_3.length = gb->snd_3.reg[1]; // VGM log fix -Valley Bell gb->snd_3.length_counting = true; gb->snd_3.frequency = ((gb->snd_3.reg[4] & 0x7) << 8) | gb->snd_3.reg[3]; gb->snd_3.frequency_counter = gb->snd_3.frequency; // There is a tiny bit of delay in starting up the wave channel(?) // // Results from older code where corruption of wave ram was triggered when sample_reading == true: // 4 breaks test 09 (read wram), fixes test 10 (write trigger), breaks test 12 (write wram) // 6 fixes test 09 (read wram), breaks test 10 (write trigger), fixes test 12 (write wram) gb->snd_3.cycles_left = 0 + 6; gb->snd_3.sample_reading = false; if (!gb_dac_enabled(&gb->snd_3)) { gb->snd_3.on = false; } if (gb->snd_3.length == 0 && gb->snd_3.length_enabled && !(gb->snd_control.cycles & FRAME_CYCLES)) { gb_tick_length(&gb->snd_3); } } else { gb->snd_3.frequency = ((gb->snd_3.reg[4] & 0x7) << 8) | gb->snd_3.reg[3]; } } break; /*MODE 4 */ case NR41: /* Sound Length (R/W) */ gb->snd_4.reg[1] = data; gb->snd_4.length = data & 0x3f; gb->snd_4.length_counting = true; break; case NR42: /* Envelope (R/W) */ gb->snd_4.reg[2] = data; gb->snd_4.envelope_value = data >> 4; gb->snd_4.envelope_direction = (data & 0x8) ? 1 : -1; gb->snd_4.envelope_time = data & 0x07; if (!gb_dac_enabled(&gb->snd_4)) { gb->snd_4.on = false; } break; case NR43: /* Polynomial Counter/Frequency */ gb->snd_4.reg[3] = data; gb->snd_4.noise_short = (data & 0x8); break; case NR44: /* Counter/Consecutive / Initialize (R/W) */ gb->snd_4.reg[4] = data; { bool length_was_enabled = gb->snd_4.length_enabled; gb->snd_4.length_enabled = (data & 0x40) ? true : false; if (!length_was_enabled && !(gb->snd_control.cycles & FRAME_CYCLES) && gb->snd_4.length_counting) { if (gb->snd_4.length_enabled) { gb_tick_length(&gb->snd_4); } } if (data & 0x80) { gb->snd_4.on = true; gb->snd_4.envelope_enabled = true; gb->snd_4.envelope_value = gb->snd_4.reg[2] >> 4; gb->snd_4.envelope_count = gb->snd_4.envelope_time; gb->snd_4.frequency_counter = 0; gb->snd_4.cycles_left = gb_noise_period_cycles(gb); gb->snd_4.signal = -1; gb->snd_4.noise_lfsr = 0x7fff; gb->snd_4.length = gb->snd_4.reg[1] & 0x3f; // VGM log fix -Valley Bell gb->snd_4.length_counting = true; if (!gb_dac_enabled(&gb->snd_4)) { gb->snd_4.on = false; } if (gb->snd_4.length == 0 && gb->snd_4.length_enabled && !(gb->snd_control.cycles & FRAME_CYCLES)) { gb_tick_length(&gb->snd_4); } } } break; /* CONTROL */ case NR50: /* Channel Control / On/Off / Volume (R/W) */ gb->snd_control.vol_left = data & 0x7; gb->snd_control.vol_right = (data & 0x70) >> 4; break; case NR51: /* Selection of Sound Output Terminal */ gb->snd_control.mode1_right = data & 0x1; gb->snd_control.mode1_left = (data & 0x10) >> 4; gb->snd_control.mode2_right = (data & 0x2) >> 1; gb->snd_control.mode2_left = (data & 0x20) >> 5; gb->snd_control.mode3_right = (data & 0x4) >> 2; gb->snd_control.mode3_left = (data & 0x40) >> 6; gb->snd_control.mode4_right = (data & 0x8) >> 3; gb->snd_control.mode4_left = (data & 0x80) >> 7; break; case NR52: // Sound On/Off (R/W) // Only bit 7 is writable, writing to bits 0-3 does NOT enable or disable sound. They are read-only. if (!(data & 0x80)) { // On DMG the length counters are not affected and not clocked // powering off should actually clear all registers gb_apu_power_off(gb); } else { if (!gb->snd_control.on) { // When switching on, the next step should be 0. gb->snd_control.cycles |= 7 * FRAME_CYCLES; } } gb->snd_control.on = (data & 0x80) ? true : false; gb->snd_regs[NR52] = data & 0x80; break; } } void gb_sound_w(UINT8 ChipID, offs_t offset, UINT8 data) { gb_sound_t *gb = &GBSoundData[ChipID]; //gb_update_state(gb, 0); if (offset < AUD3W0) { if (gb->gbMode == GBMODE_DMG) { /* Only register NR52 is accessible if the sound controller is disabled */ if( !gb->snd_control.on && offset != NR52 && offset != NR11 && offset != NR21 && offset != NR31 && offset != NR41) return; } else if (gb->gbMode == GBMODE_CGB04) { /* Only register NR52 is accessible if the sound controller is disabled */ if (!gb->snd_control.on && offset != NR52) return; } gb_sound_w_internal(gb, offset, data); } else if (offset <= AUD3WF) { gb_wave_w(ChipID, offset - AUD3W0, data); } } static void gb_corrupt_wave_ram(gb_sound_t *gb) { if (gb->gbMode != GBMODE_DMG) return; if (gb->snd_3.offset < 8) { gb->snd_regs[AUD3W0] = gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)]; } else { int i; for (i = 0; i < 4; i++) { gb->snd_regs[AUD3W0 + i] = gb->snd_regs[AUD3W0 + ((gb->snd_3.offset / 2) & ~0x03) + i]; } } } static void gb_apu_power_off(gb_sound_t *gb) { int i; switch(gb->gbMode) { case GBMODE_DMG: gb_sound_w_internal(gb, NR10, 0x00); gb->snd_1.duty = 0; gb->snd_regs[NR11] = 0; gb_sound_w_internal(gb, NR12, 0x00); gb_sound_w_internal(gb, NR13, 0x00); gb_sound_w_internal(gb, NR14, 0x00); gb->snd_1.length_counting = false; gb->snd_1.sweep_neg_mode_used = false; gb->snd_regs[NR21] = 0; gb_sound_w_internal(gb, NR22, 0x00); gb_sound_w_internal(gb, NR23, 0x00); gb_sound_w_internal(gb, NR24, 0x00); gb->snd_2.length_counting = false; gb_sound_w_internal(gb, NR30, 0x00); gb_sound_w_internal(gb, NR32, 0x00); gb_sound_w_internal(gb, NR33, 0x00); gb_sound_w_internal(gb, NR34, 0x00); gb->snd_3.length_counting = false; gb->snd_3.current_sample = 0; gb->snd_regs[NR41] = 0; gb_sound_w_internal(gb, NR42, 0x00); gb_sound_w_internal(gb, NR43, 0x00); gb_sound_w_internal(gb, NR44, 0x00); gb->snd_4.length_counting = false; gb->snd_4.cycles_left = gb_noise_period_cycles(gb); break; case GBMODE_CGB04: gb_sound_w_internal(gb, NR10, 0x00); gb->snd_1.duty = 0; gb_sound_w_internal(gb, NR11, 0x00); gb_sound_w_internal(gb, NR12, 0x00); gb_sound_w_internal(gb, NR13, 0x00); gb_sound_w_internal(gb, NR14, 0x00); gb->snd_1.length_counting = false; gb->snd_1.sweep_neg_mode_used = false; gb_sound_w_internal(gb, NR21, 0x00); gb_sound_w_internal(gb, NR22, 0x00); gb_sound_w_internal(gb, NR23, 0x00); gb_sound_w_internal(gb, NR24, 0x00); gb->snd_2.length_counting = false; gb_sound_w_internal(gb, NR30, 0x00); gb_sound_w_internal(gb, NR31, 0x00); gb_sound_w_internal(gb, NR32, 0x00); gb_sound_w_internal(gb, NR33, 0x00); gb_sound_w_internal(gb, NR34, 0x00); gb->snd_3.length_counting = false; gb->snd_3.current_sample = 0; gb_sound_w_internal(gb, NR41, 0x00); gb_sound_w_internal(gb, NR42, 0x00); gb_sound_w_internal(gb, NR43, 0x00); gb_sound_w_internal(gb, NR44, 0x00); gb->snd_4.length_counting = false; gb->snd_4.cycles_left = gb_noise_period_cycles(gb); break; } gb->snd_1.on = false; gb->snd_2.on = false; gb->snd_3.on = false; gb->snd_4.on = false; gb->snd_control.wave_ram_locked = false; for (i = NR44 + 1; i < NR52; i++) { gb_sound_w_internal(gb, i, 0x00); } return; } static void gb_tick_length(struct SOUND *snd) { if (snd->length_enabled) { snd->length = (snd->length + 1) & snd->length_mask; if (snd->length == 0) { snd->on = false; snd->length_counting = false; } } } static INT32 gb_calculate_next_sweep(struct SOUND *snd) { INT32 new_frequency; snd->sweep_neg_mode_used = (snd->sweep_direction < 0); new_frequency = snd->frequency + snd->sweep_direction * (snd->frequency >> snd->sweep_shift); if (new_frequency > 0x7FF) { snd->on = false; } return new_frequency; } static void gb_apply_next_sweep(struct SOUND *snd) { INT32 new_frequency = gb_calculate_next_sweep(snd); if (snd->on && snd->sweep_shift > 0) { snd->frequency = new_frequency; snd->reg[3] = snd->frequency & 0xFF; } } static void gb_tick_sweep(struct SOUND *snd) { snd->sweep_count = (snd->sweep_count - 1) & 0x07; if (snd->sweep_count == 0) { snd->sweep_count = snd->sweep_time; if (snd->sweep_enabled && snd->sweep_time > 0) { gb_apply_next_sweep(snd); gb_calculate_next_sweep(snd); } } } static void gb_tick_envelope(struct SOUND *snd) { if (snd->envelope_enabled) { snd->envelope_count = (snd->envelope_count - 1) & 0x07; if (snd->envelope_count == 0) { snd->envelope_count = snd->envelope_time; if (snd->envelope_count) { INT8 new_envelope_value = snd->envelope_value + snd->envelope_direction; if (new_envelope_value >= 0 && new_envelope_value <= 15) { snd->envelope_value = new_envelope_value; } else { snd->envelope_enabled = false; } } } } } INLINE bool gb_dac_enabled(struct SOUND *snd) { return (snd->channel != 3) ? snd->reg[2] & 0xF8 : snd->reg[0] & 0x80; } static void gb_update_square_channel(struct SOUND *snd, UINT32 cycles) { if (snd->on) { // compensate for leftover cycles if (snd->cycles_left > 0) { cycles += snd->cycles_left; snd->cycles_left = 0; } while (cycles > 0) { // Emit sample(s) if (cycles < 4) { snd->cycles_left = cycles; cycles = 0; } else { cycles -= 4; snd->frequency_counter = (snd->frequency_counter + 1) & 0x7FF; if (snd->frequency_counter == 0) { snd->duty_count = (snd->duty_count + 1) & 0x07; snd->signal = wave_duty_table[snd->duty][snd->duty_count]; // Reload frequency counter snd->frequency_counter = snd->frequency; } } } } } static void gb_update_wave_channel(gb_sound_t *gb, struct SOUND *snd, UINT32 cycles) { if (snd->on) { // compensate for leftover cycles if (snd->cycles_left > 0) { cycles += snd->cycles_left; snd->cycles_left = 0; } while (cycles > 0) { // Emit current sample // cycles -= 2 if (cycles < 2) { snd->cycles_left = cycles; cycles = 0; } else { cycles -= 2; // Calculate next state snd->frequency_counter = (snd->frequency_counter + 1) & 0x7FF; snd->sample_reading = false; if (gb->gbMode == GBMODE_DMG && snd->frequency_counter == 0x7ff) snd->offset = (snd->offset + 1) & 0x1F; if (snd->frequency_counter == 0) { // Read next sample snd->sample_reading = true; if (gb->gbMode == GBMODE_CGB04) snd->offset = (snd->offset + 1) & 0x1F; snd->current_sample = gb->snd_regs[AUD3W0 + (snd->offset/2)]; if (!(snd->offset & 0x01)) { snd->current_sample >>= 4; } snd->current_sample = (snd->current_sample & 0x0F) - 8; if (BoostWaveChn) snd->current_sample <<= 1; snd->signal = snd->level ? snd->current_sample / (1 << (snd->level - 1)) : 0; // Reload frequency counter snd->frequency_counter = snd->frequency; } } } } } static void gb_update_noise_channel(gb_sound_t *gb, struct SOUND *snd, UINT32 cycles) { while (cycles > 0) { if (cycles < snd->cycles_left) { if (snd->on) { // generate samples } snd->cycles_left -= cycles; cycles = 0; } else { UINT16 feedback; if (snd->on) { // generate samples } cycles -= snd->cycles_left; snd->cycles_left = gb_noise_period_cycles(gb); /* Using a Polynomial Counter (aka Linear Feedback Shift Register) Mode 4 has a 15 bit counter so we need to shift the bits around accordingly */ feedback = ((snd->noise_lfsr >> 1) ^ snd->noise_lfsr) & 1; snd->noise_lfsr = (snd->noise_lfsr >> 1) | (feedback << 14); if (snd->noise_short) { snd->noise_lfsr = (snd->noise_lfsr & ~(1 << 6)) | (feedback << 6); } snd->signal = (snd->noise_lfsr & 1) ? -1 : 1; } } } static void gb_update_state(gb_sound_t *gb, UINT32 cycles) { UINT32 old_cycles; if (!gb->snd_control.on) return; old_cycles = gb->snd_control.cycles; gb->snd_control.cycles += cycles; if ((old_cycles / FRAME_CYCLES) != (gb->snd_control.cycles / FRAME_CYCLES)) { // Left over cycles in current frame UINT32 cycles_current_frame = FRAME_CYCLES - (old_cycles & (FRAME_CYCLES - 1)); gb_update_square_channel(&gb->snd_1, cycles_current_frame); gb_update_square_channel(&gb->snd_2, cycles_current_frame); gb_update_wave_channel(gb, &gb->snd_3, cycles_current_frame); gb_update_noise_channel(gb, &gb->snd_4, cycles_current_frame); cycles -= cycles_current_frame; // Switch to next frame switch ((gb->snd_control.cycles / FRAME_CYCLES) & 0x07) { case 0: // length gb_tick_length(&gb->snd_1); gb_tick_length(&gb->snd_2); gb_tick_length(&gb->snd_3); gb_tick_length(&gb->snd_4); break; case 2: // sweep gb_tick_sweep(&gb->snd_1); // length gb_tick_length(&gb->snd_1); gb_tick_length(&gb->snd_2); gb_tick_length(&gb->snd_3); gb_tick_length(&gb->snd_4); break; case 4: // length gb_tick_length(&gb->snd_1); gb_tick_length(&gb->snd_2); gb_tick_length(&gb->snd_3); gb_tick_length(&gb->snd_4); break; case 6: // sweep gb_tick_sweep(&gb->snd_1); // length gb_tick_length(&gb->snd_1); gb_tick_length(&gb->snd_2); gb_tick_length(&gb->snd_3); gb_tick_length(&gb->snd_4); break; case 7: // update envelope gb_tick_envelope(&gb->snd_1); gb_tick_envelope(&gb->snd_2); gb_tick_envelope(&gb->snd_4); break; } } gb_update_square_channel(&gb->snd_1, cycles); gb_update_square_channel(&gb->snd_2, cycles); gb_update_wave_channel(gb, &gb->snd_3, cycles); gb_update_noise_channel(gb, &gb->snd_4, cycles); } INLINE UINT32 gb_noise_period_cycles(gb_sound_t *gb) { static const int divisor[8] = { 8, 16,32, 48, 64, 80, 96, 112 }; return divisor[gb->snd_4.reg[3] & 7] << (gb->snd_4.reg[3] >> 4); } void gameboy_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { gb_sound_t *gb = &GBSoundData[ChipID]; stream_sample_t sample, left, right; int i; UINT32 cycles; for (i = 0; i < samples; i++) { left = right = 0; gb->cycleCntr.val += gb->cycleCntr.inc; cycles = (UINT32)(gb->cycleCntr.val >> RC_SHIFT); gb->cycleCntr.val &= ((1 << RC_SHIFT) - 1); gb_update_state(gb, cycles); /* Mode 1 - Wave with Envelope and Sweep */ if (gb->snd_1.on && !gb->snd_1.Muted) { sample = gb->snd_1.signal * gb->snd_1.envelope_value; if (gb->snd_control.mode1_left) left += sample; if (gb->snd_control.mode1_right) right += sample; } /* Mode 2 - Wave with Envelope */ if (gb->snd_2.on && !gb->snd_2.Muted) { sample = gb->snd_2.signal * gb->snd_2.envelope_value; if (gb->snd_control.mode2_left) left += sample; if (gb->snd_control.mode2_right) right += sample; } /* Mode 3 - Wave patterns from WaveRAM */ if (gb->snd_3.on && !gb->snd_3.Muted) { sample = gb->snd_3.signal; if (gb->snd_control.mode3_left) left += sample; if (gb->snd_control.mode3_right) right += sample; } /* Mode 4 - Noise with Envelope */ if (gb->snd_4.on && !gb->snd_4.Muted) { sample = gb->snd_4.signal * gb->snd_4.envelope_value; if (gb->snd_control.mode4_left) left += sample; if (gb->snd_control.mode4_right) right += sample; } /* Adjust for master volume */ left *= gb->snd_control.vol_left; right *= gb->snd_control.vol_right; /* pump up the volume */ left <<= 6; right <<= 6; /* Update the buffers */ outputs[0][i] = left; outputs[1][i] = right; } gb->snd_regs[NR52] = (gb->snd_regs[NR52]&0xf0) | gb->snd_1.on | (gb->snd_2.on << 1) | (gb->snd_3.on << 2) | (gb->snd_4.on << 3); } int device_start_gameboy_sound(UINT8 ChipID, int clock) { gb_sound_t *gb; if (ChipID >= MAX_CHIPS) return 0; gb = &GBSoundData[ChipID]; memset(gb, 0x00, sizeof(gb_sound_t)); gb->rate = (clock & 0x7FFFFFFF) / 64; if (((CHIP_SAMPLING_MODE & 0x01) && gb->rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) gb->rate = CHIP_SAMPLE_RATE; gb->gbMode = (clock & 0x80000000) ? GBMODE_CGB04 : GBMODE_DMG; RC_SET_RATIO(&gb->cycleCntr, clock & 0x7FFFFFFF, gb->rate); gameboy_sound_set_mute_mask(ChipID, 0x00); //gb->BoostWaveChn = 0x00; return gb->rate; } void device_stop_gameboy_sound(UINT8 ChipID) { return; } void device_reset_gameboy_sound(UINT8 ChipID) { gb_sound_t *gb = &GBSoundData[ChipID]; UINT32 muteMask; muteMask = gameboy_sound_get_mute_mask(ChipID); gb->cycleCntr.val = 0; memset(&gb->snd_1, 0, sizeof(gb->snd_1)); memset(&gb->snd_2, 0, sizeof(gb->snd_2)); memset(&gb->snd_3, 0, sizeof(gb->snd_3)); memset(&gb->snd_4, 0, sizeof(gb->snd_4)); gameboy_sound_set_mute_mask(ChipID, muteMask); gb->snd_1.channel = 1; gb->snd_1.length_mask = 0x3F; gb->snd_2.channel = 2; gb->snd_2.length_mask = 0x3F; gb->snd_3.channel = 3; gb->snd_3.length_mask = 0xFF; gb->snd_4.channel = 4; gb->snd_4.length_mask = 0x3F; gb_sound_w_internal(gb, NR52, 0x00); switch(gb->gbMode) { case GBMODE_DMG: gb->snd_regs[AUD3W0] = 0xac; gb->snd_regs[AUD3W1] = 0xdd; gb->snd_regs[AUD3W2] = 0xda; gb->snd_regs[AUD3W3] = 0x48; gb->snd_regs[AUD3W4] = 0x36; gb->snd_regs[AUD3W5] = 0x02; gb->snd_regs[AUD3W6] = 0xcf; gb->snd_regs[AUD3W7] = 0x16; gb->snd_regs[AUD3W8] = 0x2c; gb->snd_regs[AUD3W9] = 0x04; gb->snd_regs[AUD3WA] = 0xe5; gb->snd_regs[AUD3WB] = 0x2c; gb->snd_regs[AUD3WC] = 0xac; gb->snd_regs[AUD3WD] = 0xdd; gb->snd_regs[AUD3WE] = 0xda; gb->snd_regs[AUD3WF] = 0x48; break; case GBMODE_CGB04: gb->snd_regs[AUD3W0] = 0x00; gb->snd_regs[AUD3W1] = 0xFF; gb->snd_regs[AUD3W2] = 0x00; gb->snd_regs[AUD3W3] = 0xFF; gb->snd_regs[AUD3W4] = 0x00; gb->snd_regs[AUD3W5] = 0xFF; gb->snd_regs[AUD3W6] = 0x00; gb->snd_regs[AUD3W7] = 0xFF; gb->snd_regs[AUD3W8] = 0x00; gb->snd_regs[AUD3W9] = 0xFF; gb->snd_regs[AUD3WA] = 0x00; gb->snd_regs[AUD3WB] = 0xFF; gb->snd_regs[AUD3WC] = 0x00; gb->snd_regs[AUD3WD] = 0xFF; gb->snd_regs[AUD3WE] = 0x00; gb->snd_regs[AUD3WF] = 0xFF; break; } return; } void gameboy_sound_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { gb_sound_t *gb = &GBSoundData[ChipID]; gb->snd_1.Muted = (MuteMask >> 0) & 0x01; gb->snd_2.Muted = (MuteMask >> 1) & 0x01; gb->snd_3.Muted = (MuteMask >> 2) & 0x01; gb->snd_4.Muted = (MuteMask >> 3) & 0x01; return; } UINT32 gameboy_sound_get_mute_mask(UINT8 ChipID) { gb_sound_t *gb = &GBSoundData[ChipID]; UINT32 muteMask; muteMask = (gb->snd_1.Muted << 0) | (gb->snd_2.Muted << 1) | (gb->snd_3.Muted << 2) | (gb->snd_4.Muted << 3); return muteMask; } void gameboy_sound_set_options(UINT8 Flags) { BoostWaveChn = (Flags & 0x01) >> 0; return; } ================================================ FILE: VGMPlay/chips/gb.h ================================================ /* Custom Sound Interface */ UINT8 gb_wave_r(UINT8 ChipID, offs_t offset); void gb_wave_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 gb_sound_r(UINT8 ChipID, offs_t offset); void gb_sound_w(UINT8 ChipID, offs_t offset, UINT8 data); void gameboy_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_gameboy_sound(UINT8 ChipID, int clock); void device_stop_gameboy_sound(UINT8 ChipID); void device_reset_gameboy_sound(UINT8 ChipID); void gameboy_sound_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); UINT32 gameboy_sound_get_mute_mask(UINT8 ChipID); void gameboy_sound_set_options(UINT8 Flags); ================================================ FILE: VGMPlay/chips/iremga20.c ================================================ /********************************************************* Irem GA20 PCM Sound Chip It's not currently known whether this chip is stereo. Revisions: 04-15-2002 Acho A. Tang - rewrote channel mixing - added prelimenary volume and sample rate emulation 05-30-2002 Acho A. Tang - applied hyperbolic gain control to volume and used a musical-note style progression in sample rate calculation(still very inaccurate) 02-18-2004 R. Belmont - sample rate calculation reverse-engineered. Thanks to Fujix, Yasuhiro Ogawa, the Guru, and Tormod for real PCB samples that made this possible. 02-03-2007 R. Belmont - Cleaned up faux x86 assembly. *********************************************************/ //#include "emu.h" #include #include // for memset #include // for NULL #include "mamedef.h" #include "iremga20.h" #define MAX_VOL 256 struct IremGA20_channel_def { UINT32 rate; //UINT32 size; UINT32 start; UINT32 pos; UINT32 frac; UINT32 end; UINT32 volume; UINT32 pan; //UINT32 effect; UINT8 play; UINT8 Muted; }; typedef struct _ga20_state ga20_state; struct _ga20_state { UINT8 *rom; UINT32 rom_size; //sound_stream * stream; UINT16 regs[0x40]; struct IremGA20_channel_def channel[4]; }; #define MAX_CHIPS 0x02 static ga20_state GA20Data[MAX_CHIPS]; /*INLINE ga20_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == IREMGA20); return (ga20_state *)downcast(device)->token(); }*/ //static STREAM_UPDATE( IremGA20_update ) void IremGA20_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ga20_state *chip = (ga20_state *)param; ga20_state *chip = &GA20Data[ChipID]; UINT32 rate[4], pos[4], frac[4], end[4], vol[4], play[4]; UINT8 *pSamples; stream_sample_t *outL, *outR; int i, sampleout; /* precache some values */ for (i=0; i < 4; i++) { rate[i] = chip->channel[i].rate; pos[i] = chip->channel[i].pos; frac[i] = chip->channel[i].frac; end[i] = chip->channel[i].end - 0x20; vol[i] = chip->channel[i].volume; play[i] = (! chip->channel[i].Muted) ? chip->channel[i].play : 0; } i = samples; pSamples = chip->rom; outL = outputs[0]; outR = outputs[1]; for (i = 0; i < samples; i++) { sampleout = 0; // update the 4 channels inline if (play[0]) { sampleout += (pSamples[pos[0]] - 0x80) * vol[0]; frac[0] += rate[0]; pos[0] += frac[0] >> 24; frac[0] &= 0xffffff; play[0] = (pos[0] < end[0]); } if (play[1]) { sampleout += (pSamples[pos[1]] - 0x80) * vol[1]; frac[1] += rate[1]; pos[1] += frac[1] >> 24; frac[1] &= 0xffffff; play[1] = (pos[1] < end[1]); } if (play[2]) { sampleout += (pSamples[pos[2]] - 0x80) * vol[2]; frac[2] += rate[2]; pos[2] += frac[2] >> 24; frac[2] &= 0xffffff; play[2] = (pos[2] < end[2]); } if (play[3]) { sampleout += (pSamples[pos[3]] - 0x80) * vol[3]; frac[3] += rate[3]; pos[3] += frac[3] >> 24; frac[3] &= 0xffffff; play[3] = (pos[3] < end[3]); } sampleout >>= 2; outL[i] = sampleout; outR[i] = sampleout; } /* update the regs now */ for (i=0; i < 4; i++) { chip->channel[i].pos = pos[i]; chip->channel[i].frac = frac[i]; if (! chip->channel[i].Muted) chip->channel[i].play = play[i]; } } //WRITE8_DEVICE_HANDLER( irem_ga20_w ) void irem_ga20_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ga20_state *chip = get_safe_token(device); ga20_state *chip = &GA20Data[ChipID]; int channel; //logerror("GA20: Offset %02x, data %04x\n",offset,data); //chip->stream->update(); channel = offset >> 3; chip->regs[offset] = data; switch (offset & 0x7) { case 0: /* start address low */ chip->channel[channel].start = ((chip->channel[channel].start)&0xff000) | (data<<4); break; case 1: /* start address high */ chip->channel[channel].start = ((chip->channel[channel].start)&0x00ff0) | (data<<12); break; case 2: /* end address low */ chip->channel[channel].end = ((chip->channel[channel].end)&0xff000) | (data<<4); break; case 3: /* end address high */ chip->channel[channel].end = ((chip->channel[channel].end)&0x00ff0) | (data<<12); break; case 4: chip->channel[channel].rate = 0x1000000 / (256 - data); break; case 5: //AT: gain control chip->channel[channel].volume = (data * MAX_VOL) / (data + 10); break; case 6: //AT: this is always written 2(enabling both channels?) chip->channel[channel].play = data; chip->channel[channel].pos = chip->channel[channel].start; chip->channel[channel].frac = 0; break; } } //READ8_DEVICE_HANDLER( irem_ga20_r ) UINT8 irem_ga20_r(UINT8 ChipID, offs_t offset) { //ga20_state *chip = get_safe_token(device); ga20_state *chip = &GA20Data[ChipID]; int channel; //chip->stream->update(); channel = offset >> 3; switch (offset & 0x7) { case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo) return chip->channel[channel].play ? 1 : 0; default: logerror("GA20: read unk. register %d, channel %d\n", offset & 0xf, channel); break; } return 0; } static void iremga20_reset(ga20_state *chip) { int i; for( i = 0; i < 4; i++ ) { chip->channel[i].rate = 0; //chip->channel[i].size = 0; chip->channel[i].start = 0; chip->channel[i].pos = 0; chip->channel[i].frac = 0; chip->channel[i].end = 0; chip->channel[i].volume = 0; chip->channel[i].pan = 0; //chip->channel[i].effect = 0; chip->channel[i].play = 0; } } //static DEVICE_RESET( iremga20 ) void device_reset_iremga20(UINT8 ChipID) { //iremga20_reset(get_safe_token(device)); ga20_state *chip = &GA20Data[ChipID]; iremga20_reset(chip); memset(chip->regs, 0x00, 0x40 * sizeof(UINT16)); } //static DEVICE_START( iremga20 ) int device_start_iremga20(UINT8 ChipID, int clock) { //ga20_state *chip = get_safe_token(device); ga20_state *chip; int i; if (ChipID >= MAX_CHIPS) return 0; chip = &GA20Data[ChipID]; /* Initialize our chip structure */ //chip->rom = *device->region(); //chip->rom_size = device->region()->bytes(); chip->rom = NULL; chip->rom_size = 0x00; iremga20_reset(chip); for ( i = 0; i < 0x40; i++ ) chip->regs[i] = 0; //chip->stream = device->machine().sound().stream_alloc( *device, 0, 2, device->clock()/4, chip, IremGA20_update ); /*device->save_item(NAME(chip->regs)); for (i = 0; i < 4; i++) { device->save_item(NAME(chip->channel[i].rate), i); device->save_item(NAME(chip->channel[i].size), i); device->save_item(NAME(chip->channel[i].start), i); device->save_item(NAME(chip->channel[i].pos), i); device->save_item(NAME(chip->channel[i].end), i); device->save_item(NAME(chip->channel[i].volume), i); device->save_item(NAME(chip->channel[i].pan), i); device->save_item(NAME(chip->channel[i].effect), i); device->save_item(NAME(chip->channel[i].play), i); }*/ for (i = 0; i < 4; i ++) chip->channel[i].Muted = 0x00; return clock / 4; } void device_stop_iremga20(UINT8 ChipID) { ga20_state *chip = &GA20Data[ChipID]; free(chip->rom); chip->rom = NULL; return; } void iremga20_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ga20_state *chip = &GA20Data[ChipID]; if (chip->rom_size != ROMSize) { chip->rom = (UINT8*)realloc(chip->rom, ROMSize); chip->rom_size = ROMSize; memset(chip->rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->rom + DataStart, ROMData, DataLength); return; } void iremga20_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ga20_state *chip = &GA20Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) chip->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( iremga20 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ga20_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( iremga20 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( iremga20 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "Irem GA20"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Irem custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(IREMGA20, iremga20);*/ ================================================ FILE: VGMPlay/chips/iremga20.h ================================================ /********************************************************* Irem GA20 PCM Sound Chip *********************************************************/ #pragma once #ifndef __IREMGA20_H__ #define __IREMGA20_H__ //#include "devlegcy.h" void IremGA20_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_iremga20(UINT8 ChipID, int clock); void device_stop_iremga20(UINT8 ChipID); void device_reset_iremga20(UINT8 ChipID); //WRITE8_DEVICE_HANDLER( irem_ga20_w ); //READ8_DEVICE_HANDLER( irem_ga20_r ); UINT8 irem_ga20_r(UINT8 ChipID, offs_t offset); void irem_ga20_w(UINT8 ChipID, offs_t offset, UINT8 data); void iremga20_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void iremga20_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(IREMGA20, iremga20); #endif /* __IREMGA20_H__ */ ================================================ FILE: VGMPlay/chips/k051649.c ================================================ /*************************************************************************** Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber This file is pieced together by Bryan McPhail from a combination of Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first figured out SCC! The 051649 is a 5 channel sound generator, each channel gets its waveform from RAM (32 bytes per waveform, 8 bit signed data). This sound chip is the same as the sound chip in some Konami megaROM cartridges for the MSX. It is actually well researched and documented: http://bifi.msxnet.org/msxnet/tech/scc.html Thanks to Sean Young (sean@mess.org) for some bugfixes. K052539 is more or less equivalent to this chip except channel 5 does not share waveram with channel 4. ***************************************************************************/ #include "mamedef.h" #include #include // for memset //#include "emu.h" //#include "streams.h" #include "k051649.h" #define FREQ_BITS 16 #define DEF_GAIN 8 /* this structure defines the parameters for a channel */ typedef struct { unsigned long counter; int frequency; int volume; int key; signed char waveram[32]; /* 19991207.CAB */ UINT8 Muted; } k051649_sound_channel; typedef struct _k051649_state k051649_state; struct _k051649_state { k051649_sound_channel channel_list[5]; /* global sound parameters */ //sound_stream * stream; int mclock,rate; /* mixer tables and internal buffers */ INT16 *mixer_table; INT16 *mixer_lookup; short *mixer_buffer; int cur_reg; UINT8 test; }; #define MAX_CHIPS 0x02 static k051649_state SCC1Data[MAX_CHIPS]; /*INLINE k051649_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == K051649); return (k051649_state *)downcast(device)->token(); }*/ /* build a table to divide by the number of voices */ static void make_mixer_table(/*running_machine *machine,*/ k051649_state *info, int voices) { int count = voices * 256; int i; /* allocate memory */ //info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices); info->mixer_table = (INT16*)malloc(sizeof(INT16) * 2 * count); /* find the middle of the table */ info->mixer_lookup = info->mixer_table + count; /* fill in the table - 16 bit case */ for (i = 0; i < count; i++) { int val = i * DEF_GAIN * 16 / voices; //if (val > 32767) val = 32767; if (val > 32768) val = 32768; info->mixer_lookup[ i] = val; info->mixer_lookup[-i] = -val; } } /* generate sound to the mix buffer */ //static STREAM_UPDATE( k051649_update ) void k051649_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //k051649_state *info = (k051649_state *)param; k051649_state *info = &SCC1Data[ChipID]; k051649_sound_channel *voice=info->channel_list; stream_sample_t *buffer = outputs[0]; stream_sample_t *buffer2 = outputs[1]; short *mix; int i,j; // zap the contents of the mixer buffer memset(info->mixer_buffer, 0, samples * sizeof(short)); for (j=0; j<5; j++) { // channel is halted for freq < 9 if (voice[j].frequency > 8 && ! voice[j].Muted) { const signed char *w = voice[j].waveram; /* 19991207.CAB */ int v=voice[j].volume * voice[j].key; int c=voice[j].counter; /* Amuse source: Cab suggests this method gives greater resolution */ /* Sean Young 20010417: the formula is really: f = clock/(16*(f+1))*/ int step = (int)(((INT64)info->mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (info->rate / 32)) + 0.5); mix = info->mixer_buffer; // add our contribution for (i = 0; i < samples; i++) { int offs; c += step; offs = (c >> FREQ_BITS) & 0x1f; *mix++ += (w[offs] * v)>>3; } // update the counter for this voice voice[j].counter = c; } } // mix it down mix = info->mixer_buffer; for (i = 0; i < samples; i++) *buffer++ = *buffer2++ = info->mixer_lookup[*mix++]; } //static DEVICE_START( k051649 ) int device_start_k051649(UINT8 ChipID, int clock) { //k051649_state *info = get_safe_token(device); k051649_state *info; UINT8 CurChn; if (ChipID >= MAX_CHIPS) return 0; info = &SCC1Data[ChipID]; /* get stream channels */ //info->rate = device->clock()/16; //info->stream = stream_create(device, 0, 1, info->rate, info, k051649_update); //info->mclock = device->clock(); info->mclock = clock & 0x7FFFFFFF; info->rate = info->mclock / 16; /* allocate a buffer to mix into - 1 second's worth should be more than enough */ //info->mixer_buffer = auto_alloc_array(device->machine, short, 2 * info->rate); info->mixer_buffer = (short*)malloc(sizeof(short) * info->rate); /* build the mixer table */ //make_mixer_table(device->machine, info, 5); make_mixer_table(info, 5); for (CurChn = 0; CurChn < 5; CurChn ++) info->channel_list[CurChn].Muted = 0x00; return info->rate; } void device_stop_k051649(UINT8 ChipID) { k051649_state *info = &SCC1Data[ChipID]; free(info->mixer_buffer); free(info->mixer_table); return; } //static DEVICE_RESET( k051649 ) void device_reset_k051649(UINT8 ChipID) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; k051649_sound_channel *voice = info->channel_list; int i; // reset all the voices for (i = 0; i < 5; i++) { voice[i].frequency = 0; voice[i].volume = 0; voice[i].counter = 0; voice[i].key = 0; } // other parameters info->test = 0x00; info->cur_reg = 0x00; return; } /********************************************************************************/ //WRITE8_DEVICE_HANDLER( k051649_waveform_w ) void k051649_waveform_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; // waveram is read-only? if (info->test & 0x40 || (info->test & 0x80 && offset >= 0x60)) return; //stream_update(info->stream); if (offset >= 0x60) { // channel 5 shares waveram with channel 4 info->channel_list[3].waveram[offset&0x1f]=data; info->channel_list[4].waveram[offset&0x1f]=data; } else info->channel_list[offset>>5].waveram[offset&0x1f]=data; } //READ8_DEVICE_HANDLER ( k051649_waveform_r ) UINT8 k051649_waveform_r(UINT8 ChipID, offs_t offset) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; // test-register bits 6/7 expose the internal counter if (info->test & 0xc0) { //stream_update(info->stream); if (offset >= 0x60) offset += (info->channel_list[3 + (info->test >> 6 & 1)].counter >> FREQ_BITS); else if (info->test & 0x40) offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); } return info->channel_list[offset>>5].waveram[offset&0x1f]; } /* SY 20001114: Channel 5 doesn't share the waveform with channel 4 on this chip */ //WRITE8_DEVICE_HANDLER( k052539_waveform_w ) void k052539_waveform_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; // waveram is read-only? if (info->test & 0x40) return; //stream_update(info->stream); info->channel_list[offset>>5].waveram[offset&0x1f]=data; } //READ8_DEVICE_HANDLER ( k052539_waveform_r ) UINT8 k052539_waveform_r(UINT8 ChipID, offs_t offset) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; // test-register bit 6 exposes the internal counter if (info->test & 0x40) { //stream_update(info->stream); offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); } return info->channel_list[offset>>5].waveram[offset&0x1f]; } //WRITE8_DEVICE_HANDLER( k051649_volume_w ) void k051649_volume_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; //stream_update(info->stream); info->channel_list[offset&0x7].volume=data&0xf; } //WRITE8_DEVICE_HANDLER( k051649_frequency_w ) void k051649_frequency_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; k051649_sound_channel* chn = &info->channel_list[offset >> 1]; //stream_update(info->stream); // test-register bit 5 resets the internal counter if (info->test & 0x20) chn->counter = ~0; else if (chn->frequency < 9) chn->counter |= ((1 << FREQ_BITS) - 1); // update frequency if (offset & 1) chn->frequency = (chn->frequency & 0x0FF) | ((data << 8) & 0xF00); else chn->frequency = (chn->frequency & 0xF00) | (data << 0); chn->counter &= 0xFFFF0000; // Valley Bell: Behaviour according to openMSX } //WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ) void k051649_keyonoff_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k051649_state *info = get_safe_token(device); k051649_state *info = &SCC1Data[ChipID]; int i; //stream_update(info->stream); for (i = 0; i < 5; i++) { info->channel_list[i].key=data&1; data >>= 1; } } //WRITE8_MEMBER( k051649_device::k051649_test_w ) void k051649_test_w(UINT8 ChipID, offs_t offset, UINT8 data) { k051649_state *info = &SCC1Data[ChipID]; info->test = data; } //READ8_MEMBER ( k051649_device::k051649_test_r ) UINT8 k051649_test_r(UINT8 ChipID, offs_t offset) { // reading the test register sets it to $ff! k051649_test_w(ChipID, offset, 0xff); return 0xff; } void k051649_w(UINT8 ChipID, offs_t offset, UINT8 data) { k051649_state *info = &SCC1Data[ChipID]; switch(offset & 1) { case 0x00: info->cur_reg = data; break; case 0x01: switch(offset >> 1) { case 0x00: k051649_waveform_w(ChipID, info->cur_reg, data); break; case 0x01: k051649_frequency_w(ChipID, info->cur_reg, data); break; case 0x02: k051649_volume_w(ChipID, info->cur_reg, data); break; case 0x03: k051649_keyonoff_w(ChipID, info->cur_reg, data); break; case 0x04: k052539_waveform_w(ChipID, info->cur_reg, data); break; case 0x05: k051649_test_w(ChipID, info->cur_reg, data); break; } break; } return; } void k051649_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { k051649_state *info = &SCC1Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 5; CurChn ++) info->channel_list[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( k051649 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k051649_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k051649 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k051649 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "K051649"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ //DEFINE_LEGACY_SOUND_DEVICE(K051649, k051649); ================================================ FILE: VGMPlay/chips/k051649.h ================================================ #pragma once //#ifndef __K051649_H__ //#define __K051649_H__ //#include "devlegcy.h" /*WRITE8_DEVICE_HANDLER( k051649_waveform_w ); READ8_DEVICE_HANDLER( k051649_waveform_r ); WRITE8_DEVICE_HANDLER( k051649_volume_w ); WRITE8_DEVICE_HANDLER( k051649_frequency_w ); WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ); WRITE8_DEVICE_HANDLER( k052539_waveform_w ); DECLARE_LEGACY_SOUND_DEVICE(K051649, k051649);*/ void k051649_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_k051649(UINT8 ChipID, int clock); void device_stop_k051649(UINT8 ChipID); void device_reset_k051649(UINT8 ChipID); void k051649_waveform_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 k051649_waveform_r(UINT8 ChipID, offs_t offset); void k051649_volume_w(UINT8 ChipID, offs_t offset, UINT8 data); void k051649_frequency_w(UINT8 ChipID, offs_t offset, UINT8 data); void k051649_keyonoff_w(UINT8 ChipID, offs_t offset, UINT8 data); void k052539_waveform_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 k052539_waveform_r(UINT8 ChipID, offs_t offset); void k051649_test_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 k051649_test_r(UINT8 ChipID, offs_t offset); void k051649_w(UINT8 ChipID, offs_t offset, UINT8 data); void k051649_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //#endif /* __K051649_H__ */ ================================================ FILE: VGMPlay/chips/k053260.c ================================================ /********************************************************* Konami 053260 PCM Sound Chip *********************************************************/ #include "mamedef.h" //#include "emu.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include "k053260.h" /* 2004-02-28: Fixed PPCM decoding. Games sound much better now.*/ #define LOG 0 #define BASE_SHIFT 16 typedef struct _k053260_channel k053260_channel; struct _k053260_channel { UINT32 rate; UINT32 size; UINT32 start; UINT32 bank; UINT32 volume; int play; UINT32 pan; UINT32 pos; int loop; int ppcm; /* packed PCM ( 4 bit signed ) */ int ppcm_data; UINT8 Muted; }; typedef struct _k053260_state k053260_state; struct _k053260_state { //sound_stream * channel; int mode; int regs[0x30]; UINT8 *rom; //int rom_size; UINT32 rom_size; UINT32 *delta_table; k053260_channel channels[4]; //const k053260_interface *intf; //device_t *device; }; #define MAX_CHIPS 0x02 static k053260_state K053260Data[MAX_CHIPS]; /*INLINE k053260_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == K053260); return (k053260_state *)downcast(device)->token(); }*/ static void InitDeltaTable( k053260_state *ic, int rate, int clock ) { int i; double base = ( double )rate; double max = (double)(clock); /* Hz */ UINT32 val; for( i = 0; i < 0x1000; i++ ) { double v = ( double )( 0x1000 - i ); double target = (max) / v; double fixed = ( double )( 1 << BASE_SHIFT ); if ( target && base ) { target = fixed / ( base / target ); val = ( UINT32 )target; if ( val == 0 ) val = 1; } else val = 1; ic->delta_table[i] = val; } } //static DEVICE_RESET( k053260 ) void device_reset_k053260(UINT8 ChipID) { //k053260_state *ic = get_safe_token(device); k053260_state *ic = &K053260Data[ChipID]; int i; for( i = 0; i < 4; i++ ) { ic->channels[i].rate = 0; ic->channels[i].size = 0; ic->channels[i].start = 0; ic->channels[i].bank = 0; ic->channels[i].volume = 0; ic->channels[i].play = 0; ic->channels[i].pan = 0; ic->channels[i].pos = 0; ic->channels[i].loop = 0; ic->channels[i].ppcm = 0; ic->channels[i].ppcm_data = 0; } } INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) val = min; return val; } //#define MAXOUT 0x7fff #define MAXOUT +0x8000 #define MINOUT -0x8000 //static STREAM_UPDATE( k053260_update ) void k053260_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { static const INT8 dpcmcnv[] = { 0,1,2,4,8,16,32,64, -128, -64, -32, -16, -8, -4, -2, -1}; int i, j, lvol[4], rvol[4], play[4], loop[4], ppcm[4]; UINT8 *rom[4]; UINT32 delta[4], end[4], pos[4]; INT8 ppcm_data[4]; int dataL, dataR; INT8 d; //k053260_state *ic = (k053260_state *)param; k053260_state *ic = &K053260Data[ChipID]; /* precache some values */ for ( i = 0; i < 4; i++ ) { if (ic->channels[i].Muted) { play[i] = 0; continue; } rom[i]= &ic->rom[ic->channels[i].start + ( ic->channels[i].bank << 16 )]; delta[i] = ic->delta_table[ic->channels[i].rate]; lvol[i] = ic->channels[i].volume * ic->channels[i].pan; rvol[i] = ic->channels[i].volume * ( 8 - ic->channels[i].pan ); end[i] = ic->channels[i].size; pos[i] = ic->channels[i].pos; play[i] = ic->channels[i].play; loop[i] = ic->channels[i].loop; ppcm[i] = ic->channels[i].ppcm; ppcm_data[i] = ic->channels[i].ppcm_data; if ( ppcm[i] ) delta[i] /= 2; } for ( j = 0; j < samples; j++ ) { dataL = dataR = 0; for ( i = 0; i < 4; i++ ) { /* see if the voice is on */ if ( play[i] ) { /* see if we're done */ if ( ( pos[i] >> BASE_SHIFT ) >= end[i] ) { ppcm_data[i] = 0; if ( loop[i] ) pos[i] = 0; else { play[i] = 0; continue; } } if ( ppcm[i] ) { /* Packed PCM */ /* we only update the signal if we're starting or a real sound sample has gone by */ /* this is all due to the dynamic sample rate conversion */ if ( pos[i] == 0 || ( ( pos[i] ^ ( pos[i] - delta[i] ) ) & 0x8000 ) == 0x8000 ) { int newdata; if ( pos[i] & 0x8000 ){ newdata = ((rom[i][pos[i] >> BASE_SHIFT]) >> 4) & 0x0f; /*high nybble*/ } else{ newdata = ( ( rom[i][pos[i] >> BASE_SHIFT] ) ) & 0x0f; /*low nybble*/ } /*ppcm_data[i] = (( ( ppcm_data[i] * 62 ) >> 6 ) + dpcmcnv[newdata]); if ( ppcm_data[i] > 127 ) ppcm_data[i] = 127; else if ( ppcm_data[i] < -128 ) ppcm_data[i] = -128;*/ ppcm_data[i] += dpcmcnv[newdata]; } d = ppcm_data[i]; pos[i] += delta[i]; } else { /* PCM */ d = rom[i][pos[i] >> BASE_SHIFT]; pos[i] += delta[i]; } if ( ic->mode & 2 ) { dataL += ( d * lvol[i] ) >> 2; dataR += ( d * rvol[i] ) >> 2; } } } outputs[1][j] = limit( dataL, MAXOUT, MINOUT ); outputs[0][j] = limit( dataR, MAXOUT, MINOUT ); } /* update the regs now */ for ( i = 0; i < 4; i++ ) { if (ic->channels[i].Muted) continue; ic->channels[i].pos = pos[i]; ic->channels[i].play = play[i]; ic->channels[i].ppcm_data = ppcm_data[i]; } } //static DEVICE_START( k053260 ) int device_start_k053260(UINT8 ChipID, int clock) { //static const k053260_interface defintrf = { 0 }; //k053260_state *ic = get_safe_token(device); k053260_state *ic; //int rate = device->clock() / 32; int rate = clock / 32; int i; if (ChipID >= MAX_CHIPS) return 0; ic = &K053260Data[ChipID]; /* Initialize our chip structure */ //ic->device = device; //ic->intf = (device->static_config() != NULL) ? (const k053260_interface *)device->static_config() : &defintrf; ic->mode = 0; //const memory_region *region = (ic->intf->rgnoverride != NULL) ? device->machine().region(ic->intf->rgnoverride) : device->region(); //ic->rom = *region; //ic->rom_size = region->bytes(); ic->rom = NULL; ic->rom_size = 0x00; // has to be done by the player after calling device_start //DEVICE_RESET_CALL(k053260); for ( i = 0; i < 0x30; i++ ) ic->regs[i] = 0; //ic->delta_table = auto_alloc_array( device->machine(), UINT32, 0x1000 ); ic->delta_table = (UINT32*)malloc(0x1000 * sizeof(UINT32)); //ic->channel = device->machine().sound().stream_alloc( *device, 0, 2, rate, ic, k053260_update ); //InitDeltaTable( ic, rate, device->clock() ); InitDeltaTable( ic, rate, clock ); /* register with the save state system */ /*device->save_item(NAME(ic->mode)); device->save_item(NAME(ic->regs)); for ( i = 0; i < 4; i++ ) { device->save_item(NAME(ic->channels[i].rate), i); device->save_item(NAME(ic->channels[i].size), i); device->save_item(NAME(ic->channels[i].start), i); device->save_item(NAME(ic->channels[i].bank), i); device->save_item(NAME(ic->channels[i].volume), i); device->save_item(NAME(ic->channels[i].play), i); device->save_item(NAME(ic->channels[i].pan), i); device->save_item(NAME(ic->channels[i].pos), i); device->save_item(NAME(ic->channels[i].loop), i); device->save_item(NAME(ic->channels[i].ppcm), i); device->save_item(NAME(ic->channels[i].ppcm_data), i); }*/ /* setup SH1 timer if necessary */ //if ( ic->intf->irq ) // device->machine().scheduler().timer_pulse( attotime::from_hz(device->clock()) * 32, ic->intf->irq, "ic->intf->irq" ); for (i = 0; i < 4; i ++) ic->channels[i].Muted = 0x00; return rate; } void device_stop_k053260(UINT8 ChipID) { k053260_state *ic = &K053260Data[ChipID]; free(ic->delta_table); free(ic->rom); ic->rom = NULL; return; } INLINE void check_bounds( k053260_state *ic, int channel ) { int channel_start = ( ic->channels[channel].bank << 16 ) + ic->channels[channel].start; int channel_end = channel_start + ic->channels[channel].size - 1; if ( channel_start > ic->rom_size ) { logerror("K53260: Attempting to start playing past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].play = 0; return; } if ( channel_end > ic->rom_size ) { logerror("K53260: Attempting to play past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].size = ic->rom_size - channel_start; } if (LOG) logerror("K053260: Sample Start = %06x, Sample End = %06x, Sample rate = %04x, PPCM = %s\n", channel_start, channel_end, ic->channels[channel].rate, ic->channels[channel].ppcm ? "yes" : "no" ); } //WRITE8_DEVICE_HANDLER( k053260_w ) void k053260_w(UINT8 ChipID, offs_t offset, UINT8 data) { int i, t; int r = offset; int v = data; //k053260_state *ic = get_safe_token(device); k053260_state *ic = &K053260Data[ChipID]; if ( r > 0x2f ) { logerror("K053260: Writing past registers\n" ); return; } //ic->channel->update(); /* before we update the regs, we need to check for a latched reg */ if ( r == 0x28 ) { t = ic->regs[r] ^ v; for ( i = 0; i < 4; i++ ) { if ( t & ( 1 << i ) ) { if ( v & ( 1 << i ) ) { ic->channels[i].play = 1; ic->channels[i].pos = 0; ic->channels[i].ppcm_data = 0; check_bounds( ic, i ); } else ic->channels[i].play = 0; } } ic->regs[r] = v; return; } /* update regs */ ic->regs[r] = v; /* communication registers */ if ( r < 8 ) return; /* channel setup */ if ( r < 0x28 ) { int channel = ( r - 8 ) / 8; switch ( ( r - 8 ) & 0x07 ) { case 0: /* sample rate low */ ic->channels[channel].rate &= 0x0f00; ic->channels[channel].rate |= v; break; case 1: /* sample rate high */ ic->channels[channel].rate &= 0x00ff; ic->channels[channel].rate |= ( v & 0x0f ) << 8; break; case 2: /* size low */ ic->channels[channel].size &= 0xff00; ic->channels[channel].size |= v; break; case 3: /* size high */ ic->channels[channel].size &= 0x00ff; ic->channels[channel].size |= v << 8; break; case 4: /* start low */ ic->channels[channel].start &= 0xff00; ic->channels[channel].start |= v; break; case 5: /* start high */ ic->channels[channel].start &= 0x00ff; ic->channels[channel].start |= v << 8; break; case 6: /* bank */ ic->channels[channel].bank = v & 0xff; break; case 7: /* volume is 7 bits. Convert to 8 bits now. */ ic->channels[channel].volume = ( ( v & 0x7f ) << 1 ) | ( v & 1 ); break; } return; } switch( r ) { case 0x2a: /* loop, ppcm */ for ( i = 0; i < 4; i++ ) ic->channels[i].loop = ( v & ( 1 << i ) ) != 0; for ( i = 4; i < 8; i++ ) ic->channels[i-4].ppcm = ( v & ( 1 << i ) ) != 0; break; case 0x2c: /* pan */ ic->channels[0].pan = v & 7; ic->channels[1].pan = ( v >> 3 ) & 7; break; case 0x2d: /* more pan */ ic->channels[2].pan = v & 7; ic->channels[3].pan = ( v >> 3 ) & 7; break; case 0x2f: /* control */ ic->mode = v & 7; /* bit 0 = read ROM */ /* bit 1 = enable sound output */ /* bit 2 = unknown */ break; } } //READ8_DEVICE_HANDLER( k053260_r ) UINT8 k053260_r(UINT8 ChipID, offs_t offset) { //k053260_state *ic = get_safe_token(device); k053260_state *ic = &K053260Data[ChipID]; switch ( offset ) { case 0x29: /* channel status */ { int i, status = 0; for ( i = 0; i < 4; i++ ) status |= ic->channels[i].play << i; return status; } break; case 0x2e: /* read ROM */ if ( ic->mode & 1 ) { UINT32 offs = ic->channels[0].start + ( ic->channels[0].pos >> BASE_SHIFT ) + ( ic->channels[0].bank << 16 ); ic->channels[0].pos += ( 1 << 16 ); if ( offs > ic->rom_size ) { //logerror("%s: K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", device->machine().describe_context(),offs,ic->rom_size ); logerror("K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", offs,ic->rom_size ); return 0; } return ic->rom[offs]; } break; } return ic->regs[offset]; } void k053260_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { k053260_state *info = &K053260Data[ChipID]; if (info->rom_size != ROMSize) { info->rom = (UINT8*)realloc(info->rom, ROMSize); info->rom_size = ROMSize; memset(info->rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(info->rom + DataStart, ROMData, DataLength); return; } void k053260_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { k053260_state *info = &K053260Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) info->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( k053260 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k053260_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k053260 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k053260 ); break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "K053260"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(K053260, k053260);*/ ================================================ FILE: VGMPlay/chips/k053260.h ================================================ /********************************************************* Konami 053260 PCM/ADPCM Sound Chip *********************************************************/ #pragma once //#include "devlegcy.h" /*typedef struct _k053260_interface k053260_interface; struct _k053260_interface { const char *rgnoverride; timer_expired_func irq; // called on SH1 complete cycle ( clock / 32 ) // };*/ void k053260_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_k053260(UINT8 ChipID, int clock); void device_stop_k053260(UINT8 ChipID); void device_reset_k053260(UINT8 ChipID); //WRITE8_DEVICE_HANDLER( k053260_w ); //READ8_DEVICE_HANDLER( k053260_r ); void k053260_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 k053260_r(UINT8 ChipID, offs_t offset); void k053260_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void k053260_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(K053260, k053260); ================================================ FILE: VGMPlay/chips/k054539.c ================================================ /********************************************************* Konami 054539 (TOP) PCM Sound Chip A lot of information comes from Amuse. Big thanks to them. *********************************************************/ //#include "emu.h" #include #include // for memset #include // for NULL #include #include "mamedef.h" #ifdef _DEBUG #include #endif #include "k054539.h" #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) /* Registers: 00..ff: 20 bytes/channel, 8 channels 00..02: pitch (lsb, mid, msb) 03: volume (0=max, 0x40=-36dB) 04: reverb volume (idem) 05: pan (1-f right, 10 middle, 11-1f left) 06..07: reverb delay (0=max, current computation non-trusted) 08..0a: loop (lsb, mid, msb) 0c..0e: start (lsb, mid, msb) (and current position ?) 100.1ff: effects? 13f: pan of the analog input (1-1f) 200..20f: 2 bytes/channel, 8 channels 00: type (b2-3), reverse (b5) 01: loop (b0) 214: Key on (b0-7 = channel 0-7) 215: Key off "" 225: ? 227: Timer frequency 228: ? 229: ? 22a: ? 22b: ? 22c: Channel active? (b0-7 = channel 0-7) 22d: Data read/write port 22e: ROM/RAM select (00..7f == ROM banks, 80 = Reverb RAM) 22f: Global control: .......x - Enable PCM ......x. - Timer related? ...x.... - Enable ROM/RAM readback from 0x22d ..x..... - Timer output enable? x....... - Disable register RAM updates The chip has an optional 0x8000 byte reverb buffer. The reverb delay is actually an offset in this buffer. */ typedef struct _k054539_channel k054539_channel; struct _k054539_channel { UINT32 pos; UINT32 pfrac; INT32 val; INT32 pval; }; typedef struct _k054539_state k054539_state; struct _k054539_state { //const k054539_interface *intf; //device_t *device; double voltab[256]; double pantab[0xf]; double k054539_gain[8]; UINT8 k054539_posreg_latch[8][3]; int k054539_flags; unsigned char regs[0x230]; unsigned char *ram; int reverb_pos; INT32 cur_ptr; int cur_limit; unsigned char *cur_zone; unsigned char *rom; UINT32 rom_size; UINT32 rom_mask; //sound_stream * stream; k054539_channel channels[8]; UINT8 Muted[8]; int clock; }; #define MAX_CHIPS 0x02 static k054539_state K054539Data[MAX_CHIPS]; /*INLINE k054539_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == K054539); return (k054539_state *)downcast(device)->token(); }*/ //* //void k054539_init_flags(device_t *device, int flags) void k054539_init_flags(UINT8 ChipID, int flags) { //k054539_state *info = get_safe_token(device); k054539_state *info = &K054539Data[ChipID]; info->k054539_flags = flags; } //void k054539_set_gain(device_t *device, int channel, double gain) void k054539_set_gain(UINT8 ChipID, int channel, double gain) { //k054539_state *info = get_safe_token(device); k054539_state *info = &K054539Data[ChipID]; if (gain >= 0) info->k054539_gain[channel] = gain; } //* static int k054539_regupdate(k054539_state *info) { return !(info->regs[0x22f] & 0x80); } static void k054539_keyon(k054539_state *info, int channel) { if(k054539_regupdate(info)) info->regs[0x22c] |= 1 << channel; } static void k054539_keyoff(k054539_state *info, int channel) { if(k054539_regupdate(info)) info->regs[0x22c] &= ~(1 << channel); } //static STREAM_UPDATE( k054539_update ) void k054539_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //k054539_state *info = (k054539_state *)param; k054539_state *info = &K054539Data[ChipID]; #define VOL_CAP 1.80 static const INT16 dpcm[16] = { 0<<8, 1<<8, 4<<8, 9<<8, 16<<8, 25<<8, 36<<8, 49<<8, -64<<8, -49<<8, -36<<8, -25<<8, -16<<8, -9<<8, -4<<8, -1<<8 }; INT16 *rbase = (INT16 *)info->ram; unsigned char *rom; UINT32 rom_mask; int i, ch; double lval, rval; unsigned char *base1, *base2; k054539_channel *chan; int delta, vol, bval, pan; double cur_gain, lvol, rvol, rbvol; int rdelta; UINT32 cur_pos; int fdelta, pdelta; int cur_pfrac, cur_val, cur_pval; memset(outputs[0], 0, samples*sizeof(*outputs[0])); memset(outputs[1], 0, samples*sizeof(*outputs[1])); if(!(info->regs[0x22f] & 1)) return; rom = info->rom; rom_mask = info->rom_mask; for(i = 0; i != samples; i++) { if(!(info->k054539_flags & K054539_DISABLE_REVERB)) lval = rval = rbase[info->reverb_pos]; else lval = rval = 0; rbase[info->reverb_pos] = 0; for(ch=0; ch<8; ch++) if(info->regs[0x22c] & (1<Muted[ch]) { base1 = info->regs + 0x20*ch; base2 = info->regs + 0x200 + 0x2*ch; chan = info->channels + ch; delta = base1[0x00] | (base1[0x01] << 8) | (base1[0x02] << 16); vol = base1[0x03]; bval = vol + base1[0x04]; if (bval > 255) bval = 255; pan = base1[0x05]; // DJ Main: 81-87 right, 88 middle, 89-8f left if (pan >= 0x81 && pan <= 0x8f) pan -= 0x81; else if (pan >= 0x11 && pan <= 0x1f) pan -= 0x11; else pan = 0x18 - 0x11; cur_gain = info->k054539_gain[ch]; lvol = info->voltab[vol] * info->pantab[pan] * cur_gain; if (lvol > VOL_CAP) lvol = VOL_CAP; rvol = info->voltab[vol] * info->pantab[0xe - pan] * cur_gain; if (rvol > VOL_CAP) rvol = VOL_CAP; rbvol= info->voltab[bval] * cur_gain / 2; if (rbvol > VOL_CAP) rbvol = VOL_CAP; rdelta = (base1[6] | (base1[7] << 8)) >> 3; rdelta = (rdelta + info->reverb_pos) & 0x3fff; cur_pos = base1[0x0c] | (base1[0x0d] << 8) | (base1[0x0e] << 16); if(base2[0] & 0x20) { delta = -delta; fdelta = +0x10000; pdelta = -1; } else { fdelta = -0x10000; pdelta = +1; } if(cur_pos != chan->pos) { chan->pos = cur_pos; cur_pfrac = 0; cur_val = 0; cur_pval = 0; } else { cur_pfrac = chan->pfrac; cur_val = chan->val; cur_pval = chan->pval; } switch(base2[0] & 0xc) { case 0x0: { // 8bit pcm cur_pfrac += delta; while(cur_pfrac & ~0xffff) { cur_pfrac += fdelta; cur_pos += pdelta; cur_pval = cur_val; cur_val = (INT16)(rom[cur_pos & rom_mask] << 8); if(cur_val == (INT16)0x8000 && (base2[1] & 1)) { cur_pos = base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16); cur_val = (INT16)(rom[cur_pos & rom_mask] << 8); } if(cur_val == (INT16)0x8000) { k054539_keyoff(info, ch); cur_val = 0; break; } } break; } case 0x4: { // 16bit pcm lsb first pdelta <<= 1; cur_pfrac += delta; while(cur_pfrac & ~0xffff) { cur_pfrac += fdelta; cur_pos += pdelta; cur_pval = cur_val; cur_val = (INT16)(rom[cur_pos & rom_mask] | rom[(cur_pos+1) & rom_mask]<<8); if(cur_val == (INT16)0x8000 && (base2[1] & 1)) { cur_pos = base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16); cur_val = (INT16)(rom[cur_pos & rom_mask] | rom[(cur_pos+1) & rom_mask]<<8); } if(cur_val == (INT16)0x8000) { k054539_keyoff(info, ch); cur_val = 0; break; } } break; } case 0x8: { // 4bit dpcm cur_pos <<= 1; cur_pfrac <<= 1; if(cur_pfrac & 0x10000) { cur_pfrac &= 0xffff; cur_pos |= 1; } cur_pfrac += delta; while(cur_pfrac & ~0xffff) { cur_pfrac += fdelta; cur_pos += pdelta; cur_pval = cur_val; cur_val = rom[(cur_pos>>1) & rom_mask]; if(cur_val == 0x88 && (base2[1] & 1)) { cur_pos = (base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) << 1; cur_val = rom[(cur_pos>>1) & rom_mask]; } if(cur_val == 0x88) { k054539_keyoff(info, ch); cur_val = 0; break; } if(cur_pos & 1) cur_val >>= 4; else cur_val &= 15; cur_val = cur_pval + dpcm[cur_val]; if(cur_val < -32768) cur_val = -32768; else if(cur_val > 32767) cur_val = 32767; } cur_pfrac >>= 1; if(cur_pos & 1) cur_pfrac |= 0x8000; cur_pos >>= 1; break; } default: LOG(("Unknown sample type %x for channel %d\n", base2[0] & 0xc, ch)); break; } lval += cur_val * lvol; rval += cur_val * rvol; rbase[(rdelta + info->reverb_pos) & 0x1fff] += (INT16)(cur_val*rbvol); chan->pos = cur_pos; chan->pfrac = cur_pfrac; chan->pval = cur_pval; chan->val = cur_val; if(k054539_regupdate(info)) { base1[0x0c] = cur_pos & 0xff; base1[0x0d] = cur_pos>> 8 & 0xff; base1[0x0e] = cur_pos>>16 & 0xff; } } info->reverb_pos = (info->reverb_pos + 1) & 0x1fff; outputs[0][i] = (INT32)lval; outputs[1][i] = (INT32)rval; } } /*static TIMER_CALLBACK( k054539_irq ) { k054539_state *info = (k054539_state *)ptr; if(info->regs[0x22f] & 0x20) info->intf->irq(info->device); }*/ //static void k054539_init_chip(device_t *device, k054539_state *info) static int k054539_init_chip(k054539_state *info, int clock) { //int i; if (clock < 1000000) // if < 1 MHz, then it's the sample rate, not the clock clock *= 384; // (for backwards compatibility with old VGM logs) info->clock = clock; // most of these are done in device_reset // memset(info->regs, 0, sizeof(info->regs)); // memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch)); //* info->k054539_flags |= K054539_UPDATE_AT_KEYON; //* make it default until proven otherwise info->ram = (unsigned char*)malloc(0x4000); // info->reverb_pos = 0; // info->cur_ptr = 0; // memset(info->ram, 0, 0x4000); /*const memory_region *region = (info->intf->rgnoverride != NULL) ? device->machine().region(info->intf->rgnoverride) : device->region(); info->rom = *region; info->rom_size = region->bytes(); info->rom_mask = 0xffffffffU; for(i=0; i<32; i++) if((1U<= info->rom_size) { info->rom_mask = (1U<rom = NULL; info->rom_size = 0; info->rom_mask = 0x00; //if(info->intf->irq) // One or more of the registers must be the timer period // And anyway, this particular frequency is probably wrong // 480 hz is TRUSTED by gokuparo disco stage - the looping sample doesn't line up otherwise // device->machine().scheduler().timer_pulse(attotime::from_hz(480), FUNC(k054539_irq), 0, info); //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock() / 384, info, k054539_update); //device->save_item(NAME(info->regs)); //device->save_pointer(NAME(info->ram), 0x4000); //device->save_item(NAME(info->cur_ptr)); return info->clock / 384; } //WRITE8_DEVICE_HANDLER( k054539_w ) void k054539_w(UINT8 ChipID, offs_t offset, UINT8 data) { //k054539_state *info = get_safe_token(device); k054539_state *info = &K054539Data[ChipID]; #if 0 int voice, reg; /* The K054539 has behavior like many other wavetable chips including the Ensoniq 550x and Gravis GF-1: if a voice is active, writing to it's current position is silently ignored. Dadandaan depends on this or the vocals go wrong. */ if (offset < 8*0x20) { voice = offset / 0x20; reg = offset & ~0x20; if(info->regs[0x22c] & (1<= 0xc && reg <= 0xe) return; } } #endif int latch, offs, ch, pan; UINT8 *regbase, *regptr, *posptr; regbase = info->regs; latch = (info->k054539_flags & K054539_UPDATE_AT_KEYON) && (regbase[0x22f] & 1); if (latch && offset < 0x100) { offs = (offset & 0x1f) - 0xc; ch = offset >> 5; if (offs >= 0 && offs <= 2) { // latch writes to the position index registers info->k054539_posreg_latch[ch][offs] = data; return; } } else switch(offset) { case 0x13f: pan = data >= 0x11 && data <= 0x1f ? data - 0x11 : 0x18 - 0x11; //if(info->intf->apan) // info->intf->apan(info->device, info->pantab[pan], info->pantab[0xe - pan]); break; case 0x214: if (latch) { for(ch=0; ch<8; ch++) { if(data & (1<k054539_posreg_latch[ch][0]; regptr = regbase + (ch<<5) + 0xc; // update the chip at key-on regptr[0] = posptr[0]; regptr[1] = posptr[1]; regptr[2] = posptr[2]; k054539_keyon(info, ch); } } } else { for(ch=0; ch<8; ch++) if(data & (1<adjust(period, 0, period); m_timer_state = 0; m_timer_handler(m_timer_state); }*/ break; case 0x22d: if(regbase[0x22e] == 0x80) info->cur_zone[info->cur_ptr] = data; info->cur_ptr++; if(info->cur_ptr == info->cur_limit) info->cur_ptr = 0; break; case 0x22e: info->cur_zone = data == 0x80 ? info->ram : info->rom + 0x20000*data; info->cur_limit = data == 0x80 ? 0x4000 : 0x20000; info->cur_ptr = 0; break; /*case 0x22f: if (!(data & 0x20)) // Disable timer output? { m_timer_state = 0; m_timer_handler(m_timer_state); } break;*/ default: #if 0 if(regbase[offset] != data) { if((offset & 0xff00) == 0) { chanoff = offset & 0x1f; if(chanoff < 4 || chanoff == 5 || (chanoff >=8 && chanoff <= 0xa) || (chanoff >= 0xc && chanoff <= 0xe)) break; } if(1 || ((offset >= 0x200) && (offset <= 0x210))) break; logerror("K054539 %03x = %02x\n", offset, data); } #endif break; } regbase[offset] = data; } static void reset_zones(k054539_state *info) { int data = info->regs[0x22e]; info->cur_zone = data == 0x80 ? info->ram : info->rom + 0x20000*data; info->cur_limit = data == 0x80 ? 0x4000 : 0x20000; } //READ8_DEVICE_HANDLER( k054539_r ) UINT8 k054539_r(UINT8 ChipID, offs_t offset) { //k054539_state *info = get_safe_token(device); k054539_state *info = &K054539Data[ChipID]; switch(offset) { case 0x22d: if(info->regs[0x22f] & 0x10) { UINT8 res = info->cur_zone[info->cur_ptr]; info->cur_ptr++; if(info->cur_ptr == info->cur_limit) info->cur_ptr = 0; return res; } else return 0; case 0x22c: break; default: LOG(("K054539 read %03x\n", offset)); break; } return info->regs[offset]; } //static DEVICE_START( k054539 ) int device_start_k054539(UINT8 ChipID, int clock) { //static const k054539_interface defintrf = { 0 }; int i; //k054539_state *info = get_safe_token(device); k054539_state *info; if (ChipID >= MAX_CHIPS) return 0; info = &K054539Data[ChipID]; //info->device = device; for (i = 0; i < 8; i++) info->k054539_gain[i] = 1.0; info->k054539_flags = K054539_RESET_FLAGS; //info->intf = (device->static_config() != NULL) ? (const k054539_interface *)device->static_config() : &defintrf; /* I've tried various equations on volume control but none worked consistently. The upper four channels in most MW/GX games simply need a significant boost to sound right. For example, the bass and smash sound volumes in Violent Storm have roughly the same values and the voices in Tokimeki Puzzledama are given values smaller than those of the hihats. Needless to say the two K054539 chips in Mystic Warriors are completely out of balance. Rather than forcing a "one size fits all" function to the voltab the current invert exponential appraoch seems most appropriate. */ // Factor the 1/4 for the number of channels in the volume (1/8 is too harsh, 1/2 gives clipping) // vol=0 -> no attenuation, vol=0x40 -> -36dB for(i=0; i<256; i++) info->voltab[i] = pow(10.0, (-36.0 * (double)i / (double)0x40) / 20.0) / 4.0; // Pan table for the left channel // Right channel is identical with inverted index // Formula is such that pan[i]**2+pan[0xe-i]**2 = 1 (constant output power) // and pan[0xe] = 1 (full panning) for(i=0; i<0xf; i++) info->pantab[i] = sqrt((double)i) / sqrt((double)0xe); //k054539_init_chip(device, info); //device->machine().save().register_postload(save_prepost_delegate(FUNC(reset_zones), info)); for (i = 0; i < 8; i ++) info->Muted[i] = 0x00; return k054539_init_chip(info, clock); } void device_stop_k054539(UINT8 ChipID) { k054539_state *info = &K054539Data[ChipID]; free(info->rom); info->rom = NULL; free(info->ram); info->ram = NULL; return; } void device_reset_k054539(UINT8 ChipID) { k054539_state *info = &K054539Data[ChipID]; memset(info->regs, 0, sizeof(info->regs)); memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch)); //info->k054539_flags |= K054539_UPDATE_AT_KEYON; info->reverb_pos = 0; info->cur_ptr = 0; memset(info->ram, 0, 0x4000); return; } void k054539_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { k054539_state *info = &K054539Data[ChipID]; if (info->rom_size != ROMSize) { UINT8 i; info->rom = (UINT8*)realloc(info->rom, ROMSize); info->rom_size = ROMSize; memset(info->rom, 0xFF, ROMSize); info->rom_mask = 0xFFFFFFFF; for (i = 0; i < 32; i ++) { if ((1U << i) >= info->rom_size) { info->rom_mask = (1 << i) - 1; break; } } } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(info->rom + DataStart, ROMData, DataLength); return; } void k054539_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { k054539_state *info = &K054539Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) info->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( k054539 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k054539_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k054539 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: // nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "K054539"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ //DEFINE_LEGACY_SOUND_DEVICE(K054539, k054539); ================================================ FILE: VGMPlay/chips/k054539.h ================================================ /********************************************************* Konami 054539 PCM Sound Chip *********************************************************/ #pragma once //#include "devlegcy.h" /*typedef struct _k054539_interface k054539_interface; struct _k054539_interface { const char *rgnoverride; void (*apan)(device_t *, double, double); // Callback for analog output mixing levels (0..1 for each channel) void (*irq)(device_t *); };*/ void k054539_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_k054539(UINT8 ChipID, int clock); void device_stop_k054539(UINT8 ChipID); void device_reset_k054539(UINT8 ChipID); //WRITE8_DEVICE_HANDLER( k054539_w ); //READ8_DEVICE_HANDLER( k054539_r ); void k054539_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 k054539_r(UINT8 ChipID, offs_t offset); //* control flags, may be set at DRIVER_INIT(). #define K054539_RESET_FLAGS 0 #define K054539_REVERSE_STEREO 1 #define K054539_DISABLE_REVERB 2 #define K054539_UPDATE_AT_KEYON 4 //void k054539_init_flags(device_t *device, int flags); void k054539_init_flags(UINT8 ChipID, int flags); /* Note that the eight PCM channels of a K054539 do not have separate volume controls. Considering the global attenuation equation may not be entirely accurate, k054539_set_gain() provides means to control channel gain. It can be called anywhere but preferrably from DRIVER_INIT(). Parameters: chip : 0 / 1 channel : 0 - 7 gain : 0.0=silent, 1.0=no gain, 2.0=twice as loud, etc. */ //void k054539_set_gain(device_t *device, int channel, double gain); void k054539_set_gain(UINT8 ChipID, int channel, double gain); void k054539_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void k054539_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(K054539, k054539); ================================================ FILE: VGMPlay/chips/mamedef.h ================================================ #ifndef __MAMEDEF_H__ #define __MAMEDEF_H__ // typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) /* 8-bit values */ typedef unsigned char UINT8; typedef signed char INT8; /* 16-bit values */ typedef unsigned short UINT16; typedef signed short INT16; /* 32-bit values */ #ifndef _WINDOWS_H typedef unsigned int UINT32; typedef signed int INT32; #endif /* 64-bit values */ #ifndef _WINDOWS_H #ifdef _MSC_VER typedef signed __int64 INT64; typedef unsigned __int64 UINT64; #else __extension__ typedef unsigned long long UINT64; __extension__ typedef signed long long INT64; #endif #endif /* offsets and addresses are 32-bit (for now...) */ typedef UINT32 offs_t; /* stream_sample_t is used to represent a single sample in a sound stream */ typedef INT32 stream_sample_t; #if defined(VGM_BIG_ENDIAN) #define BYTE_XOR_BE(x) (x) #elif defined(VGM_LITTLE_ENDIAN) #define BYTE_XOR_BE(x) ((x) ^ 0x01) #else // don't define BYTE_XOR_BE so that it throws an error when compiling #endif #if defined(_MSC_VER) //#define INLINE static __forceinline #define INLINE static __inline #elif defined(__GNUC__) #define INLINE static __inline__ #else #define INLINE static inline #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef _DEBUG #define logerror printf #else #define logerror #endif extern stream_sample_t* DUMMYBUF[]; typedef void (*SRATE_CALLBACK)(void*, UINT32); #endif // __MAMEDEF_H__ ================================================ FILE: VGMPlay/chips/multipcm.c ================================================ /* * Sega System 32 Multi/Model 1/Model 2 custom PCM chip (315-5560) emulation. * * by Miguel Angel Horna (ElSemi) for Model 2 Emulator and MAME. * Information by R.Belmont and the YMF278B (OPL4) manual. * * voice registers: * 0: Pan * 1: Index of sample * 2: LSB of pitch (low 2 bits seem unused so) * 3: MSB of pitch (ooooppppppppppxx) (o=octave (4 bit signed), p=pitch (10 bits), x=unused? * 4: voice control: top bit = 1 for key on, 0 for key off * 5: bit 0: 0: interpolate volume changes, 1: direct set volume, bits 1-7 = volume attenuate (0=max, 7f=min) * 6: LFO frequency + Phase LFO depth * 7: Amplitude LFO size * * The first sample ROM contains a variable length table with 12 * bytes per instrument/sample. This is very similar to the YMF278B. * * The first 3 bytes are the offset into the file (big endian). * The next 2 are the loop start offset into the file (big endian) * The next 2 are the 2's complement of the total sample size (big endian) * The next byte is LFO freq + depth (copied to reg 6 ?) * The next 3 are envelope params (Attack, Decay1 and 2, sustain level, release, Key Rate Scaling) * The next byte is Amplitude LFO size (copied to reg 7 ?) * * TODO * - The YM278B manual states that the chip supports 512 instruments. The MultiPCM probably supports them * too but the high bit position is unknown (probably reg 2 low bit). Any game use more than 256? * */ //#include "emu.h" //#include "streams.h" #include "mamedef.h" #include #include #include // for memset #include // for NULL #include "multipcm.h" //???? #define MULTIPCM_CLOCKDIV (180.0) //#define SHOW_WARNINGS static UINT8 didWarn = 0x00; struct _Sample { unsigned int Start; unsigned int Loop; unsigned int End; unsigned char AR,DR1,DR2,DL,RR; unsigned char KRS; unsigned char LFOVIB; unsigned char AM; }; typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE; struct _EG { int volume; // _STATE state; int step; //step vals int AR; //Attack int D1R; //Decay1 int D2R; //Decay2 int RR; //Release int DL; //Decay level }; struct _LFO { unsigned short phase; UINT32 phase_step; int *table; int *scale; }; struct _SLOT { unsigned char Num; unsigned char Regs[8]; int Playing; struct _Sample *Sample; unsigned int Base; unsigned int offset; unsigned int step; unsigned int Pan,TL; unsigned int DstTL; int TLStep; signed int Prev; struct _EG EG; struct _LFO PLFO; //Phase lfo struct _LFO ALFO; //AM lfo UINT8 Muted; }; typedef struct _MultiPCM MultiPCM; struct _MultiPCM { //sound_stream * stream; struct _Sample Samples[0x200]; //Max 512 samples struct _SLOT Slots[28]; unsigned int CurSlot; unsigned int Address; unsigned int Bank; unsigned int BankR,BankL; float Rate; UINT32 ROMMask; UINT32 ROMSize; INT8 *ROM; //I include these in the chip because they depend on the chip clock unsigned int ARStep[0x40],DRStep[0x40]; //Envelope step table unsigned int FNS_Table[0x400]; //Frequency step table }; static UINT8 IsInit = 0x00; static signed int LPANTABLE[0x800],RPANTABLE[0x800]; #define FIX(v) ((UINT32) ((float) (1<type() == MULTIPCM); return (MultiPCM *)downcast(device)->token(); }*/ /******************************* ENVELOPE SECTION *******************************/ //Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup static const double BaseTimes[64]={0,0,0,0,6222.95,4978.37,4148.66,3556.01,3111.47,2489.21,2074.33,1778.00,1555.74,1244.63,1037.19,889.02, 777.87,622.31,518.59,444.54,388.93,311.16,259.32,222.27,194.47,155.60,129.66,111.16,97.23,77.82,64.85,55.60, 48.62,38.91,32.43,27.80,24.31,19.46,16.24,13.92,12.15,9.75,8.12,6.98,6.08,4.90,4.08,3.49, 3.04,2.49,2.13,1.90,1.72,1.41,1.18,1.04,0.91,0.73,0.59,0.50,0.45,0.45,0.45,0.45}; #define AR2DR 14.32833 static signed int lin2expvol[0x400]; static int TLSteps[2]; #define EG_SHIFT 16 static int EG_Update(struct _SLOT *slot) { switch(slot->EG.state) { case ATTACK: slot->EG.volume+=slot->EG.AR; if(slot->EG.volume>=(0x3ff<EG.state=DECAY1; if(slot->EG.D1R>=(0x400<EG.state=DECAY2; slot->EG.volume=0x3ff<EG.volume-=slot->EG.D1R; if(slot->EG.volume<=0) slot->EG.volume=0; if(slot->EG.volume>>EG_SHIFT<=(slot->EG.DL<<(10-4))) slot->EG.state=DECAY2; break; case DECAY2: slot->EG.volume-=slot->EG.D2R; if(slot->EG.volume<=0) slot->EG.volume=0; break; case RELEASE: slot->EG.volume-=slot->EG.RR; if(slot->EG.volume<=0) { slot->EG.volume=0; slot->Playing=0; } break; default: return 1<EG.volume>>EG_SHIFT]; } static unsigned int Get_RATE(unsigned int *Steps,unsigned int rate,unsigned int val) { int r=4*val+rate; if(val==0) return Steps[0]; if(val==0xf) return Steps[0x3f]; if(r>0x3f) r=0x3f; return Steps[r]; } static void EG_Calc(MultiPCM *ptChip,struct _SLOT *slot) { int octave=((slot->Regs[3]>>4)-1)&0xf; int rate; if(octave&8) octave=octave-16; if(slot->Sample->KRS!=0xf) rate=(octave+slot->Sample->KRS)*2+((slot->Regs[3]>>3)&1); else rate=0; slot->EG.AR=Get_RATE(ptChip->ARStep,rate,slot->Sample->AR); slot->EG.D1R=Get_RATE(ptChip->DRStep,rate,slot->Sample->DR1); slot->EG.D2R=Get_RATE(ptChip->DRStep,rate,slot->Sample->DR2); slot->EG.RR=Get_RATE(ptChip->DRStep,rate,slot->Sample->RR); slot->EG.DL=0xf-slot->Sample->DL; } /***************************** LFO SECTION *****************************/ #define LFO_SHIFT 8 #define LFIX(v) ((unsigned int) ((float) (1<phase+=LFO->phase_step; p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff]; p=LFO->scale[p+128]; return p<<(SHIFT-LFO_SHIFT); } INLINE signed int ALFO_Step(struct _LFO *LFO) { int p; LFO->phase+=LFO->phase_step; p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff]; p=LFO->scale[p]; return p<<(SHIFT-LFO_SHIFT); } static void LFO_ComputeStep(MultiPCM *ptChip,struct _LFO *LFO,UINT32 LFOF,UINT32 LFOS,int ALFO) { float step=(float) LFOFreq[LFOF]*256.0/(float) ptChip->Rate; LFO->phase_step=(unsigned int) ((float) (1<table=ALFO_TRI; LFO->scale=ASCALES[LFOS]; } else { LFO->table=PLFO_TRI; LFO->scale=PSCALES[LFOS]; } } static void WriteSlot(MultiPCM *ptChip,struct _SLOT *slot,int reg,unsigned char data) { slot->Regs[reg]=data; switch(reg) { case 0: //PANPOT slot->Pan=(data>>4)&0xf; break; case 1: //Sample //according to YMF278 sample write causes some base params written to the regs (envelope+lfos) //the game should never change the sample while playing. { struct _Sample *Sample=ptChip->Samples+slot->Regs[1]+((slot->Regs[2]&1)<<8); WriteSlot(ptChip,slot,6,Sample->LFOVIB); WriteSlot(ptChip,slot,7,Sample->AM); } break; case 2: //Pitch case 3: { unsigned int oct=((slot->Regs[3]>>4)-1)&0xf; unsigned int pitch=((slot->Regs[3]&0xf)<<6)|(slot->Regs[2]>>2); pitch=ptChip->FNS_Table[pitch]; if(oct&0x8) pitch>>=(16-oct); else pitch<<=oct; slot->step=pitch/ptChip->Rate; } break; case 4: //KeyOn/Off (and more?) { if(data&0x80) //KeyOn { UINT16 sampleID = slot->Regs[1]+((slot->Regs[2]&1)<<8); if (ptChip->Bank && sampleID >= 0x100) { #ifdef SHOW_WARNINGS if (! (didWarn & 0x02)) { didWarn |= 0x02; printf("YMW Warning: SEGA Banking + playing sample with ID 0x100+\n"); } #endif sampleID &= 0x0FF; } slot->Sample=ptChip->Samples+sampleID; //slot->Sample=ptChip->Samples+slot->Regs[1]+((slot->Regs[2]&1)<<8); slot->Playing=1; slot->Base=slot->Sample->Start; slot->offset=0; slot->Prev=0; slot->TL=slot->DstTL<EG.state=ATTACK; slot->EG.volume=0; if(ptChip->Bank && slot->Base>=0x100000) { #ifdef SHOW_WARNINGS UINT8 otherBnk; if (slot->Pan & 8) otherBnk = (ptChip->Bank != ptChip->BankL); else otherBnk = (ptChip->Bank != ptChip->BankR); if (otherBnk) { if (! (didWarn & 0x01)) { didWarn |= 0x01; printf("YMW Warning: Playing sound on possibly unintended bank!\n"); } } #endif slot->Base=(slot->Base&0xfffff)|(ptChip->Bank); } } else { if(slot->Playing) { if(slot->Sample->RR!=0xf) slot->EG.state=RELEASE; else slot->Playing=0; } } } break; case 5: //TL+Interpolation { slot->DstTL=(data>>1)&0x7f; if(!(data&1)) //Interpolate TL { if((slot->TL>>SHIFT)>slot->DstTL) slot->TLStep=TLSteps[0]; //decrease else slot->TLStep=TLSteps[1]; //increase } else slot->TL=slot->DstTL<PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0); LFO_ComputeStep(ptChip,&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1); } } break; case 7: //ALFO { if(data) { LFO_ComputeStep(ptChip,&(slot->PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0); LFO_ComputeStep(ptChip,&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1); } } break; } } //static STREAM_UPDATE( MultiPCM_update ) void MultiPCM_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //MultiPCM *ptChip = (MultiPCM *)param; MultiPCM *ptChip = &MultiPCMData[ChipID]; stream_sample_t *datap[2]; int i,sl; datap[0] = outputs[0]; datap[1] = outputs[1]; memset(datap[0], 0, sizeof(*datap[0])*samples); memset(datap[1], 0, sizeof(*datap[1])*samples); for(i=0;iSlots+sl; if(slot->Playing && ! slot->Muted) { unsigned int vol=(slot->TL>>SHIFT)|(slot->Pan<<7); unsigned int adr=slot->offset>>SHIFT; signed int sample; unsigned int step=slot->step; signed int csample=(signed short) (ptChip->ROM[(slot->Base+adr) & ptChip->ROMMask]<<8); signed int fpart=slot->offset&((1<Prev*((1<>SHIFT; if(slot->Regs[6]&7) //Vibrato enabled { step=step*PLFO_Step(&(slot->PLFO)); step>>=SHIFT; } slot->offset+=step; if(slot->offset>=(slot->Sample->End<offset=slot->Sample->Loop<offset>>SHIFT)) { slot->Prev=csample; } if((slot->TL>>SHIFT)!=slot->DstTL) slot->TL+=slot->TLStep; if(slot->Regs[7]&7) //Tremolo enabled { sample=sample*ALFO_Step(&(slot->ALFO)); sample>>=SHIFT; } sample=(sample*EG_Update(slot))>>10; smpl+=(LPANTABLE[vol]*sample)>>SHIFT; smpr+=(RPANTABLE[vol]*sample)>>SHIFT; } } /*#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) datap[0][i]=ICLIP16(smpl); datap[1][i]=ICLIP16(smpr);*/ datap[0][i] = smpl; datap[1][i] = smpr; } } //READ8_DEVICE_HANDLER( multipcm_r ) UINT8 multipcm_r(UINT8 ChipID, offs_t offset) { // MultiPCM *ptChip = get_safe_token(device); // MultiPCM *ptChip = &MultiPCMData[ChipID]; return 0; } //static DEVICE_START( multipcm ) int device_start_multipcm(UINT8 ChipID, int clock) { //MultiPCM *ptChip = get_safe_token(device); MultiPCM *ptChip; int i; if (ChipID >= MAX_CHIPS) return 0; ptChip = &MultiPCMData[ChipID]; //ptChip->ROM=*device->region(); ptChip->ROMMask = 0x00; ptChip->ROMSize = 0x00; ptChip->ROM = NULL; //ptChip->Rate=(float) device->clock() / MULTIPCM_CLOCKDIV; ptChip->Rate=(float) clock / MULTIPCM_CLOCKDIV; //ptChip->stream = stream_create(device, 0, 2, ptChip->Rate, ptChip, MultiPCM_update); if (! IsInit) { //Volume+pan table for(i=0;i<0x800;++i) { float SegaDB=0; float TL; float LPAN,RPAN; unsigned char iTL=i&0x7f; unsigned char iPAN=(i>>7)&0xf; SegaDB=(float) iTL*(-24.0)/(float) 0x40; TL=pow(10.0,SegaDB/20.0); if(iPAN==0x8) { LPAN=RPAN=0.0; } else if(iPAN==0x0) { LPAN=RPAN=1.0; } else if(iPAN&0x8) { LPAN=1.0; iPAN=0x10-iPAN; SegaDB=(float) iPAN*(-12.0)/(float) 0x4; RPAN=pow(10.0,SegaDB/20.0); if((iPAN&0x7)==7) RPAN=0.0; } else { RPAN=1.0; SegaDB=(float) iPAN*(-12.0)/(float) 0x4; LPAN=pow(10.0,SegaDB/20.0); if((iPAN&0x7)==7) LPAN=0.0; } TL/=4.0; LPANTABLE[i]=FIX((LPAN*TL)); RPANTABLE[i]=FIX((RPAN*TL)); } IsInit = 0x01; } //Pitch steps for(i=0;i<0x400;++i) { float fcent=ptChip->Rate*(1024.0+(float) i)/1024.0; ptChip->FNS_Table[i]=(unsigned int ) ((float) (1<ARStep[i]=(float) (0x400<DRStep[i]=(float) (0x400<ARStep[0]=ptChip->ARStep[1]=ptChip->ARStep[2]=ptChip->ARStep[3]=0; ptChip->ARStep[0x3f]=0x400<DRStep[0]=ptChip->DRStep[1]=ptChip->DRStep[2]=ptChip->DRStep[3]=0; //TL Interpolation steps //lower TLSteps[0]=-(float) (0x80<exponential ramps for(i=0;i<0x400;++i) { float db=-(96.0-(96.0*(float) i/(float) 0x400)); lin2expvol[i]=pow(10.0,db/20.0)*(float) (1<ROM+i*12; ptChip->Samples[i].Start=(ptSample[0]<<16)|(ptSample[1]<<8)|(ptSample[2]<<0); ptChip->Samples[i].Loop=(ptSample[3]<<8)|(ptSample[4]<<0); ptChip->Samples[i].End=0xffff-((ptSample[5]<<8)|(ptSample[6]<<0)); ptChip->Samples[i].LFOVIB=ptSample[7]; ptChip->Samples[i].DR1=ptSample[8]&0xf; ptChip->Samples[i].AR=(ptSample[8]>>4)&0xf; ptChip->Samples[i].DR2=ptSample[9]&0xf; ptChip->Samples[i].DL=(ptSample[9]>>4)&0xf; ptChip->Samples[i].RR=ptSample[10]&0xf; ptChip->Samples[i].KRS=(ptSample[10]>>4)&0xf; ptChip->Samples[i].AM=ptSample[11]; }*/ /*state_save_register_device_item(device, 0, ptChip->CurSlot); state_save_register_device_item(device, 0, ptChip->Address); state_save_register_device_item(device, 0, ptChip->BankL); state_save_register_device_item(device, 0, ptChip->BankR);*/ // reset is done via DEVICE_RESET /*for(i=0;i<28;++i) { ptChip->Slots[i].Num=i; ptChip->Slots[i].Playing=0; state_save_register_device_item(device, i, ptChip->Slots[i].Num); state_save_register_device_item_array(device, i, ptChip->Slots[i].Regs); state_save_register_device_item(device, i, ptChip->Slots[i].Playing); state_save_register_device_item(device, i, ptChip->Slots[i].Base); state_save_register_device_item(device, i, ptChip->Slots[i].offset); state_save_register_device_item(device, i, ptChip->Slots[i].step); state_save_register_device_item(device, i, ptChip->Slots[i].Pan); state_save_register_device_item(device, i, ptChip->Slots[i].TL); state_save_register_device_item(device, i, ptChip->Slots[i].DstTL); state_save_register_device_item(device, i, ptChip->Slots[i].TLStep); state_save_register_device_item(device, i, ptChip->Slots[i].Prev); state_save_register_device_item(device, i, ptChip->Slots[i].EG.volume); state_save_register_device_item(device, i, ptChip->Slots[i].EG.state); state_save_register_device_item(device, i, ptChip->Slots[i].EG.step); state_save_register_device_item(device, i, ptChip->Slots[i].EG.AR); state_save_register_device_item(device, i, ptChip->Slots[i].EG.D1R); state_save_register_device_item(device, i, ptChip->Slots[i].EG.D2R); state_save_register_device_item(device, i, ptChip->Slots[i].EG.RR); state_save_register_device_item(device, i, ptChip->Slots[i].EG.DL); state_save_register_device_item(device, i, ptChip->Slots[i].PLFO.phase); state_save_register_device_item(device, i, ptChip->Slots[i].PLFO.phase_step); state_save_register_device_item(device, i, ptChip->Slots[i].ALFO.phase); state_save_register_device_item(device, i, ptChip->Slots[i].ALFO.phase_step); }*/ LFO_Init(); didWarn = 0x00; multipcm_set_bank(ChipID, 0x00, 0x00); return (int)(ptChip->Rate + 0.5); } void device_stop_multipcm(UINT8 ChipID) { MultiPCM *ptChip = &MultiPCMData[ChipID]; free(ptChip->ROM); ptChip->ROM = NULL; return; } void device_reset_multipcm(UINT8 ChipID) { MultiPCM *ptChip = &MultiPCMData[ChipID]; int i; for(i=0;i<28;++i) { ptChip->Slots[i].Num=i; ptChip->Slots[i].Playing=0; } return; } //WRITE8_DEVICE_HANDLER( multipcm_w ) void multipcm_w(UINT8 ChipID, offs_t offset, UINT8 data) { //MultiPCM *ptChip = get_safe_token(device); MultiPCM *ptChip = &MultiPCMData[ChipID]; switch(offset) { case 0: //Data write WriteSlot(ptChip,ptChip->Slots+ptChip->CurSlot,ptChip->Address,data); break; case 1: ptChip->CurSlot=val2chan[data&0x1f]; break; case 2: ptChip->Address=(data>7)?7:data; break; } /*ptChip->CurSlot = val2chan[(offset >> 3) & 0x1F]; ptChip->Address = offset & 0x07; WriteSlot(ptChip, ptChip->Slots + ptChip->CurSlot, ptChip->Address, data);*/ } /* MAME/M1 access functions */ //void multipcm_set_bank(running_device *device, UINT32 leftoffs, UINT32 rightoffs) void multipcm_set_bank(UINT8 ChipID, UINT32 leftoffs, UINT32 rightoffs) { //MultiPCM *ptChip = get_safe_token(device); MultiPCM *ptChip = &MultiPCMData[ChipID]; ptChip->BankL = leftoffs; ptChip->BankR = rightoffs; ptChip->Bank = ptChip->BankR ? ptChip->BankR : ptChip->BankL; } void multipcm_bank_write(UINT8 ChipID, UINT8 offset, UINT16 data) { MultiPCM *ptChip = &MultiPCMData[ChipID]; if (offset & 0x01) ptChip->BankL = data << 16; if (offset & 0x02) ptChip->BankR = data << 16; ptChip->Bank = ptChip->BankR ? ptChip->BankR : ptChip->BankL; #ifdef SHOW_WARNINGS if (ptChip->BankL != 0 && ptChip->BankR != 0 && ptChip->BankL != ptChip->BankR) //printf("Bank L/R mismatch: 0x%X != 0x%X\n", ptChip->BankL, ptChip->BankR); printf("YMW Banks: 0x%X != 0x%X \n", ptChip->BankL, ptChip->BankR); else printf("YMW Banks: 0x%X / 0x%X \n", ptChip->BankL, ptChip->BankR); #endif return; } void multipcm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { MultiPCM *ptChip = &MultiPCMData[ChipID]; UINT16 CurSmpl; struct _Sample* TempSmpl; UINT8* ptSample; if (ptChip->ROMSize != ROMSize) { ptChip->ROM = (INT8*)realloc(ptChip->ROM, ROMSize); ptChip->ROMSize = ROMSize; for (ptChip->ROMMask = 1; ptChip->ROMMask < ROMSize; ptChip->ROMMask <<= 1) ; ptChip->ROMMask --; memset(ptChip->ROM, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(ptChip->ROM + DataStart, ROMData, DataLength); if (DataStart < 0x200 * 12) { for (CurSmpl = 0; CurSmpl < 512; CurSmpl ++) { TempSmpl = &ptChip->Samples[CurSmpl]; ptSample = (UINT8*)ptChip->ROM + CurSmpl * 12; TempSmpl->Start = (ptSample[0]<<16)|(ptSample[1]<<8)|(ptSample[2]<<0); TempSmpl->Loop = (ptSample[3]<<8)|(ptSample[4]<<0); TempSmpl->End = 0xffff-((ptSample[5]<<8)|(ptSample[6]<<0)); TempSmpl->LFOVIB = ptSample[7]; TempSmpl->DR1 = ptSample[8]&0xf; TempSmpl->AR = (ptSample[8]>>4)&0xf; TempSmpl->DR2 = ptSample[9]&0xf; TempSmpl->DL = (ptSample[9]>>4)&0xf; TempSmpl->RR = ptSample[10]&0xf; TempSmpl->KRS = (ptSample[10]>>4)&0xf; TempSmpl->AM = ptSample[11]; } } return; } void multipcm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { MultiPCM* ptChip = &MultiPCMData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 28; CurChn ++) ptChip->Slots[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } #if 0 // for debugging only UINT8 multipcm_get_channels(UINT8 ChipID, UINT32* ChannelMask) { MultiPCM* ptChip = &MultiPCMData[ChipID]; UINT8 CurChn; UINT8 UsedChns; UINT32 ChnMask; ChnMask = 0x00000000; UsedChns = 0x00; for (CurChn = 0; CurChn < 28; CurChn ++) { if (ptChip->Slots[CurChn].Playing) { ChnMask |= (1 << CurChn); UsedChns ++; } } if (ChannelMask != NULL) *ChannelMask = ChnMask; return UsedChns; } #endif /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( multipcm ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(MultiPCM); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( multipcm ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: // Nothing break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "Sega/Yamaha 315-5560"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "2.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ //DEFINE_LEGACY_SOUND_DEVICE(MULTIPCM, multipcm); ================================================ FILE: VGMPlay/chips/multipcm.h ================================================ #pragma once //#include "devlegcy.h" void MultiPCM_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_multipcm(UINT8 ChipID, int clock); void device_stop_multipcm(UINT8 ChipID); void device_reset_multipcm(UINT8 ChipID); //WRITE8_DEVICE_HANDLER( multipcm_w ); //READ8_DEVICE_HANDLER( multipcm_r ); void multipcm_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 multipcm_r(UINT8 ChipID, offs_t offset); void multipcm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); //void multipcm_set_bank(running_device *device, UINT32 leftoffs, UINT32 rightoffs); void multipcm_set_bank(UINT8 ChipID, UINT32 leftoffs, UINT32 rightoffs); void multipcm_bank_write(UINT8 ChipID, UINT8 offset, UINT16 data); void multipcm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(MULTIPCM, multipcm); ================================================ FILE: VGMPlay/chips/nes_apu.c ================================================ /***************************************************************************** MAME/MESS NES APU CORE Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by Who Wants to Know? (wwtk@mail.com) This core is written with the advise and consent of Matthew Conte and is released under the GNU Public License. This core is freely avaiable for use in any freeware project, subject to the following terms: Any modifications to this code must be duly noted in the source and approved by Matthew Conte and myself prior to public submission. timing notes: master = 21477270 2A03 clock = master/12 sequencer = master/89490 or CPU/7457 ***************************************************************************** NES_APU.C Actual NES APU interface. LAST MODIFIED 02/29/2004 - Based on Matthew Conte's Nofrendo/Nosefart core and redesigned to use MAME system calls and to enable multiple APUs. Sound at this point should be just about 100% accurate, though I cannot tell for certain as yet. A queue interface is also available for additional speed. However, the implementation is not yet 100% (DPCM sounds are inaccurate), so it is disabled by default. ***************************************************************************** BUGFIXES: - Various bugs concerning the DPCM channel fixed. (Oliver Achten) - Fixed $4015 read behaviour. (Oliver Achten) *****************************************************************************/ #include "mamedef.h" #include #include // for memset #include // for NULL //#include "emu.h" //#include "streams.h" #include "nes_apu.h" //#include "cpu/m6502/m6502.h" #include "nes_defs.h" /* GLOBAL CONSTANTS */ #define SYNCS_MAX1 0x20 #define SYNCS_MAX2 0x80 /* GLOBAL VARIABLES */ typedef struct _nesapu_state nesapu_state; struct _nesapu_state { apu_t APU; /* Actual APUs */ float apu_incsize; /* Adjustment increment */ uint32 samps_per_sync; /* Number of samples per vsync */ uint32 buffer_size; /* Actual buffer size in bytes */ uint32 real_rate; /* Actual playback rate */ uint8 noise_lut[NOISE_LONG]; /* Noise sample lookup table */ uint32 vbl_times[0x20]; /* VBL durations in samples */ uint32 sync_times1[SYNCS_MAX1]; /* Samples per sync table */ uint32 sync_times2[SYNCS_MAX2]; /* Samples per sync table */ //sound_stream *stream; }; static UINT8 DPCMBase0 = 0x01; //#define MAX_CHIPS 0x02 //static nesapu_state NESAPUData[MAX_CHIPS]; /*INLINE nesapu_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == NES); return (nesapu_state *)downcast(device)->token(); }*/ /* INTERNAL FUNCTIONS */ /* INITIALIZE WAVE TIMES RELATIVE TO SAMPLE RATE */ static void create_vbltimes(uint32 * table,const uint8 *vbl,unsigned int rate) { int i; for (i = 0; i < 0x20; i++) table[i] = vbl[i] * rate; } /* INITIALIZE SAMPLE TIMES IN TERMS OF VSYNCS */ static void create_syncs(nesapu_state *info, unsigned long sps) { int i; unsigned long val = sps; for (i = 0; i < SYNCS_MAX1; i++) { info->sync_times1[i] = val; val += sps; } val = 0; for (i = 0; i < SYNCS_MAX2; i++) { info->sync_times2[i] = val; info->sync_times2[i] >>= 2; val += sps; } } /* INITIALIZE NOISE LOOKUP TABLE */ static void create_noise(uint8 *buf, const int bits, int size) { int m = 0x0011; int xor_val, i; for (i = 0; i < size; i++) { xor_val = m & 1; m >>= 1; xor_val ^= (m & 1); m |= xor_val << (bits - 1); buf[i] = m; } } /* TODO: sound channels should *ALL* have DC volume decay */ /* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM -16 to +15) */ static int8 apu_square(nesapu_state *info, square_t *chan) { int env_delay; int sweep_delay; int8 output; uint8 freq_index; /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on ** reg2: 8 bits of freq ** reg3: 0-2=high freq, 7-4=vbl length counter */ if (FALSE == chan->enabled || chan->Muted) return 0; /* enveloping */ env_delay = info->sync_times1[chan->regs[0] & 0x0F]; /* decay is at a rate of (env_regs + 1) / 240 secs */ chan->env_phase -= 4; while (chan->env_phase < 0) { chan->env_phase += env_delay; if (chan->regs[0] & 0x20) chan->env_vol = (chan->env_vol + 1) & 15; else if (chan->env_vol < 15) chan->env_vol++; } /* vbl length counter */ if (chan->vbl_length > 0 && 0 == (chan->regs [0] & 0x20)) chan->vbl_length--; if (0 == chan->vbl_length) return 0; /* freqsweeps */ if ((chan->regs[1] & 0x80) && (chan->regs[1] & 7)) { sweep_delay = info->sync_times1[(chan->regs[1] >> 4) & 7]; chan->sweep_phase -= 2; while (chan->sweep_phase < 0) { chan->sweep_phase += sweep_delay; if (chan->regs[1] & 8) chan->freq -= chan->freq >> (chan->regs[1] & 7); else chan->freq += chan->freq >> (chan->regs[1] & 7); } } // if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[chan->regs[1] & 7]) // || (chan->freq >> 16) < 4) // return 0; // Thanks to Delek for the fix if (chan->regs[1] & 0x80) freq_index = chan->regs[1] & 7; //If sweeping is enabled, I choose it as normal. else freq_index = 7; //If sweeping is disabled, I choose the lower limit. if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[freq_index]) || (chan->freq >> 16) < 4) return 0; chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ while (chan->phaseacc < 0) { chan->phaseacc += (chan->freq >> 16); chan->adder = (chan->adder + 1) & 0x0F; } if (chan->regs[0] & 0x10) /* fixed volume */ output = chan->regs[0] & 0x0F; else output = 0x0F - chan->env_vol; if (chan->adder < (duty_lut[chan->regs[0] >> 6])) output = -output; return (int8) output; } /* OUTPUT TRIANGLE WAVE SAMPLE (VALUES FROM -16 to +15) */ static int8 apu_triangle(nesapu_state *info, triangle_t *chan) { int freq; int8 output; /* reg0: 7=holdnote, 6-0=linear length counter ** reg2: low 8 bits of frequency ** reg3: 7-3=length counter, 2-0=high 3 bits of frequency */ if (FALSE == chan->enabled || chan->Muted) return 0; if (FALSE == chan->counter_started && 0 == (chan->regs[0] & 0x80)) { if (chan->write_latency) chan->write_latency--; if (0 == chan->write_latency) chan->counter_started = TRUE; } if (chan->counter_started) { if (chan->linear_length > 0) chan->linear_length--; if (chan->vbl_length && 0 == (chan->regs[0] & 0x80)) chan->vbl_length--; if (0 == chan->vbl_length) return 0; } if (0 == chan->linear_length) return 0; freq = (((chan->regs[3] & 7) << 8) + chan->regs[2]) + 1; if (freq < 4) /* inaudible */ return 0; chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ while (chan->phaseacc < 0) { chan->phaseacc += freq; chan->adder = (chan->adder + 1) & 0x1F; output = (chan->adder & 7) << 1; if (chan->adder & 8) output = 0x10 - output; if (chan->adder & 0x10) output = -output; chan->output_vol = output; } return (int8) chan->output_vol; } /* OUTPUT NOISE WAVE SAMPLE (VALUES FROM -16 to +15) */ static int8 apu_noise(nesapu_state *info, noise_t *chan) { int freq, env_delay; uint8 outvol; uint8 output; /* reg0: 0-3=volume, 4=envelope, 5=hold ** reg2: 7=small(93 byte) sample,3-0=freq lookup ** reg3: 7-4=vbl length counter */ if (FALSE == chan->enabled || chan->Muted) return 0; /* enveloping */ env_delay = info->sync_times1[chan->regs[0] & 0x0F]; /* decay is at a rate of (env_regs + 1) / 240 secs */ chan->env_phase -= 4; while (chan->env_phase < 0) { chan->env_phase += env_delay; if (chan->regs[0] & 0x20) chan->env_vol = (chan->env_vol + 1) & 15; else if (chan->env_vol < 15) chan->env_vol++; } /* length counter */ if (0 == (chan->regs[0] & 0x20)) { if (chan->vbl_length > 0) chan->vbl_length--; } if (0 == chan->vbl_length) return 0; freq = noise_freq[chan->regs[2] & 0x0F]; chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ while (chan->phaseacc < 0) { chan->phaseacc += freq; chan->cur_pos++; if (NOISE_SHORT == chan->cur_pos && (chan->regs[2] & 0x80)) chan->cur_pos = 0; else if (NOISE_LONG == chan->cur_pos) chan->cur_pos = 0; } if (chan->regs[0] & 0x10) /* fixed volume */ outvol = chan->regs[0] & 0x0F; else outvol = 0x0F - chan->env_vol; output = info->noise_lut[chan->cur_pos]; if (output > outvol) output = outvol; if (info->noise_lut[chan->cur_pos] & 0x80) /* make it negative */ output = -output; return (int8) output; } /* RESET DPCM PARAMETERS */ INLINE void apu_dpcmreset(dpcm_t *chan) { chan->address = 0xC000 + (uint16) (chan->regs[2] << 6); chan->length = (uint16) (chan->regs[3] << 4) + 1; chan->bits_left = chan->length << 3; chan->irq_occurred = FALSE; chan->enabled = TRUE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ // Note: according to NSFPlay, it does NOT do that chan->vol = 0; /* Fixed * DPCM DAC resets itself when restarted */ } /* OUTPUT DPCM WAVE SAMPLE (VALUES FROM -64 to +63) */ /* TODO: centerline naughtiness */ static int8 apu_dpcm(nesapu_state *info, dpcm_t *chan) { int freq, bit_pos; /* reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table ** reg1: output dc level, 7 bits unsigned ** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64) ** reg3: length, (value * 16) + 1 */ if (chan->Muted) return 0; if (chan->enabled) { freq = dpcm_clocks[chan->regs[0] & 0x0F]; chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ while (chan->phaseacc < 0) { chan->phaseacc += freq; if (0 == chan->length) { chan->enabled = FALSE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ chan->vol=0; /* Fixed * DPCM DAC resets itself when restarted */ if (chan->regs[0] & 0x40) apu_dpcmreset(chan); else { if (chan->regs[0] & 0x80) /* IRQ Generator */ { chan->irq_occurred = TRUE; //n2a03_irq(info->APU.dpcm.memory->cpu); } break; } } chan->bits_left--; bit_pos = 7 - (chan->bits_left & 7); if (7 == bit_pos) { //chan->cur_byte = info->APU.dpcm.memory->read_byte(chan->address); chan->cur_byte = info->APU.dpcm.memory[chan->address]; chan->address++; // On overflow, the address is set to 8000 if (chan->address >= 0x10000) chan->address -= 0x8000; chan->length--; } if (chan->cur_byte & (1 << bit_pos)) // chan->regs[1]++; chan->vol+=2; /* FIXED * DPCM channel only uses the upper 6 bits of the DAC */ else // chan->regs[1]--; chan->vol-=2; } } if (! DPCMBase0) { if (chan->vol > 63) chan->vol = 63; else if (chan->vol < -64) chan->vol = -64; } else { if (chan->vol > 127) chan->vol = 127; else if (chan->vol < 0) chan->vol = 0; } return (int8) (chan->vol); //return (int8) 0; } /* WRITE REGISTER VALUE */ INLINE void apu_regwrite(nesapu_state *info,int address, uint8 value) { int chan = (address & 4) ? 1 : 0; switch (address) { /* squares */ case APU_WRA0: case APU_WRB0: info->APU.squ[chan].regs[0] = value; break; case APU_WRA1: case APU_WRB1: info->APU.squ[chan].regs[1] = value; break; case APU_WRA2: case APU_WRB2: info->APU.squ[chan].regs[2] = value; if (info->APU.squ[chan].enabled) info->APU.squ[chan].freq = ((((info->APU.squ[chan].regs[3] & 7) << 8) + value) + 1) << 16; break; case APU_WRA3: case APU_WRB3: info->APU.squ[chan].regs[3] = value; if (info->APU.squ[chan].enabled) { // TODO: Test, if it sounds better with or without it. // info->APU.squ[chan].adder = 0; // Thanks to Delek info->APU.squ[chan].vbl_length = info->vbl_times[value >> 3]; info->APU.squ[chan].env_vol = 0; info->APU.squ[chan].freq = ((((value & 7) << 8) + info->APU.squ[chan].regs[2]) + 1) << 16; } break; /* triangle */ case APU_WRC0: info->APU.tri.regs[0] = value; if (info->APU.tri.enabled) { /* ??? */ if (FALSE == info->APU.tri.counter_started) info->APU.tri.linear_length = info->sync_times2[value & 0x7F]; } break; //case 0x4009: case APU_WRC1: /* unused */ info->APU.tri.regs[1] = value; break; case APU_WRC2: info->APU.tri.regs[2] = value; break; case APU_WRC3: info->APU.tri.regs[3] = value; /* this is somewhat of a hack. there is some latency on the Real ** Thing between when trireg0 is written to and when the linear ** length counter actually begins its countdown. we want to prevent ** the case where the program writes to the freq regs first, then ** to reg 0, and the counter accidentally starts running because of ** the sound queue's timestamp processing. ** ** set to a few NES sample -- should be sufficient ** ** 3 * (1789772.727 / 44100) = ~122 cycles, just around one scanline ** ** should be plenty of time for the 6502 code to do a couple of table ** dereferences and load up the other triregs */ /* used to be 3, but now we run the clock faster, so base it on samples/sync */ info->APU.tri.write_latency = (info->samps_per_sync + 239) / 240; if (info->APU.tri.enabled) { info->APU.tri.counter_started = FALSE; info->APU.tri.vbl_length = info->vbl_times[value >> 3]; info->APU.tri.linear_length = info->sync_times2[info->APU.tri.regs[0] & 0x7F]; } break; /* noise */ case APU_WRD0: info->APU.noi.regs[0] = value; break; case 0x400D: /* unused */ info->APU.noi.regs[1] = value; break; case APU_WRD2: info->APU.noi.regs[2] = value; info->APU.noi.cur_pos = 0; // Thanks to Delek for this fix. break; case APU_WRD3: info->APU.noi.regs[3] = value; if (info->APU.noi.enabled) { info->APU.noi.vbl_length = info->vbl_times[value >> 3]; info->APU.noi.env_vol = 0; /* reset envelope */ } break; /* DMC */ case APU_WRE0: info->APU.dpcm.regs[0] = value; if (0 == (value & 0x80)) info->APU.dpcm.irq_occurred = FALSE; break; case APU_WRE1: /* 7-bit DAC */ //info->APU.dpcm.regs[1] = value - 0x40; info->APU.dpcm.regs[1] = value & 0x7F; if (! DPCMBase0) info->APU.dpcm.vol = (info->APU.dpcm.regs[1]-64); else info->APU.dpcm.vol = info->APU.dpcm.regs[1]; break; case APU_WRE2: info->APU.dpcm.regs[2] = value; //apu_dpcmreset(info->APU.dpcm); break; case APU_WRE3: info->APU.dpcm.regs[3] = value; break; case APU_IRQCTRL: if(value & 0x80) info->APU.step_mode = 5; else info->APU.step_mode = 4; break; case APU_SMASK: if (value & 0x01) info->APU.squ[0].enabled = TRUE; else { info->APU.squ[0].enabled = FALSE; info->APU.squ[0].vbl_length = 0; } if (value & 0x02) info->APU.squ[1].enabled = TRUE; else { info->APU.squ[1].enabled = FALSE; info->APU.squ[1].vbl_length = 0; } if (value & 0x04) info->APU.tri.enabled = TRUE; else { info->APU.tri.enabled = FALSE; info->APU.tri.vbl_length = 0; info->APU.tri.linear_length = 0; info->APU.tri.counter_started = FALSE; info->APU.tri.write_latency = 0; } if (value & 0x08) info->APU.noi.enabled = TRUE; else { info->APU.noi.enabled = FALSE; info->APU.noi.vbl_length = 0; } if (value & 0x10) { /* only reset dpcm values if DMA is finished */ if (FALSE == info->APU.dpcm.enabled) { info->APU.dpcm.enabled = TRUE; apu_dpcmreset(&info->APU.dpcm); } } else info->APU.dpcm.enabled = FALSE; info->APU.dpcm.irq_occurred = FALSE; break; default: #ifdef MAME_DEBUG logerror("invalid apu write: $%02X at $%04X\n", value, address); #endif break; } } /* UPDATE SOUND BUFFER USING CURRENT DATA */ //INLINE void apu_update(nesapu_state *info, stream_sample_t *buffer16, int samples) INLINE void apu_update(nesapu_state *info, stream_sample_t **buffer16, int samples) { int accum; stream_sample_t* bufL = buffer16[0]; stream_sample_t* bufR = buffer16[1]; while (samples--) { /*accum = apu_square(info, &info->APU.squ[0]); accum += apu_square(info, &info->APU.squ[1]); accum += apu_triangle(info, &info->APU.tri); accum += apu_noise(info, &info->APU.noi); accum += apu_dpcm(info, &info->APU.dpcm); // 8-bit clamps if (accum > 127) accum = 127; else if (accum < -128) accum = -128; *(bufL++)=accum<<8; *(bufR++)=accum<<8;*/ // These volumes should match NSFPlay's NES core better accum = apu_square(info, &info->APU.squ[0]) << 8; // << 8 * 1.0 accum += apu_square(info, &info->APU.squ[1]) << 8; // << 8 * 1.0 accum += apu_triangle(info, &info->APU.tri) * 0xC0; // << 8 * 0.75 accum += apu_noise(info, &info->APU.noi) * 0xC0; // << 8 * 0.75 accum += apu_dpcm(info, &info->APU.dpcm) * 0xC0; // << 8 * 0.75 *(bufL++)=accum; *(bufR++)=accum; } } /* READ VALUES FROM REGISTERS */ INLINE uint8 apu_read(nesapu_state *info,int address) { if (address == 0x15) /*FIXED* Address $4015 has different behaviour*/ { int readval = 0; if (info->APU.squ[0].vbl_length > 0) readval |= 0x01; if (info->APU.squ[1].vbl_length > 0) readval |= 0x02; if (info->APU.tri.vbl_length > 0) readval |= 0x04; if (info->APU.noi.vbl_length > 0) readval |= 0x08; if (info->APU.dpcm.enabled == TRUE) readval |= 0x10; if (info->APU.dpcm.irq_occurred == TRUE) readval |= 0x80; return readval; } else return info->APU.regs[address]; } /* WRITE VALUE TO TEMP REGISTRY AND QUEUE EVENT */ INLINE void apu_write(nesapu_state *info,int address, uint8 value) { if (address >= 0x20) return; info->APU.regs[address]=value; //stream_update(info->stream); apu_regwrite(info,address,value); } /* EXTERNAL INTERFACE FUNCTIONS */ /* REGISTER READ/WRITE FUNCTIONS */ //READ8_DEVICE_HANDLER( nes_psg_r ) UINT8 nes_psg_r(void* chip, offs_t offset) { //return apu_read(get_safe_token(device),offset); return apu_read((nesapu_state*)chip, offset); } //WRITE8_DEVICE_HANDLER( nes_psg_w ) void nes_psg_w(void* chip, offs_t offset, UINT8 data) { //apu_write(get_safe_token(device),offset,data); apu_write((nesapu_state*)chip, offset, data); } /* UPDATE APU SYSTEM */ //static STREAM_UPDATE( nes_psg_update_sound ) void nes_psg_update_sound(void* chip, stream_sample_t **outputs, int samples) { //nesapu_state *info = (nesapu_state *)param; nesapu_state *info = (nesapu_state*)chip; //apu_update(info, outputs[0], samples); apu_update(info, outputs, samples); } /* INITIALIZE APU SYSTEM */ #define SCREEN_HZ 60 //static DEVICE_START( nesapu ) void* device_start_nesapu(int clock, int rate) { //const nes_interface *intf = (const nes_interface *)device->baseconfig().static_config(); //nesapu_state *info = get_safe_token(device); nesapu_state *info; //int rate = clock / 4; //int i; // if (ChipID >= MAX_CHIPS) // return 0; info = (nesapu_state*)malloc(sizeof(nesapu_state)); if (info == NULL) return NULL; /* Initialize global variables */ //info->samps_per_sync = rate / ATTOSECONDS_TO_HZ(device->machine->primary_screen->frame_period().attoseconds); info->samps_per_sync = rate / SCREEN_HZ; info->buffer_size = info->samps_per_sync; //info->real_rate = info->samps_per_sync * ATTOSECONDS_TO_HZ(device->machine->primary_screen->frame_period().attoseconds); info->real_rate = info->samps_per_sync * SCREEN_HZ; //info->apu_incsize = (float) (device->clock() / (float) info->real_rate); info->apu_incsize = (float) (clock / (float) info->real_rate); /* Use initializer calls */ create_noise(info->noise_lut, 13, NOISE_LONG); create_vbltimes(info->vbl_times,vbl_length,info->samps_per_sync); create_syncs(info, info->samps_per_sync); /* Adjust buffer size if 16 bits */ info->buffer_size+=info->samps_per_sync; /* Initialize individual chips */ //(info->APU.dpcm).memory = cputag_get_address_space(device->machine, intf->cpu_tag, ADDRESS_SPACE_PROGRAM); // no idea how to obtain this info->APU.dpcm.memory = NULL; //info->stream = stream_create(device, 0, 1, rate, info, nes_psg_update_sound); /* register for save */ /*for (i = 0; i < 2; i++) { state_save_register_device_item_array(device, i, info->APU.squ[i].regs); state_save_register_device_item(device, i, info->APU.squ[i].vbl_length); state_save_register_device_item(device, i, info->APU.squ[i].freq); state_save_register_device_item(device, i, info->APU.squ[i].phaseacc); state_save_register_device_item(device, i, info->APU.squ[i].output_vol); state_save_register_device_item(device, i, info->APU.squ[i].env_phase); state_save_register_device_item(device, i, info->APU.squ[i].sweep_phase); state_save_register_device_item(device, i, info->APU.squ[i].adder); state_save_register_device_item(device, i, info->APU.squ[i].env_vol); state_save_register_device_item(device, i, info->APU.squ[i].enabled); } state_save_register_device_item_array(device, 0, info->APU.tri.regs); state_save_register_device_item(device, 0, info->APU.tri.linear_length); state_save_register_device_item(device, 0, info->APU.tri.vbl_length); state_save_register_device_item(device, 0, info->APU.tri.write_latency); state_save_register_device_item(device, 0, info->APU.tri.phaseacc); state_save_register_device_item(device, 0, info->APU.tri.output_vol); state_save_register_device_item(device, 0, info->APU.tri.adder); state_save_register_device_item(device, 0, info->APU.tri.counter_started); state_save_register_device_item(device, 0, info->APU.tri.enabled); state_save_register_device_item_array(device, 0, info->APU.noi.regs); state_save_register_device_item(device, 0, info->APU.noi.cur_pos); state_save_register_device_item(device, 0, info->APU.noi.vbl_length); state_save_register_device_item(device, 0, info->APU.noi.phaseacc); state_save_register_device_item(device, 0, info->APU.noi.output_vol); state_save_register_device_item(device, 0, info->APU.noi.env_phase); state_save_register_device_item(device, 0, info->APU.noi.env_vol); state_save_register_device_item(device, 0, info->APU.noi.enabled); state_save_register_device_item_array(device, 0, info->APU.dpcm.regs); state_save_register_device_item(device, 0, info->APU.dpcm.address); state_save_register_device_item(device, 0, info->APU.dpcm.length); state_save_register_device_item(device, 0, info->APU.dpcm.bits_left); state_save_register_device_item(device, 0, info->APU.dpcm.phaseacc); state_save_register_device_item(device, 0, info->APU.dpcm.output_vol); state_save_register_device_item(device, 0, info->APU.dpcm.cur_byte); state_save_register_device_item(device, 0, info->APU.dpcm.enabled); state_save_register_device_item(device, 0, info->APU.dpcm.irq_occurred); state_save_register_device_item(device, 0, info->APU.dpcm.vol); state_save_register_device_item_array(device, 0, info->APU.regs); #ifdef USE_QUEUE state_save_register_device_item_array(device, 0, info->APU.queue); state_save_register_device_item(device, 0, info->APU.head); state_save_register_device_item(device, 0, info->APU.tail); #else state_save_register_device_item(device, 0, info->APU.buf_pos); state_save_register_device_item(device, 0, info->APU.step_mode); #endif */ info->APU.squ[0].Muted = 0x00; info->APU.squ[1].Muted = 0x00; info->APU.tri.Muted = 0x00; info->APU.noi.Muted = 0x00; info->APU.dpcm.Muted = 0x00; return info; } void device_stop_nesapu(void* chip) { nesapu_state *info = (nesapu_state*)chip; info->APU.dpcm.memory = NULL; return; } void device_reset_nesapu(void* chip) { nesapu_state *info = (nesapu_state*)chip; const UINT8* MemPtr; UINT8 CurReg; MemPtr = info->APU.dpcm.memory; memset(&info->APU, 0x00, sizeof(apu_t)); info->APU.dpcm.memory = MemPtr; apu_dpcmreset(&info->APU.dpcm); for (CurReg = 0x00; CurReg < 0x18; CurReg ++) apu_write(info, CurReg, 0x00); apu_write(info, 0x15, 0x00); apu_write(info, 0x15, 0x0F); return; } void nesapu_set_rom(void* chip, const UINT8* ROMData) { nesapu_state *info = (nesapu_state*)chip; info->APU.dpcm.memory = ROMData; return; } void nesapu_set_mute_mask(void* chip, UINT32 MuteMask) { nesapu_state *info = (nesapu_state*)chip; info->APU.squ[0].Muted = (MuteMask >> 0) & 0x01; info->APU.squ[1].Muted = (MuteMask >> 1) & 0x01; info->APU.tri.Muted = (MuteMask >> 2) & 0x01; info->APU.noi.Muted = (MuteMask >> 3) & 0x01; info->APU.dpcm.Muted = (MuteMask >> 4) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( nesapu ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(nesapu_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( nesapu ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: // Nothing break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "N2A03"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Nintendo custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(NES, nesapu);*/ ================================================ FILE: VGMPlay/chips/nes_apu.h ================================================ /***************************************************************************** MAME/MESS NES APU CORE Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by Who Wants to Know? (wwtk@mail.com) This core is written with the advise and consent of Matthew Conte and is released under the GNU Public License. This core is freely avaiable for use in any freeware project, subject to the following terms: Any modifications to this code must be duly noted in the source and approved by Matthew Conte and myself prior to public submission. ***************************************************************************** NES_APU.H NES APU external interface. *****************************************************************************/ #pragma once #ifndef __NES_APU_H__ #define __NES_APU_H__ //#include "devlegcy.h" /* AN EXPLANATION * * The NES APU is actually integrated into the Nintendo processor. * You must supply the same number of APUs as you do processors. * Also make sure to correspond the memory regions to those used in the * processor, as each is shared. */ /*typedef struct _nes_interface nes_interface; struct _nes_interface { const char *cpu_tag; // CPU tag }; READ8_DEVICE_HANDLER( nes_psg_r ); WRITE8_DEVICE_HANDLER( nes_psg_w ); DECLARE_LEGACY_SOUND_DEVICE(NES, nesapu);*/ UINT8 nes_psg_r(void* chip, offs_t offset); void nes_psg_w(void* chip, offs_t offset, UINT8 data); void nes_psg_update_sound(void* chip, stream_sample_t **outputs, int samples); void* device_start_nesapu(int clock, int rate); void device_stop_nesapu(void* chip); void device_reset_nesapu(void* chip); void nesapu_set_rom(void* chip, const UINT8* ROMData); void nesapu_set_mute_mask(void* chip, UINT32 MuteMask); #endif /* __NES_APU_H__ */ ================================================ FILE: VGMPlay/chips/nes_defs.h ================================================ /***************************************************************************** MAME/MESS NES APU CORE Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by Who Wants to Know? (wwtk@mail.com) This core is written with the advise and consent of Matthew Conte and is released under the GNU Public License. This core is freely avaiable for use in any freeware project, subject to the following terms: Any modifications to this code must be duly noted in the source and approved by Matthew Conte and myself prior to public submission. ***************************************************************************** NES_DEFS.H NES APU internal type definitions and constants. *****************************************************************************/ #pragma once #ifndef __NES_DEFS_H__ #define __NES_DEFS_H__ /* BOOLEAN CONSTANTS */ #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* REGULAR TYPE DEFINITIONS */ typedef INT8 int8; typedef INT16 int16; typedef INT32 int32; typedef UINT8 uint8; typedef UINT16 uint16; typedef UINT32 uint32; typedef UINT8 boolean; /* QUEUE TYPES */ #ifdef USE_QUEUE #define QUEUE_SIZE 0x2000 #define QUEUE_MAX (QUEUE_SIZE-1) typedef struct queue_s { int pos; unsigned char reg,val; } queue_t; #endif /* REGISTER DEFINITIONS */ #define APU_WRA0 0x00 #define APU_WRA1 0x01 #define APU_WRA2 0x02 #define APU_WRA3 0x03 #define APU_WRB0 0x04 #define APU_WRB1 0x05 #define APU_WRB2 0x06 #define APU_WRB3 0x07 #define APU_WRC0 0x08 #define APU_WRC1 0x09 #define APU_WRC2 0x0A #define APU_WRC3 0x0B #define APU_WRD0 0x0C #define APU_WRD2 0x0E #define APU_WRD3 0x0F #define APU_WRE0 0x10 #define APU_WRE1 0x11 #define APU_WRE2 0x12 #define APU_WRE3 0x13 #define APU_SMASK 0x15 #define APU_IRQCTRL 0x17 #define NOISE_LONG 0x4000 #define NOISE_SHORT 93 /* CHANNEL TYPE DEFINITIONS */ /* Square Wave */ typedef struct square_s { uint8 regs[4]; int vbl_length; int freq; float phaseacc; float output_vol; float env_phase; float sweep_phase; uint8 adder; uint8 env_vol; boolean enabled; boolean Muted; } square_t; /* Triangle Wave */ typedef struct triangle_s { uint8 regs[4]; /* regs[1] unused */ int linear_length; int vbl_length; int write_latency; float phaseacc; float output_vol; uint8 adder; boolean counter_started; boolean enabled; boolean Muted; } triangle_t; /* Noise Wave */ typedef struct noise_s { uint8 regs[4]; /* regs[1] unused */ int cur_pos; int vbl_length; float phaseacc; float output_vol; float env_phase; uint8 env_vol; boolean enabled; boolean Muted; } noise_t; /* DPCM Wave */ typedef struct dpcm_s { uint8 regs[4]; uint32 address; uint32 length; int bits_left; float phaseacc; float output_vol; uint8 cur_byte; boolean enabled; boolean irq_occurred; //address_space *memory; const uint8 *memory; //signed char vol; signed short vol; boolean Muted; } dpcm_t; /* APU type */ typedef struct apu { /* Sound channels */ square_t squ[2]; triangle_t tri; noise_t noi; dpcm_t dpcm; /* APU registers */ unsigned char regs[0x20]; /* Sound pointers */ void *buffer; #ifdef USE_QUEUE /* Event queue */ queue_t queue[QUEUE_SIZE]; int head, tail; #else int buf_pos; #endif int step_mode; } apu_t; /* CONSTANTS */ /* vblank length table used for squares, triangle, noise */ static const uint8 vbl_length[32] = { 5, 127, 10, 1, 19, 2, 40, 3, 80, 4, 30, 5, 7, 6, 13, 7, 6, 8, 12, 9, 24, 10, 48, 11, 96, 12, 36, 13, 8, 14, 16, 15 }; /* frequency limit of square channels */ static const int freq_limit[8] = { //0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0, // Fixed, thanks to Delek 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F2, }; /* table of noise frequencies */ static const int noise_freq[16] = { //4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 2046 // Fixed, thanks to Delek 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }; /* dpcm transfer freqs */ static const int dpcm_clocks[16] = { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 85, 72, 54 }; /* ratios of pos/neg pulse for square waves */ /* 2/16 = 12.5%, 4/16 = 25%, 8/16 = 50%, 12/16 = 75% */ static const int duty_lut[4] = { 2, 4, 8, 12 }; #endif /* __NES_DEFS_H__ */ ================================================ FILE: VGMPlay/chips/nes_intf.c ================================================ /**************************************************************** MAME / MESS functions ****************************************************************/ #include "mamedef.h" #include // for free #include // for memset #include // for NULL #include "../stdbool.h" //#include "sndintrf.h" //#include "streams.h" #include "nes_apu.h" #include "np_nes_apu.h" #include "np_nes_dmc.h" #include "np_nes_fds.h" #include "nes_intf.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // NES core from MAME #endif #define EC_NSFPLAY 0x00 // NES core from NSFPlay // Note: FDS core from NSFPlay is always used /* for stream system */ typedef struct _nes_state nes_state; struct _nes_state { void* chip_apu; void* chip_dmc; void* chip_fds; UINT8* Memory; }; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static nes_state NESAPUData[MAX_CHIPS]; static UINT16 NesOptions = 0x8000; static void nes_set_chip_option(UINT8 ChipID); void nes_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { nes_state *info = &NESAPUData[ChipID]; int CurSmpl; INT32 Buffer[4]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: nes_psg_update_sound(info->chip_apu, outputs, samples); break; #endif case EC_NSFPLAY: for (CurSmpl = 0x00; CurSmpl < samples; CurSmpl ++) { NES_APU_np_Render(info->chip_apu, &Buffer[0]); NES_DMC_np_Render(info->chip_dmc, &Buffer[2]); outputs[0][CurSmpl] = Buffer[0] + Buffer[2]; outputs[1][CurSmpl] = Buffer[1] + Buffer[3]; } break; } if (info->chip_fds != NULL) { for (CurSmpl = 0x00; CurSmpl < samples; CurSmpl ++) { NES_FDS_Render(info->chip_fds, &Buffer[0]); outputs[0][CurSmpl] += Buffer[0]; outputs[1][CurSmpl] += Buffer[1]; } } return; } int device_start_nes(UINT8 ChipID, int clock) { nes_state *info; int rate; UINT8 EnableFDS; if (ChipID >= MAX_CHIPS) return 0; EnableFDS = (clock >> 31) & 0x01; clock &= 0x7FFFFFFF; info = &NESAPUData[ChipID]; rate = clock / 4; if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->chip_apu = device_start_nesapu(clock, rate); if (info->chip_apu == NULL) return 0; info->chip_dmc = NULL; info->chip_fds = NULL; info->Memory = (UINT8*)malloc(0x8000); memset(info->Memory, 0x00, 0x8000); nesapu_set_rom(info->chip_apu, info->Memory - 0x8000); break; #endif case EC_NSFPLAY: info->chip_apu = NES_APU_np_Create(clock, rate); if (info->chip_apu == NULL) return 0; info->chip_dmc = NES_DMC_np_Create(clock, rate); if (info->chip_dmc == NULL) { NES_APU_np_Destroy(info->chip_apu); info->chip_apu = NULL; return 0; } NES_DMC_np_SetAPU(info->chip_dmc, info->chip_apu); info->Memory = (UINT8*)malloc(0x8000); memset(info->Memory, 0x00, 0x8000); NES_DMC_np_SetMemory(info->chip_dmc, info->Memory - 0x8000); break; } if (EnableFDS) { info->chip_fds = NES_FDS_Create(clock, rate); // If it returns NULL, that's okay. } else { info->chip_fds = NULL; } nes_set_chip_option(ChipID); return rate; } void device_stop_nes(UINT8 ChipID) { nes_state *info = &NESAPUData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_stop_nesapu(info->chip_apu); break; #endif case EC_NSFPLAY: NES_APU_np_Destroy(info->chip_apu); NES_DMC_np_Destroy(info->chip_dmc); break; } if (info->chip_fds != NULL) NES_FDS_Destroy(info->chip_fds); if (info->Memory != NULL) { free(info->Memory); info->Memory = NULL; } info->chip_apu = NULL; info->chip_dmc = NULL; info->chip_fds = NULL; return; } void device_reset_nes(UINT8 ChipID) { nes_state *info = &NESAPUData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_reset_nesapu(info->chip_apu); break; #endif case EC_NSFPLAY: NES_APU_np_Reset(info->chip_apu); NES_DMC_np_Reset(info->chip_dmc); break; } if (info->chip_fds != NULL) NES_FDS_Reset(info->chip_fds); } void nes_w(UINT8 ChipID, offs_t offset, UINT8 data) { nes_state *info = &NESAPUData[ChipID]; switch(offset & 0xE0) { case 0x00: // NES APU switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: nes_psg_w(info->chip_apu, offset, data); break; #endif case EC_NSFPLAY: // NES_APU handles the sqaure waves, NES_DMC the rest NES_APU_np_Write(info->chip_apu, 0x4000 | offset, data); NES_DMC_np_Write(info->chip_dmc, 0x4000 | offset, data); break; } break; case 0x20: // FDS register if (info->chip_fds == NULL) break; if (offset == 0x3F) NES_FDS_Write(info->chip_fds, 0x4023, data); else NES_FDS_Write(info->chip_fds, 0x4080 | (offset & 0x1F), data); break; case 0x40: // FDS wave RAM case 0x60: if (info->chip_fds == NULL) break; NES_FDS_Write(info->chip_fds, 0x4000 | offset, data); break; } } void nes_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { nes_state* info = &NESAPUData[ChipID]; UINT32 RemainBytes; if (DataStart >= 0x10000) return; if (DataStart < 0x8000) { if (DataStart + DataLength <= 0x8000) return; RemainBytes = 0x8000 - DataStart; DataStart = 0x8000; RAMData += RemainBytes; DataLength -= RemainBytes; } RemainBytes = 0x00; if (DataStart + DataLength > 0x10000) { RemainBytes = DataLength; DataLength = 0x10000 - DataStart; RemainBytes -= DataLength; } memcpy(info->Memory + (DataStart - 0x8000), RAMData, DataLength); if (RemainBytes) { if (RemainBytes > 0x8000) RemainBytes = 0x8000; memcpy(info->Memory, RAMData + DataLength, RemainBytes); } return; } void nes_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_NSFPLAY; #endif return; } void nes_set_options(UINT16 Options) { NesOptions = Options; return; } static void nes_set_chip_option(UINT8 ChipID) { nes_state *info = &NESAPUData[ChipID]; UINT8 CurOpt; if (NesOptions & 0x8000) return; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: // no options for MAME's NES core break; #endif case EC_NSFPLAY: // shared APU/DMC options for (CurOpt = 0; CurOpt < 2; CurOpt ++) { NES_APU_np_SetOption(info->chip_apu, CurOpt, (NesOptions >> CurOpt) & 0x01); NES_DMC_np_SetOption(info->chip_dmc, CurOpt, (NesOptions >> CurOpt) & 0x01); } // APU-only options for (; CurOpt < 4; CurOpt ++) NES_APU_np_SetOption(info->chip_apu, CurOpt-2+2, (NesOptions >> CurOpt) & 0x01); // DMC-only options for (; CurOpt < 10; CurOpt ++) NES_DMC_np_SetOption(info->chip_dmc, CurOpt-4+2, (NesOptions >> CurOpt) & 0x01); break; } if (info->chip_fds != NULL) { // FDS options // I skip the Cutoff frequency here, since it's not a boolean value. for (CurOpt = 12; CurOpt < 14; CurOpt ++) NES_FDS_SetOption(info->chip_fds, CurOpt-12+1, (NesOptions >> CurOpt) & 0x01); } return; } void nes_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { nes_state *info = &NESAPUData[ChipID]; switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: nesapu_set_mute_mask(info->chip_apu, MuteMask); break; #endif case EC_NSFPLAY: NES_APU_np_SetMask(info->chip_apu, (MuteMask & 0x03) >> 0); NES_DMC_np_SetMask(info->chip_dmc, (MuteMask & 0x1C) >> 2); break; } if (info->chip_fds != NULL) NES_FDS_SetMask(info->chip_fds, (MuteMask & 0x20) >> 5); return; } ================================================ FILE: VGMPlay/chips/nes_intf.h ================================================ #pragma once void nes_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_nes(UINT8 ChipID, int clock); void device_stop_nes(UINT8 ChipID); void device_reset_nes(UINT8 ChipID); void nes_w(UINT8 ChipID, offs_t offset, UINT8 data); void nes_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void nes_set_emu_core(UINT8 Emulator); void nes_set_options(UINT16 Options); void nes_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/np_nes_apu.c ================================================ // // NES 2A03 // // Ported from NSFPlay 2.2 to VGMPlay (including C++ -> C conversion) // by Valley Bell on 24 September 2013 // Updated to NSFPlay 2.3 on 26 September 2013 // (Note: Encoding is UTF-8) //#include #include #include // for memset #include // for NULL #include "mamedef.h" #include "../stdbool.h" #include "np_nes_apu.h" // Master Clock: 21477272 (NTSC) // APU Clock = Master Clock / 12 #define DEFAULT_CLOCK 1789772.0 // not sure if this shouldn't be 1789772,667 instead #define DEFAULT_RATE 44100 /** Upper half of APU **/ enum { OPT_UNMUTE_ON_RESET=0, OPT_NONLINEAR_MIXER, OPT_PHASE_REFRESH, OPT_DUTY_SWAP, OPT_END }; enum { SQR0_MASK = 1, SQR1_MASK = 2, }; // Note: For increased speed, I'll inline all of NSFPlay's Counter member functions. #define COUNTER_SHIFT 24 typedef struct _Counter Counter; struct _Counter { double ratio; UINT32 val, step; }; #define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) #define COUNTER_iup(cntr) (cntr).val += (cntr).step #define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) #define COUNTER_init(cntr, clk, rate) \ { \ (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ (cntr).step = (UINT32)((cntr).ratio + 0.5); \ (cntr).val = 0; \ } typedef struct _NES_APU NES_APU; struct _NES_APU { int option[OPT_END]; // 各種オプション int mask; INT32 sm[2][2]; UINT32 gclock; UINT8 reg[0x20]; INT32 out[2]; double rate, clock; INT32 square_table[32]; // nonlinear mixer int scounter[2]; // frequency divider int sphase[2]; // phase counter int duty[2]; int volume[2]; int freq[2]; int sfreq[2]; bool sweep_enable[2]; bool sweep_mode[2]; bool sweep_write[2]; int sweep_div_period[2]; int sweep_div[2]; int sweep_amount[2]; bool envelope_disable[2]; bool envelope_loop[2]; bool envelope_write[2]; int envelope_div_period[2]; int envelope_div[2]; int envelope_counter[2]; int length_counter[2]; bool enable[2]; Counter tick_count; UINT32 tick_last; }; static void sweep_sqr(NES_APU* apu, int ch); // calculates target sweep frequency static INT32 calc_sqr(NES_APU* apu, int ch, UINT32 clocks); static void Tick(NES_APU* apu, UINT32 clocks); static void sweep_sqr(NES_APU* apu, int i) { int shifted = apu->freq[i] >> apu->sweep_amount[i]; if (i == 0 && apu->sweep_mode[i]) shifted += 1; apu->sfreq[i] = apu->freq[i] + (apu->sweep_mode[i] ? -shifted : shifted); //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,apu->freq[i],apu->sweep_amount[i]); } void NES_APU_np_FrameSequence(void* chip, int s) { NES_APU* apu = (NES_APU*)chip; int i; //DEBUG_OUT("FrameSequence(%d)\n",s); if (s > 3) return; // no operation in step 4 // 240hz clock for (i=0; i < 2; ++i) { bool divider = false; if (apu->envelope_write[i]) { apu->envelope_write[i] = false; apu->envelope_counter[i] = 15; apu->envelope_div[i] = 0; } else { ++apu->envelope_div[i]; if (apu->envelope_div[i] > apu->envelope_div_period[i]) { divider = true; apu->envelope_div[i] = 0; } } if (divider) { if (apu->envelope_loop[i] && apu->envelope_counter[i] == 0) apu->envelope_counter[i] = 15; else if (apu->envelope_counter[i] > 0) --apu->envelope_counter[i]; } } // 120hz clock if ((s&1) == 0) for (i=0; i < 2; ++i) { if (!apu->envelope_loop[i] && (apu->length_counter[i] > 0)) --apu->length_counter[i]; if (apu->sweep_enable[i]) { //DEBUG_OUT("Clock sweep: %d\n", i); --apu->sweep_div[i]; if (apu->sweep_div[i] <= 0) { sweep_sqr(apu, i); // calculate new sweep target //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,apu->sweep_div_period[i]); //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,apu->freq[i],i,apu->sfreq[i]); if (apu->freq[i] >= 8 && apu->sfreq[i] < 0x800 && apu->sweep_amount[i] > 0) // update frequency if appropriate { apu->freq[i] = apu->sfreq[i] < 0 ? 0 : apu->sfreq[i]; if (apu->scounter[i] > apu->freq[i]) apu->scounter[i] = apu->freq[i]; } apu->sweep_div[i] = apu->sweep_div_period[i] + 1; //DEBUG_OUT("freq[%d]=%d\n",i,apu->freq[i]); } if (apu->sweep_write[i]) { apu->sweep_div[i] = apu->sweep_div_period[i] + 1; apu->sweep_write[i] = false; } } } } static INT32 calc_sqr(NES_APU* apu, int i, UINT32 clocks) { static const INT16 sqrtbl[4][16] = { {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} }; INT32 ret = 0; apu->scounter[i] += clocks; while (apu->scounter[i] > apu->freq[i]) { apu->sphase[i] = (apu->sphase[i] + 1) & 15; apu->scounter[i] -= (apu->freq[i] + 1); } //INT32 ret = 0; if (apu->length_counter[i] > 0 && apu->freq[i] >= 8 && apu->sfreq[i] < 0x800 ) { int v = apu->envelope_disable[i] ? apu->volume[i] : apu->envelope_counter[i]; ret = sqrtbl[apu->duty[i]][apu->sphase[i]] ? v : 0; } return ret; } bool NES_APU_np_Read(void* chip, UINT32 adr, UINT32* val) { NES_APU* apu = (NES_APU*)chip; if (0x4000 <= adr && adr < 0x4008) { *val |= apu->reg[adr&0x7]; return true; } else if(adr==0x4015) { *val |= (apu->length_counter[1]?2:0)|(apu->length_counter[0]?1:0); return true; } else return false; } static void Tick(NES_APU* apu, UINT32 clocks) { apu->out[0] = calc_sqr(apu, 0, clocks); apu->out[1] = calc_sqr(apu, 1, clocks); } // 生成される波形の振幅は0-8191 UINT32 NES_APU_np_Render(void* chip, INT32 b[2]) { NES_APU* apu = (NES_APU*)chip; INT32 m[2]; COUNTER_iup(apu->tick_count); Tick(apu, (COUNTER_value(apu->tick_count) - apu->tick_last) & 0xFF); apu->tick_last = COUNTER_value(apu->tick_count); apu->out[0] = (apu->mask & 1) ? 0 : apu->out[0]; apu->out[1] = (apu->mask & 2) ? 0 : apu->out[1]; if(apu->option[OPT_NONLINEAR_MIXER]) { INT32 voltage; INT32 ref; voltage = apu->square_table[apu->out[0] + apu->out[1]]; m[0] = apu->out[0] << 6; m[1] = apu->out[1] << 6; ref = m[0] + m[1]; if (ref > 0) { m[0] = (m[0] * voltage) / ref; m[1] = (m[1] * voltage) / ref; } else { m[0] = voltage; m[1] = voltage; } } else { m[0] = apu->out[0] << 6; m[1] = apu->out[1] << 6; } // Shifting is (x-2) to match the volume of MAME's NES APU sound core b[0] = m[0] * apu->sm[0][0]; b[0] += m[1] * apu->sm[0][1]; b[0] >>= 7-2; // was 7, but is now 8 for bipolar square b[1] = m[0] * apu->sm[1][0]; b[1] += m[1] * apu->sm[1][1]; b[1] >>= 7-2; // see above return 2; } void* NES_APU_np_Create(int clock, int rate) { NES_APU* apu; int i, c, t; apu = (NES_APU*)malloc(sizeof(NES_APU)); if (apu == NULL) return NULL; memset(apu, 0x00, sizeof(NES_APU)); //NES_APU_np_SetClock(apu, DEFAULT_CLOCK); //NES_APU_np_SetRate(apu, DEFAULT_RATE); NES_APU_np_SetClock(apu, clock); NES_APU_np_SetRate(apu, rate); apu->option[OPT_UNMUTE_ON_RESET] = true; apu->option[OPT_PHASE_REFRESH] = true; apu->option[OPT_NONLINEAR_MIXER] = true; apu->option[OPT_DUTY_SWAP] = false; apu->square_table[0] = 0; for(i=1;i<32;i++) apu->square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); for(c=0;c<2;++c) for(t=0;t<2;++t) apu->sm[c][t] = 128; return apu; } void NES_APU_np_Destroy(void* chip) { free(chip); } void NES_APU_np_Reset(void* chip) { NES_APU* apu = (NES_APU*)chip; int i; apu->gclock = 0; apu->mask = 0; apu->scounter[0] = 0; apu->scounter[1] = 0; apu->sphase[0] = 0; apu->sphase[0] = 0; apu->sweep_div[0] = 1; apu->sweep_div[1] = 1; apu->envelope_div[0] = 0; apu->envelope_div[1] = 0; apu->length_counter[0] = 0; apu->length_counter[1] = 0; apu->envelope_counter[0] = 0; apu->envelope_counter[1] = 0; for (i = 0x4000; i < 0x4008; i++) NES_APU_np_Write(apu, i, 0); NES_APU_np_Write(apu, 0x4015, 0); if (apu->option[OPT_UNMUTE_ON_RESET]) NES_APU_np_Write(apu, 0x4015, 0x0f); for (i = 0; i < 2; i++) apu->out[i] = 0; NES_APU_np_SetRate(apu, apu->rate); } void NES_APU_np_SetOption(void* chip, int id, int val) { NES_APU* apu = (NES_APU*)chip; if(idoption[id] = val; } void NES_APU_np_SetClock(void* chip, double c) { NES_APU* apu = (NES_APU*)chip; apu->clock = c; } void NES_APU_np_SetRate(void* chip, double r) { NES_APU* apu = (NES_APU*)chip; apu->rate = r ? r : DEFAULT_RATE; COUNTER_init(apu->tick_count, apu->clock, apu->rate); apu->tick_last = 0; } void NES_APU_np_SetMask(void* chip, int m) { NES_APU* apu = (NES_APU*)chip; apu->mask = m; } void NES_APU_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) { NES_APU* apu = (NES_APU*)chip; if (trk < 0) return; if (trk > 1) return; apu->sm[0][trk] = mixl; apu->sm[1][trk] = mixr; } bool NES_APU_np_Write(void* chip, UINT32 adr, UINT32 val) { NES_APU* apu = (NES_APU*)chip; int ch; static const UINT8 length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E }; if (0x4000 <= adr && adr < 0x4008) { //DEBUG_OUT("$%04X = %02X\n",adr,val); adr &= 0xf; ch = adr >> 2; switch (adr) { case 0x0: case 0x4: apu->volume[ch] = val & 15; apu->envelope_disable[ch] = (val >> 4) & 1; apu->envelope_loop[ch] = (val >> 5) & 1; apu->envelope_div_period[ch] = (val & 15); apu->duty[ch] = (val >> 6) & 3; if (apu->option[OPT_DUTY_SWAP]) { if (apu->duty[ch] == 1) apu->duty[ch] = 2; else if (apu->duty[ch] == 2) apu->duty[ch] = 1; } break; case 0x1: case 0x5: apu->sweep_enable[ch] = (val >> 7) & 1; apu->sweep_div_period[ch] = (((val >> 4) & 7)); apu->sweep_mode[ch] = (val >> 3) & 1; apu->sweep_amount[ch] = val & 7; apu->sweep_write[ch] = true; sweep_sqr(apu, ch); break; case 0x2: case 0x6: apu->freq[ch] = val | (apu->freq[ch] & 0x700) ; sweep_sqr(apu, ch); if (apu->scounter[ch] > apu->freq[ch]) apu->scounter[ch] = apu->freq[ch]; break; case 0x3: case 0x7: apu->freq[ch] = (apu->freq[ch] & 0xFF) | ((val & 0x7) << 8) ; if (apu->option[OPT_PHASE_REFRESH]) apu->sphase[ch] = 0; apu->envelope_write[ch] = true; if (apu->enable[ch]) { apu->length_counter[ch] = length_table[(val >> 3) & 0x1f]; } sweep_sqr(apu, ch); if (apu->scounter[ch] > apu->freq[ch]) apu->scounter[ch] = apu->freq[ch]; break; default: return false; } apu->reg[adr] = val; return true; } else if (adr == 0x4015) { apu->enable[0] = (val & 1) ? true : false; apu->enable[1] = (val & 2) ? true : false; if (!apu->enable[0]) apu->length_counter[0] = 0; if (!apu->enable[1]) apu->length_counter[1] = 0; apu->reg[adr-0x4000] = val; return true; } // 4017 is handled in np_nes_dmc.c //else if (adr == 0x4017) //{ //} return false; } ================================================ FILE: VGMPlay/chips/np_nes_apu.h ================================================ void NES_APU_np_FrameSequence(void* chip, int s); void* NES_APU_np_Create(int clock, int rate); void NES_APU_np_Destroy(void* chip); void NES_APU_np_Reset(void* chip); bool NES_APU_np_Read(void* chip, UINT32 adr, UINT32* val); bool NES_APU_np_Write(void* chip, UINT32 adr, UINT32 val); UINT32 NES_APU_np_Render(void* chip, INT32 b[2]); void NES_APU_np_SetRate(void* chip, double rate); void NES_APU_np_SetClock(void* chip, double clock); void NES_APU_np_SetOption(void* chip, int id, int b); void NES_APU_np_SetMask(void* chip, int m); void NES_APU_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); ================================================ FILE: VGMPlay/chips/np_nes_dmc.c ================================================ // Ported from NSFPlay to VGMPlay (including C++ -> C conversion) // by Valley Bell on 25 September 2013 // Updated to NSFPlay 2.3 on 26 September 2013 // (Note: Encoding is UTF-8) #include // for rand #include #include // for memset #include // for NULL #include "mamedef.h" #include "../stdbool.h" #include "np_nes_apu.h" // for NES_APU_np_FrameSequence #include "np_nes_dmc.h" // Master Clock: 21477272 (NTSC) // APU Clock = Master Clock / 12 #define DEFAULT_CLOCK 1789772.0 #define DEFAULT_CLK_PAL 1662607 #define DEFAULT_RATE 44100 /** Bottom Half of APU **/ enum { OPT_UNMUTE_ON_RESET=0, OPT_NONLINEAR_MIXER, OPT_ENABLE_4011, OPT_ENABLE_PNOISE, OPT_DPCM_ANTI_CLICK, OPT_RANDOMIZE_NOISE, OPT_TRI_MUTE, OPT_TRI_NULL, OPT_END }; // Note: For increased speed, I'll inline all of NSFPlay's Counter member functions. #define COUNTER_SHIFT 24 typedef struct _Counter Counter; struct _Counter { double ratio; UINT32 val, step; }; #define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) #define COUNTER_iup(cntr) (cntr).val += (cntr).step #define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) #define COUNTER_init(cntr, clk, rate) \ { \ (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ (cntr).step = (UINT32)((cntr).ratio + 0.5); \ (cntr).val = 0; \ } typedef struct _NES_DMC NES_DMC; struct _NES_DMC { //const int GETA_BITS; //static const UINT32 freq_table[2][16]; //static const UINT32 wavlen_table[2][16]; UINT32 tnd_table[2][16][16][128]; int option[OPT_END]; int mask; INT32 sm[2][3]; UINT8 reg[0x10]; UINT32 len_reg; UINT32 adr_reg; //IDevice *memory; const UINT8* memory; UINT32 out[3]; UINT32 daddress; UINT32 length; UINT32 data; INT16 damp; int dac_lsb; bool dmc_pop; INT32 dmc_pop_offset; INT32 dmc_pop_follow; UINT32 clock; UINT32 rate; int pal; int mode; bool irq; bool active; UINT32 counter[3]; // frequency dividers int tphase; // triangle phase UINT32 nfreq; // noise frequency UINT32 dfreq; // DPCM frequency UINT32 tri_freq; int linear_counter; int linear_counter_reload; bool linear_counter_halt; bool linear_counter_control; int noise_volume; UINT32 noise, noise_tap; // noise envelope bool envelope_loop; bool envelope_disable; bool envelope_write; int envelope_div_period; int envelope_div; int envelope_counter; bool enable[3]; int length_counter[2]; // 0=tri, 1=noise // frame sequencer void* apu; // apu is clocked by DMC's frame sequencer int frame_sequence_count; // current cycle count int frame_sequence_length; // CPU cycles per FrameSequence int frame_sequence_step; // current step of frame sequence int frame_sequence_steps; // 4/5 steps per frame bool frame_irq; bool frame_irq_enable; Counter tick_count; UINT32 tick_last; }; INLINE UINT32 calc_tri(NES_DMC* dmc, UINT32 clocks); INLINE UINT32 calc_dmc(NES_DMC* dmc, UINT32 clocks); INLINE UINT32 calc_noise(NES_DMC* dmc, UINT32 clocks); static void FrameSequence(NES_DMC* dmc, int s); static void TickFrameSequence(NES_DMC* dmc, UINT32 clocks); static void Tick(NES_DMC* dmc, UINT32 clocks); #define GETA_BITS 20 static const UINT32 wavlen_table[2][16] = { { // NTSC 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }, { // PAL 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 }}; static const UINT32 freq_table[2][16] = { { // NTSC 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 }, { // PAL 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 }}; void* NES_DMC_np_Create(int clock, int rate) { NES_DMC* dmc; int c, t; dmc = (NES_DMC*)malloc(sizeof(NES_DMC)); if (dmc == NULL) return NULL; memset(dmc, 0x00, sizeof(NES_DMC)); //NES_DMC_np_SetClock(dmc, DEFAULT_CLOCK); //NES_DMC_np_SetRate(dmc, DEFAULT_RATE); //NES_DMC_np_SetPal(dmc, false); NES_DMC_np_SetClock(dmc, clock); // does SetPal, too NES_DMC_np_SetRate(dmc, rate); dmc->option[OPT_ENABLE_4011] = 1; dmc->option[OPT_ENABLE_PNOISE] = 1; dmc->option[OPT_UNMUTE_ON_RESET] = 1; dmc->option[OPT_DPCM_ANTI_CLICK] = 0; dmc->option[OPT_NONLINEAR_MIXER] = 1; dmc->option[OPT_RANDOMIZE_NOISE] = 1; dmc->option[OPT_TRI_MUTE] = 1; dmc->tnd_table[0][0][0][0] = 0; dmc->tnd_table[1][0][0][0] = 0; dmc->apu = NULL; dmc->frame_sequence_count = 0; dmc->frame_sequence_length = 7458; dmc->frame_sequence_steps = 4; for(c=0;c<2;++c) for(t=0;t<3;++t) dmc->sm[c][t] = 128; return dmc; } void NES_DMC_np_Destroy(void* chip) { free(chip); } int NES_DMC_np_GetDamp(void* chip) { NES_DMC* dmc = (NES_DMC*)chip; return (dmc->damp<<1)|dmc->dac_lsb; } void NES_DMC_np_SetMask(void* chip, int m) { NES_DMC* dmc = (NES_DMC*)chip; dmc->mask = m; } void NES_DMC_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) { NES_DMC* dmc = (NES_DMC*)chip; if (trk < 0) return; if (trk > 2) return; dmc->sm[0][trk] = mixl; dmc->sm[1][trk] = mixr; } static void FrameSequence(NES_DMC* dmc, int s) { //DEBUG_OUT("FrameSequence: %d\n",s); if (s > 3) return; // no operation in step 4 if (dmc->apu != NULL) { NES_APU_np_FrameSequence(dmc->apu, s); } if (s == 0 && (dmc->frame_sequence_steps == 4)) { dmc->frame_irq = true; } // 240hz clock { bool divider = false; // triangle linear counter if (dmc->linear_counter_halt) { dmc->linear_counter = dmc->linear_counter_reload; } else { if (dmc->linear_counter > 0) --dmc->linear_counter; } if (!dmc->linear_counter_control) { dmc->linear_counter_halt = false; } // noise envelope //bool divider = false; if (dmc->envelope_write) { dmc->envelope_write = false; dmc->envelope_counter = 15; dmc->envelope_div = 0; } else { ++dmc->envelope_div; if (dmc->envelope_div > dmc->envelope_div_period) { divider = true; dmc->envelope_div = 0; } } if (divider) { if (dmc->envelope_loop && dmc->envelope_counter == 0) dmc->envelope_counter = 15; else if (dmc->envelope_counter > 0) --dmc->envelope_counter; // TODO: Make this work. } } // 120hz clock if ((s&1) == 0) { // triangle length counter if (!dmc->linear_counter_control && (dmc->length_counter[0] > 0)) --dmc->length_counter[0]; // noise length counter if (!dmc->envelope_loop && (dmc->length_counter[1] > 0)) --dmc->length_counter[1]; } } // 三角波チャンネルの計算 戻り値は0-15 UINT32 calc_tri(NES_DMC* dmc, UINT32 clocks) { static UINT32 tritbl[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; if (dmc->linear_counter > 0 && dmc->length_counter[0] > 0 && (!dmc->option[OPT_TRI_MUTE] || dmc->tri_freq > 0)) { dmc->counter[0] += clocks; while (dmc->counter[0] > dmc->tri_freq) { dmc->tphase = (dmc->tphase + 1) & 31; dmc->counter[0] -= (dmc->tri_freq + 1); } } // Note: else-block added by VB else if (dmc->option[OPT_TRI_NULL]) { if (dmc->tphase && dmc->tphase < 31) { // Finish the Triangle wave to prevent clicks. dmc->counter[0] += clocks; while(dmc->counter[0] > dmc->tri_freq && dmc->tphase) { dmc->tphase = (dmc->tphase + 1) & 31; dmc->counter[0] -= (dmc->tri_freq + 1); } } } //UINT32 ret = tritbl[tphase]; //return ret; return tritbl[dmc->tphase]; } // ノイズチャンネルの計算 戻り値は0-127 // 低サンプリングレートで合成するとエイリアスノイズが激しいので // ノイズだけはこの関数内で高クロック合成し、簡易なサンプリングレート // 変換を行っている。 UINT32 calc_noise(NES_DMC* dmc, UINT32 clocks) { UINT32 env, last, count, accum, clocks_accum; env = dmc->envelope_disable ? dmc->noise_volume : dmc->envelope_counter; if (dmc->length_counter[1] < 1) env = 0; last = (dmc->noise & 0x4000) ? env : 0; if (clocks < 1) return last; // simple anti-aliasing (noise requires it, even when oversampling is off) count = 0; accum = 0; dmc->counter[1] += clocks; // assert(dmc->nfreq > 0); // prevent infinite loop if (dmc->nfreq <= 0) // prevent infinite loop -VB return last; while (dmc->counter[1] >= dmc->nfreq) { // tick the noise generator UINT32 feedback = (dmc->noise&1) ^ ((dmc->noise&dmc->noise_tap)?1:0); dmc->noise = (dmc->noise>>1) | (feedback<<14); ++count; accum += last; last = (dmc->noise & 0x4000) ? env : 0; dmc->counter[1] -= dmc->nfreq; } if (count < 1) // no change over interval, don't anti-alias { return last; } clocks_accum = clocks - dmc->counter[1]; // count = number of samples in accum // counter[1] = number of clocks since last sample accum = (accum * clocks_accum) + (last * dmc->counter[1] * count); // note accum as an average is already premultiplied by count return accum / (clocks * count); } // DMCチャンネルの計算 戻り値は0-127 UINT32 calc_dmc(NES_DMC* dmc, UINT32 clocks) { dmc->counter[2] += clocks; // assert(dmc->dfreq > 0); // prevent infinite loop if (dmc->dfreq <= 0) // prevent infinite loop -VB return (dmc->damp<<1) + dmc->dac_lsb; while (dmc->counter[2] >= dmc->dfreq) { if ( dmc->data != 0x100 ) // data = 0x100 は EMPTY を意味する。 { if ((dmc->data & 1) && (dmc->damp < 63)) dmc->damp++; else if (!(dmc->data & 1) && (0 < dmc->damp)) dmc->damp--; dmc->data >>=1; } if ( dmc->data == 0x100 && dmc->active ) { //dmc->memory->Read(dmc->daddress, dmc->data); dmc->data = dmc->memory[dmc->daddress]; dmc->data |= (dmc->data&0xFF)|0x10000; // 8bitシフトで 0x100 になる if ( dmc->length > 0 ) { dmc->daddress = ((dmc->daddress+1)&0xFFFF)|0x8000 ; dmc->length --; } } if ( dmc->length == 0 ) // 最後のフェッチが終了したら(再生完了より前に)即座に終端処理 { if (dmc->mode & 1) { dmc->daddress = ((dmc->adr_reg<<6)|0xC000); dmc->length = (dmc->len_reg<<4)+1; } else { dmc->irq = (dmc->mode==2&&dmc->active)?1:0; // 直前がactiveだったときはIRQ発行 dmc->active = false; } } dmc->counter[2] -= dmc->dfreq; } return (dmc->damp<<1) + dmc->dac_lsb; } static void TickFrameSequence(NES_DMC* dmc, UINT32 clocks) { dmc->frame_sequence_count += clocks; while (dmc->frame_sequence_count > dmc->frame_sequence_length) { FrameSequence(dmc, dmc->frame_sequence_step); dmc->frame_sequence_count -= dmc->frame_sequence_length; ++dmc->frame_sequence_step; if(dmc->frame_sequence_step >= dmc->frame_sequence_steps) dmc->frame_sequence_step = 0; } } static void Tick(NES_DMC* dmc, UINT32 clocks) { dmc->out[0] = calc_tri(dmc, clocks); dmc->out[1] = calc_noise(dmc, clocks); dmc->out[2] = calc_dmc(dmc, clocks); } UINT32 NES_DMC_np_Render(void* chip, INT32 b[2]) { NES_DMC* dmc = (NES_DMC*)chip; UINT32 clocks; INT32 m[3]; COUNTER_iup(dmc->tick_count); // increase counter (overflows after 255) clocks = (COUNTER_value(dmc->tick_count) - dmc->tick_last) & 0xFF; TickFrameSequence(dmc, clocks); Tick(dmc, clocks); dmc->tick_last = COUNTER_value(dmc->tick_count); dmc->out[0] = (dmc->mask & 1) ? 0 : dmc->out[0]; dmc->out[1] = (dmc->mask & 2) ? 0 : dmc->out[1]; dmc->out[2] = (dmc->mask & 4) ? 0 : dmc->out[2]; m[0] = dmc->tnd_table[0][dmc->out[0]][0][0]; m[1] = dmc->tnd_table[0][0][dmc->out[1]][0]; m[2] = dmc->tnd_table[0][0][0][dmc->out[2]]; if (dmc->option[OPT_NONLINEAR_MIXER]) { INT32 ref = m[0] + m[1] + m[2]; INT32 voltage = dmc->tnd_table[1][dmc->out[0]][dmc->out[1]][dmc->out[2]]; int i; if (ref) { for (i=0; i < 3; ++i) m[i] = (m[i] * voltage) / ref; } else { for (i=0; i < 3; ++i) m[i] = voltage; } } // anti-click nullifies any 4011 write but preserves nonlinearity if (dmc->option[OPT_DPCM_ANTI_CLICK]) { if (dmc->dmc_pop) // $4011 will cause pop this frame { // adjust offset to counteract pop dmc->dmc_pop_offset += dmc->dmc_pop_follow - m[2]; dmc->dmc_pop = false; // prevent overflow, keep headspace at edges //const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); #define OFFSET_MAX ((1 << 30) - (4 << 16)) if (dmc->dmc_pop_offset > OFFSET_MAX) dmc->dmc_pop_offset = OFFSET_MAX; if (dmc->dmc_pop_offset < -OFFSET_MAX) dmc->dmc_pop_offset = -OFFSET_MAX; } dmc->dmc_pop_follow = m[2]; // remember previous position m[2] += dmc->dmc_pop_offset; // apply offset // TODO implement this in a better way // roll off offset (not ideal, but prevents overflow) if (dmc->dmc_pop_offset > 0) --dmc->dmc_pop_offset; else if (dmc->dmc_pop_offset < 0) ++dmc->dmc_pop_offset; } b[0] = m[0] * dmc->sm[0][0]; b[0] += m[1] * dmc->sm[0][1]; b[0] +=-m[2] * dmc->sm[0][2]; b[0] >>= 7-2; b[1] = m[0] * dmc->sm[1][0]; b[1] += m[1] * dmc->sm[1][1]; b[1] +=-m[2] * dmc->sm[1][2]; b[1] >>= 7-2; return 2; } void NES_DMC_np_SetClock(void* chip, double c) { NES_DMC* dmc = (NES_DMC*)chip; dmc->clock = (UINT32)(c); if (abs(dmc->clock - DEFAULT_CLK_PAL) <= 1000) // check for approximately DEFAULT_CLK_PAL NES_DMC_np_SetPal(dmc, true); else NES_DMC_np_SetPal(dmc, false); } void NES_DMC_np_SetRate(void* chip, double r) { NES_DMC* dmc = (NES_DMC*)chip; dmc->rate = (UINT32)(r?r:DEFAULT_RATE); COUNTER_init(dmc->tick_count, dmc->clock, dmc->rate); dmc->tick_last = 0; } void NES_DMC_np_SetPal(void* chip, bool is_pal) { NES_DMC* dmc = (NES_DMC*)chip; dmc->pal = (is_pal ? 1 : 0); // set CPU cycles in frame_sequence dmc->frame_sequence_length = is_pal ? 8314 : 7458; } void NES_DMC_np_SetAPU(void* chip, void* apu_) { NES_DMC* dmc = (NES_DMC*)chip; dmc->apu = apu_; } // Initializing TRI, NOISE, DPCM mixing table static void InitializeTNDTable(NES_DMC* dmc, double wt, double wn, double wd) { // volume adjusted by 0.75 based on empirical measurements const double MASTER = 8192.0 * 0.75; // truthfully, the nonlinear curve does not appear to match well // with my tests, triangle in particular seems too quiet relatively. // do more testing of the APU/DMC DAC later int t, n, d; { // Linear Mixer for(t=0; t<16 ; t++) { for(n=0; n<16; n++) { for(d=0; d<128; d++) { dmc->tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); } } } } { // Non-Linear Mixer dmc->tnd_table[1][0][0][0] = 0; for(t=0; t<16 ; t++) { for(n=0; n<16; n++) { for(d=0; d<128; d++) { if(t!=0||n!=0||d!=0) dmc->tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); } } } } } void NES_DMC_np_Reset(void* chip) { NES_DMC* dmc = (NES_DMC*)chip; int i; dmc->mask = 0; InitializeTNDTable(dmc,8227,12241,22638); dmc->counter[0] = 0; dmc->counter[1] = 0; dmc->counter[2] = 0; dmc->tphase = 0; dmc->nfreq = wavlen_table[0][0]; dmc->dfreq = freq_table[0][0]; dmc->envelope_div = 0; dmc->length_counter[0] = 0; dmc->length_counter[1] = 0; dmc->linear_counter = 0; dmc->envelope_counter = 0; dmc->frame_irq = false; dmc->frame_irq_enable = false; dmc->frame_sequence_count = 0; dmc->frame_sequence_steps = 4; dmc->frame_sequence_step = 0; for (i = 0; i < 0x10; i++) NES_DMC_np_Write(dmc, 0x4008 + i, 0); dmc->irq = false; NES_DMC_np_Write(dmc, 0x4015, 0x00); if (dmc->option[OPT_UNMUTE_ON_RESET]) NES_DMC_np_Write(dmc, 0x4015, 0x0f); dmc->out[0] = dmc->out[1] = dmc->out[2] = 0; dmc->tri_freq = 0; dmc->damp = 0; dmc->dmc_pop = false; dmc->dmc_pop_offset = 0; dmc->dmc_pop_follow = 0; dmc->dac_lsb = 0; dmc->data = 0x100; dmc->adr_reg = 0; dmc->active = false; dmc->length = 0; dmc->len_reg = 0; dmc->daddress = 0; dmc->noise = 1; dmc->noise_tap = (1<<1); if (dmc->option[OPT_RANDOMIZE_NOISE]) { dmc->noise |= rand(); } NES_DMC_np_SetRate(dmc, dmc->rate); } void NES_DMC_np_SetMemory(void* chip, const UINT8* r) { NES_DMC* dmc = (NES_DMC*)chip; dmc->memory = r; } void NES_DMC_np_SetOption(void* chip, int id, int val) { NES_DMC* dmc = (NES_DMC*)chip; if(idoption[id] = val; if(id==OPT_NONLINEAR_MIXER) InitializeTNDTable(dmc, 8227,12241,22638); } } bool NES_DMC_np_Write(void* chip, UINT32 adr, UINT32 val) { static const UINT8 length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E }; NES_DMC* dmc = (NES_DMC*)chip; if (adr == 0x4015) { dmc->enable[0] = (val & 4) ? true : false; dmc->enable[1] = (val & 8) ? true : false; if (!dmc->enable[0]) { dmc->length_counter[0] = 0; } if (!dmc->enable[1]) { dmc->length_counter[1] = 0; } if ((val & 16)&&!dmc->active) { dmc->enable[2] = dmc->active = true; dmc->daddress = (0xC000 | (dmc->adr_reg << 6)); dmc->length = (dmc->len_reg << 4) + 1; dmc->irq = 0; } else if (!(val & 16)) { dmc->enable[2] = dmc->active = false; } dmc->reg[adr-0x4008] = val; return true; } if (adr == 0x4017) { //DEBUG_OUT("4017 = %02X\n", val); dmc->frame_irq_enable = ((val & 0x40) == 0x40); dmc->frame_irq = (dmc->frame_irq_enable ? dmc->frame_irq : 0); dmc->frame_sequence_count = 0; if (val & 0x80) { dmc->frame_sequence_steps = 5; dmc->frame_sequence_step = 0; FrameSequence(dmc, dmc->frame_sequence_step); ++dmc->frame_sequence_step; } else { dmc->frame_sequence_steps = 4; dmc->frame_sequence_step = 1; } } if (adr<0x4008||0x4013reg[adr-0x4008] = val&0xff; //DEBUG_OUT("$%04X %02X\n", adr, val); switch (adr) { // tri case 0x4008: dmc->linear_counter_control = (val >> 7) & 1; dmc->linear_counter_reload = val & 0x7F; break; case 0x4009: break; case 0x400a: dmc->tri_freq = val | (dmc->tri_freq & 0x700) ; if (dmc->counter[0] > dmc->tri_freq) dmc->counter[0] = dmc->tri_freq; break; case 0x400b: dmc->tri_freq = (dmc->tri_freq & 0xff) | ((val & 0x7) << 8) ; if (dmc->counter[0] > dmc->tri_freq) dmc->counter[0] = dmc->tri_freq; dmc->linear_counter_halt = true; if (dmc->enable[0]) { dmc->length_counter[0] = length_table[(val >> 3) & 0x1f]; } break; // noise case 0x400c: dmc->noise_volume = val & 15; dmc->envelope_div_period = val & 15; dmc->envelope_disable = (val >> 4) & 1; dmc->envelope_loop = (val >> 5) & 1; break; case 0x400d: break; case 0x400e: if (dmc->option[OPT_ENABLE_PNOISE]) dmc->noise_tap = (val & 0x80) ? (1<<6) : (1<<1); else dmc->noise_tap = (1<<1); dmc->nfreq = wavlen_table[dmc->pal][val&15]; if (dmc->counter[1] > dmc->nfreq) dmc->counter[1] = dmc->nfreq; break; case 0x400f: if (dmc->enable[1]) { dmc->length_counter[1] = length_table[(val >> 3) & 0x1f]; } dmc->envelope_write = true; break; // dmc case 0x4010: dmc->mode = (val >> 6) & 3; dmc->dfreq = freq_table[dmc->pal][val&15]; if (dmc->counter[2] > dmc->dfreq) dmc->counter[2] = dmc->dfreq; break; case 0x4011: if (dmc->option[OPT_ENABLE_4011]) { dmc->damp = (val >> 1) & 0x3f; dmc->dac_lsb = val & 1; dmc->dmc_pop = true; } break; case 0x4012: dmc->adr_reg = val&0xff; // ここでdaddressは更新されない break; case 0x4013: dmc->len_reg = val&0xff; // ここでlengthは更新されない break; default: return false; } return true; } bool NES_DMC_np_Read(void* chip, UINT32 adr, UINT32* val) { NES_DMC* dmc = (NES_DMC*)chip; if (adr == 0x4015) { *val |= (dmc->irq?128:0) | (dmc->frame_irq ? 0x40 : 0) | (dmc->active?16:0) | (dmc->length_counter[1]?8:0) | (dmc->length_counter[0]?4:0) ; dmc->frame_irq = false; return true; } else if (0x4008<=adr&&adr<=0x4014) { *val |= dmc->reg[adr-0x4008]; return true; } else return false; } ================================================ FILE: VGMPlay/chips/np_nes_dmc.h ================================================ #ifndef _NP_NES_DMC_H_ #define _NP_NES_DMC_H_ void* NES_DMC_np_Create(int clock, int rate); void NES_DMC_np_Destroy(void *chip); void NES_DMC_np_Reset(void *chip); void NES_DMC_np_SetRate(void* chip, double rate); void NES_DMC_np_SetPal(void* chip, bool is_pal); void NES_DMC_np_SetAPU(void* chip, void* apu_); UINT32 NES_DMC_np_Render(void* chip, INT32 b[2]); void NES_DMC_np_SetMemory(void* chip, const UINT8* r); bool NES_DMC_np_Write(void* chip, UINT32 adr, UINT32 val); bool NES_DMC_np_Read(void* chip, UINT32 adr, UINT32* val); void NES_DMC_np_SetClock(void* chip, double rate); void NES_DMC_np_SetOption(void* chip, int id, int val); int NES_DMC_np_GetDamp(void* chip); void NES_DMC_np_SetMask(void* chip, int m); void NES_DMC_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); #endif // _NP_NES_DMC_H_ ================================================ FILE: VGMPlay/chips/np_nes_fds.c ================================================ // Ported from NSFPlay 2.3 to VGMPlay (including C++ -> C conversion) // by Valley Bell on 26 September 2013 #include // for rand, malloc #include // for memset #include // for NULL #include // for exp #include "mamedef.h" #include "../stdbool.h" #include "np_nes_fds.h" #define DEFAULT_CLOCK 1789772.0 #define DEFAULT_RATE 44100 enum { OPT_CUTOFF=0, OPT_4085_RESET, OPT_WRITE_PROTECT, OPT_END }; enum { EMOD=0, EVOL=1 }; //const int RC_BITS = 12; #define RC_BITS 12 enum { TMOD=0, TWAV=1 }; // 8 bit approximation of master volume #define MASTER_VOL (2.4 * 1223.0) // max FDS vol vs max APU square (arbitrarily 1223) #define MAX_OUT (32.0 * 63.0) // value that should map to master vol static const INT32 MASTER[4] = { (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; // Although they were pretty much removed from any sound core in NSFPlay 2.3, // I find this counter structure very useful. #define COUNTER_SHIFT 24 typedef struct _Counter Counter; struct _Counter { double ratio; UINT32 val, step; }; #define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) #define COUNTER_iup(cntr) (cntr).val += (cntr).step #define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) #define COUNTER_init(cntr, clk, rate) \ { \ (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ (cntr).step = (UINT32)((cntr).ratio + 0.5); \ (cntr).val = 0; \ } typedef struct _NES_FDS NES_FDS; struct _NES_FDS { double rate, clock; int mask; INT32 sm[2]; // stereo mix INT32 fout; // current output int option[OPT_END]; bool master_io; UINT8 master_vol; UINT32 last_freq; // for trackinfo UINT32 last_vol; // for trackinfo // two wavetables //const enum { TMOD=0, TWAV=1 }; INT32 wave[2][64]; UINT32 freq[2]; UINT32 phase[2]; bool wav_write; bool wav_halt; bool env_halt; bool mod_halt; UINT32 mod_pos; UINT32 mod_write_pos; // two ramp envelopes //const enum { EMOD=0, EVOL=1 }; bool env_mode[2]; bool env_disable[2]; UINT32 env_timer[2]; UINT32 env_speed[2]; UINT32 env_out[2]; UINT32 master_env_speed; // 1-pole RC lowpass filter INT32 rc_accum; INT32 rc_k; INT32 rc_l; Counter tick_count; UINT32 tick_last; }; void* NES_FDS_Create(int clock, int rate) { NES_FDS* fds; fds = (NES_FDS*)malloc(sizeof(NES_FDS)); if (fds == NULL) return NULL; memset(fds, 0x00, sizeof(NES_FDS)); fds->option[OPT_CUTOFF] = 2000; fds->option[OPT_4085_RESET] = 0; fds->option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp fds->rc_k = 0; fds->rc_l = (1<sm[0] = 128; fds->sm[1] = 128; NES_FDS_Reset(fds); return fds; } void NES_FDS_Destroy(void* chip) { free(chip); } void NES_FDS_SetMask(void* chip, int m) { NES_FDS* fds = (NES_FDS*)chip; fds->mask = m&1; } void NES_FDS_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) { NES_FDS* fds = (NES_FDS*)chip; if (trk < 0) return; if (trk > 1) return; fds->sm[0] = mixl; fds->sm[1] = mixr; } void NES_FDS_SetClock(void* chip, double c) { NES_FDS* fds = (NES_FDS*)chip; fds->clock = c; } void NES_FDS_SetRate(void* chip, double r) { NES_FDS* fds = (NES_FDS*)chip; double cutoff, leak; fds->rate = r; COUNTER_init(fds->tick_count, fds->clock, fds->rate); fds->tick_last = 0; // configure lowpass filter cutoff = (double)fds->option[OPT_CUTOFF]; leak = 0.0; if (cutoff > 0) leak = exp(-2.0 * 3.14159 * cutoff / fds->rate); fds->rc_k = (INT32)(leak * (double)(1<rc_l = (1<rc_k; } void NES_FDS_SetOption(void* chip, int id, int val) { NES_FDS* fds = (NES_FDS*)chip; if(idoption[id] = val; // update cutoff immediately if (id == OPT_CUTOFF) NES_FDS_SetRate(fds, fds->rate); } void NES_FDS_Reset(void* chip) { NES_FDS* fds = (NES_FDS*)chip; int i; fds->master_io = true; fds->master_vol = 0; fds->last_freq = 0; fds->last_vol = 0; fds->rc_accum = 0; for (i=0; i<2; ++i) { memset(fds->wave[i], 0, sizeof(fds->wave[i])); fds->freq[i] = 0; fds->phase[i] = 0; } fds->wav_write = false; fds->wav_halt = true; fds->env_halt = true; fds->mod_halt = true; fds->mod_pos = 0; fds->mod_write_pos = 0; for (i=0; i<2; ++i) { fds->env_mode[i] = false; fds->env_disable[i] = true; fds->env_timer[i] = 0; fds->env_speed[i] = 0; fds->env_out[i] = 0; } fds->master_env_speed = 0xFF; // NOTE: the FDS BIOS reset only does the following related to audio: // $4023 = $00 // $4023 = $83 enables master_io // $4080 = $80 output volume = 0, envelope disabled // $408A = $FF master envelope speed set to slowest NES_FDS_Write(fds, 0x4023, 0x00); NES_FDS_Write(fds, 0x4023, 0x83); NES_FDS_Write(fds, 0x4080, 0x80); NES_FDS_Write(fds, 0x408A, 0xFF); // reset other stuff NES_FDS_Write(fds, 0x4082, 0x00); // wav freq 0 NES_FDS_Write(fds, 0x4083, 0x80); // wav disable NES_FDS_Write(fds, 0x4084, 0x80); // mod strength 0 NES_FDS_Write(fds, 0x4085, 0x00); // mod position 0 NES_FDS_Write(fds, 0x4086, 0x00); // mod freq 0 NES_FDS_Write(fds, 0x4087, 0x80); // mod disable NES_FDS_Write(fds, 0x4089, 0x00); // wav write disable, max global volume} } static void Tick(NES_FDS* fds, UINT32 clocks) { INT32 vol_out; // clock envelopes if (!fds->env_halt && !fds->wav_halt && (fds->master_env_speed != 0)) { int i; for (i=0; i<2; ++i) { if (!fds->env_disable[i]) { UINT32 period; fds->env_timer[i] += clocks; period = ((fds->env_speed[i]+1) * fds->master_env_speed) << 3; while (fds->env_timer[i] >= period) { // clock the envelope if (fds->env_mode[i]) { if (fds->env_out[i] < 32) ++fds->env_out[i]; } else { if (fds->env_out[i] > 0 ) --fds->env_out[i]; } fds->env_timer[i] -= period; } } } } // clock the mod table if (!fds->mod_halt) { UINT32 start_pos, end_pos, p; // advance phase, adjust for modulator start_pos = fds->phase[TMOD] >> 16; fds->phase[TMOD] += (clocks * fds->freq[TMOD]); end_pos = fds->phase[TMOD] >> 16; // wrap the phase to the 64-step table (+ 16 bit accumulator) fds->phase[TMOD] = fds->phase[TMOD] & 0x3FFFFF; // execute all clocked steps for (p = start_pos; p < end_pos; ++p) { INT32 wv = fds->wave[TMOD][p & 0x3F]; if (wv == 4) // 4 resets mod position fds->mod_pos = 0; else { static const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; fds->mod_pos += BIAS[wv]; fds->mod_pos &= 0x7F; // 7-bit clamp } } } // clock the wav table if (!fds->wav_halt) { INT32 mod, f; // complex mod calculation mod = 0; if (fds->env_out[EMOD] != 0) // skip if modulator off { // convert mod_pos to 7-bit signed INT32 pos = (fds->mod_pos < 64) ? fds->mod_pos : (fds->mod_pos-128); // multiply pos by gain, // shift off 4 bits but with odd "rounding" behaviour INT32 temp = pos * fds->env_out[EMOD]; INT32 rem = temp & 0x0F; temp >>= 4; if ((rem > 0) && ((temp & 0x80) == 0)) { if (pos < 0) temp -= 1; else temp += 2; } // wrap if range is exceeded while (temp >= 192) temp -= 256; while (temp < -64) temp += 256; // multiply result by pitch, // shift off 6 bits, round to nearest temp = fds->freq[TWAV] * temp; rem = temp & 0x3F; temp >>= 6; if (rem >= 32) temp += 1; mod = temp; } // advance wavetable position f = fds->freq[TWAV] + mod; fds->phase[TWAV] = fds->phase[TWAV] + (clocks * f); fds->phase[TWAV] = fds->phase[TWAV] & 0x3FFFFF; // wrap // store for trackinfo fds->last_freq = f; } // output volume caps at 32 vol_out = fds->env_out[EVOL]; if (vol_out > 32) vol_out = 32; // final output if (!fds->wav_write) fds->fout = fds->wave[TWAV][(fds->phase[TWAV]>>16)&0x3F] * vol_out; // NOTE: during wav_halt, the unit still outputs (at phase 0) // and volume can affect it if the first sample is nonzero. // haven't worked out 100% of the conditions for volume to // effect (vol envelope does not seem to run, but am unsure) // but this implementation is very close to correct // store for trackinfo fds->last_vol = vol_out; } UINT32 NES_FDS_Render(void* chip, INT32 b[2]) { NES_FDS* fds = (NES_FDS*)chip; /* // 8 bit approximation of master volume static const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) static const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol static const INT32 MASTER[4] = { (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };*/ UINT32 clocks; INT32 v, rc_out, m; COUNTER_iup(fds->tick_count); clocks = (COUNTER_value(fds->tick_count) - fds->tick_last) & 0xFF; Tick(fds, clocks); fds->tick_last = COUNTER_value(fds->tick_count); v = fds->fout * MASTER[fds->master_vol] >> 8; // lowpass RC filter rc_out = ((fds->rc_accum * fds->rc_k) + (v * fds->rc_l)) >> RC_BITS; fds->rc_accum = rc_out; v = rc_out; // output mix m = fds->mask ? 0 : v; b[0] = (m * fds->sm[0]) >> (7-2); b[1] = (m * fds->sm[1]) >> (7-2); return 2; } bool NES_FDS_Write(void* chip, UINT32 adr, UINT32 val) { NES_FDS* fds = (NES_FDS*)chip; // $4023 master I/O enable/disable if (adr == 0x4023) { fds->master_io = ((val & 2) != 0); return true; } if (!fds->master_io) return false; if (adr < 0x4040 || adr > 0x408A) return false; if (adr < 0x4080) // $4040-407F wave table write { if (fds->wav_write) fds->wave[TWAV][adr - 0x4040] = val & 0x3F; return true; } switch (adr & 0x00FF) { case 0x80: // $4080 volume envelope fds->env_disable[EVOL] = ((val & 0x80) != 0); fds->env_mode[EVOL] = ((val & 0x40) != 0); fds->env_timer[EVOL] = 0; fds->env_speed[EVOL] = val & 0x3F; if (fds->env_disable[EVOL]) fds->env_out[EVOL] = fds->env_speed[EVOL]; return true; case 0x81: // $4081 --- return false; case 0x82: // $4082 wave frequency low fds->freq[TWAV] = (fds->freq[TWAV] & 0xF00) | val; return true; case 0x83: // $4083 wave frequency high / enables fds->freq[TWAV] = (fds->freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8); fds->wav_halt = ((val & 0x80) != 0); fds->env_halt = ((val & 0x40) != 0); if (fds->wav_halt) fds->phase[TWAV] = 0; if (fds->env_halt) { fds->env_timer[EMOD] = 0; fds->env_timer[EVOL] = 0; } return true; case 0x84: // $4084 mod envelope fds->env_disable[EMOD] = ((val & 0x80) != 0); fds->env_mode[EMOD] = ((val & 0x40) != 0); fds->env_timer[EMOD] = 0; fds->env_speed[EMOD] = val & 0x3F; if (fds->env_disable[EMOD]) fds->env_out[EMOD] = fds->env_speed[EMOD]; return true; case 0x85: // $4085 mod position fds->mod_pos = val & 0x7F; // not hardware accurate., but prevents detune due to cycle inaccuracies // (notably in Bio Miracle Bokutte Upa) if (fds->option[OPT_4085_RESET]) fds->phase[TMOD] = fds->mod_write_pos << 16; return true; case 0x86: // $4086 mod frequency low fds->freq[TMOD] = (fds->freq[TMOD] & 0xF00) | val; return true; case 0x87: // $4087 mod frequency high / enable fds->freq[TMOD] = (fds->freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8); fds->mod_halt = ((val & 0x80) != 0); if (fds->mod_halt) fds->phase[TMOD] = fds->phase[TMOD] & 0x3F0000; // reset accumulator phase return true; case 0x88: // $4088 mod table write if (fds->mod_halt) { // writes to current playback position (there is no direct way to set phase) fds->wave[TMOD][(fds->phase[TMOD] >> 16) & 0x3F] = val & 0x7F; fds->phase[TMOD] = (fds->phase[TMOD] + 0x010000) & 0x3FFFFF; fds->wave[TMOD][(fds->phase[TMOD] >> 16) & 0x3F] = val & 0x7F; fds->phase[TMOD] = (fds->phase[TMOD] + 0x010000) & 0x3FFFFF; fds->mod_write_pos = fds->phase[TMOD] >> 16; // used by OPT_4085_RESET } return true; case 0x89: // $4089 wave write enable, master volume fds->wav_write = ((val & 0x80) != 0); fds->master_vol = val & 0x03; return true; case 0x8A: // $408A envelope speed fds->master_env_speed = val; // haven't tested whether this register resets phase on hardware, // but this ensures my inplementation won't spam envelope clocks // if this value suddenly goes low. fds->env_timer[EMOD] = 0; fds->env_timer[EVOL] = 0; return true; default: return false; } return false; } bool NES_FDS_Read(void* chip, UINT32 adr, UINT32* val) { NES_FDS* fds = (NES_FDS*)chip; if (adr >= 0x4040 && adr < 0x407F) { // TODO: if wav_write is not enabled, the // read address may not be reliable? need // to test this on hardware. *val = fds->wave[TWAV][adr - 0x4040]; return true; } if (adr == 0x4090) // $4090 read volume envelope { *val = fds->env_out[EVOL] | 0x40; return true; } if (adr == 0x4092) // $4092 read mod envelope { *val = fds->env_out[EMOD] | 0x40; return true; } return false; } ================================================ FILE: VGMPlay/chips/np_nes_fds.h ================================================ #ifndef _NP_NES_FDS_H_ #define _NP_NES_FDS_H_ void* NES_FDS_Create(int clock, int rate); void NES_FDS_Destroy(void* chip); void NES_FDS_Reset(void* chip); UINT32 NES_FDS_Render(void* chip, INT32 b[2]); bool NES_FDS_Write(void* chip, UINT32 adr, UINT32 val); bool NES_FDS_Read(void* chip, UINT32 adr, UINT32* val); void NES_FDS_SetRate(void* chip, double r); void NES_FDS_SetClock(void* chip, double c); void NES_FDS_SetOption(void* chip, int id, int val); void NES_FDS_SetMask(void* chip, int m); void NES_FDS_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); #endif // _NP_NES_FDS_H_ ================================================ FILE: VGMPlay/chips/okim6258.c ================================================ /********************************************************************************************** * * OKI MSM6258 ADPCM * * TODO: * 3-bit ADPCM support * Recording? * **********************************************************************************************/ //#include "emu.h" #include // for NULL #include "mamedef.h" #ifdef _DEBUG #include #endif //#include "streams.h" #include #include "okim6258.h" #define COMMAND_STOP (1 << 0) #define COMMAND_PLAY (1 << 1) #define COMMAND_RECORD (1 << 2) #define STATUS_PLAYING (1 << 1) #define STATUS_RECORDING (1 << 2) static const int dividers[4] = { 1024, 768, 512, 512 }; #define QUEUE_SIZE (1 << 1) #define QUEUE_MASK (QUEUE_SIZE - 1) typedef struct _okim6258_state okim6258_state; struct _okim6258_state { UINT8 status; UINT32 master_clock; /* master clock frequency */ UINT32 divider; /* master clock divider */ UINT8 adpcm_type; /* 3/4 bit ADPCM select */ UINT8 data_in; /* ADPCM data-in register */ UINT8 nibble_shift; /* nibble select */ //sound_stream *stream; /* which stream are we playing on? */ UINT8 output_bits; INT32 output_mask; // Valley Bell: Added a small queue to prevent race conditions. UINT8 data_buf[8]; UINT8 data_in_last; UINT8 data_buf_pos; // Data Empty Values: // 00 - data written, but not read yet // 01 - read data, waiting for next write // 02 - tried to read, but had no data UINT8 data_empty; // Valley Bell: Added pan UINT8 pan; INT32 last_smpl; INT32 signal; INT32 step; UINT8 clock_buffer[0x04]; UINT32 initial_clock; UINT8 initial_div; SRATE_CALLBACK SmpRateFunc; void* SmpRateData; }; /* step size index shift table */ static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; /* lookup table for the precomputed difference */ static int diff_lookup[49*16]; /* tables computed? */ static int tables_computed = 0; #define MAX_CHIPS 0x02 static okim6258_state OKIM6258Data[MAX_CHIPS]; static UINT8 Iternal10Bit = 0x00; /*INLINE okim6258_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == OKIM6258); return (okim6258_state *)downcast(device)->token(); }*/ /********************************************************************************************** compute_tables -- compute the difference tables ***********************************************************************************************/ static void compute_tables(void) { /* nibble to bit map */ static const int nbl2bit[16][4] = { { 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}, {-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} }; int step, nib; if (tables_computed) return; /* loop over all possible steps */ for (step = 0; step <= 48; step++) { /* compute the step value */ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step)); /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { diff_lookup[step*16 + nib] = nbl2bit[nib][0] * (stepval * nbl2bit[nib][1] + stepval/2 * nbl2bit[nib][2] + stepval/4 * nbl2bit[nib][3] + stepval/8); } } tables_computed = 1; } static INT16 clock_adpcm(okim6258_state *chip, UINT8 nibble) { INT32 max = chip->output_mask - 1; INT32 min = -chip->output_mask; // original MAME algorithm (causes a DC offset over time) //chip->signal += diff_lookup[chip->step * 16 + (nibble & 15)]; // awesome algorithm ported from XM6 - it works PERFECTLY int sample = diff_lookup[chip->step * 16 + (nibble & 15)]; chip->signal = ((sample << 8) + (chip->signal * 245)) >> 8; /* clamp to the maximum */ if (chip->signal > max) chip->signal = max; else if (chip->signal < min) chip->signal = min; /* adjust the step size and clamp */ chip->step += index_shift[nibble & 7]; if (chip->step > 48) chip->step = 48; else if (chip->step < 0) chip->step = 0; /* return the signal scaled up to 32767 */ return chip->signal << 4; } /********************************************************************************************** okim6258_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( okim6258_update ) void okim6258_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //okim6258_state *chip = (okim6258_state *)param; okim6258_state *chip = &OKIM6258Data[ChipID]; //stream_sample_t *buffer = outputs[0]; stream_sample_t *bufL = outputs[0]; stream_sample_t *bufR = outputs[1]; //memset(outputs[0], 0, samples * sizeof(*outputs[0])); if (chip->status & STATUS_PLAYING) { int nibble_shift = chip->nibble_shift; while (samples) { /* Compute the new amplitude and update the current step */ //int nibble = (chip->data_in >> nibble_shift) & 0xf; int nibble; INT16 sample; if (! nibble_shift) { // 1st nibble - get data if (! chip->data_empty) { chip->data_in = chip->data_buf[chip->data_buf_pos >> 4]; chip->data_buf_pos += 0x10; chip->data_buf_pos &= 0x7F; if ((chip->data_buf_pos >> 4) == (chip->data_buf_pos & 0x0F)) chip->data_empty ++; } else { //chip->data_in = chip->data_in_last; if (chip->data_empty < 0x80) chip->data_empty ++; } } nibble = (chip->data_in >> nibble_shift) & 0xf; /* Output to the buffer */ //INT16 sample = clock_adpcm(chip, nibble); if (chip->data_empty < 0x02) { sample = clock_adpcm(chip, nibble); chip->last_smpl = sample; } else { // Valley Bell: data_empty behaviour (loosely) ported from XM6 if (chip->data_empty >= 0x02 + 0x01) { chip->data_empty -= 0x01; /*if (chip->signal < 0) chip->signal ++; else if (chip->signal > 0) chip->signal --;*/ chip->signal = chip->signal * 15 / 16; chip->last_smpl = chip->signal << 4; } sample = chip->last_smpl; } nibble_shift ^= 4; //*buffer++ = sample; *bufL++ = (chip->pan & 0x02) ? 0x00 : sample; *bufR++ = (chip->pan & 0x01) ? 0x00 : sample; samples--; } /* Update the parameters */ chip->nibble_shift = nibble_shift; } else { /* Fill with 0 */ while (samples--) { //*buffer++ = 0; *bufL++ = 0; *bufR++ = 0; } } } /********************************************************************************************** state save support for MAME ***********************************************************************************************/ /*static void okim6258_state_save_register(okim6258_state *info, running_device *device) { state_save_register_device_item(device, 0, info->status); state_save_register_device_item(device, 0, info->master_clock); state_save_register_device_item(device, 0, info->divider); state_save_register_device_item(device, 0, info->data_in); state_save_register_device_item(device, 0, info->nibble_shift); state_save_register_device_item(device, 0, info->signal); state_save_register_device_item(device, 0, info->step); }*/ /********************************************************************************************** OKIM6258_start -- start emulation of an OKIM6258-compatible chip ***********************************************************************************************/ static int get_vclk(okim6258_state* info) { int clk_rnd; clk_rnd = info->master_clock; clk_rnd += info->divider / 2; // for better rounding - should help some of the streams return clk_rnd / info->divider; } //static DEVICE_START( okim6258 ) int device_start_okim6258(UINT8 ChipID, int clock, int divider, int adpcm_type, int output_12bits) { //const okim6258_interface *intf = (const okim6258_interface *)device->baseconfig().static_config(); //okim6258_state *info = get_safe_token(device); okim6258_state *info; if (ChipID >= MAX_CHIPS) return 0; info = &OKIM6258Data[ChipID]; compute_tables(); //info->master_clock = device->clock(); info->initial_clock = clock; info->initial_div = divider; info->master_clock = clock; info->adpcm_type = /*intf->*/adpcm_type; info->clock_buffer[0x00] = (clock & 0x000000FF) >> 0; info->clock_buffer[0x01] = (clock & 0x0000FF00) >> 8; info->clock_buffer[0x02] = (clock & 0x00FF0000) >> 16; info->clock_buffer[0x03] = (clock & 0xFF000000) >> 24; info->SmpRateFunc = NULL; /* D/A precision is 10-bits but 12-bit data can be output serially to an external DAC */ info->output_bits = /*intf->*/output_12bits ? 12 : 10; if (Iternal10Bit) info->output_mask = (1 << (info->output_bits - 1)); else info->output_mask = (1 << (12 - 1)); info->divider = dividers[/*intf->*/divider]; //info->stream = stream_create(device, 0, 1, device->clock()/info->divider, info, okim6258_update); info->signal = -2; info->step = 0; //okim6258_state_save_register(info, device); return get_vclk(info); } /********************************************************************************************** OKIM6258_stop -- stop emulation of an OKIM6258-compatible chip ***********************************************************************************************/ void device_stop_okim6258(UINT8 ChipID) { okim6258_state *info = &OKIM6258Data[ChipID]; return; } //static DEVICE_RESET( okim6258 ) void device_reset_okim6258(UINT8 ChipID) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; //stream_update(info->stream); info->master_clock = info->initial_clock; info->clock_buffer[0x00] = (info->initial_clock & 0x000000FF) >> 0; info->clock_buffer[0x01] = (info->initial_clock & 0x0000FF00) >> 8; info->clock_buffer[0x02] = (info->initial_clock & 0x00FF0000) >> 16; info->clock_buffer[0x03] = (info->initial_clock & 0xFF000000) >> 24; info->divider = dividers[info->initial_div]; if (info->SmpRateFunc != NULL) info->SmpRateFunc(info->SmpRateData, get_vclk(info)); info->signal = -2; info->step = 0; info->status = 0; // Valley Bell: Added reset of the Data In register. info->data_in = 0x00; info->data_buf[0] = info->data_buf[1] = 0x00; info->data_buf_pos = 0x00; info->data_empty = 0xFF; info->pan = 0x00; } /********************************************************************************************** okim6258_set_divider -- set the master clock divider ***********************************************************************************************/ //void okim6258_set_divider(running_device *device, int val) void okim6258_set_divider(UINT8 ChipID, int val) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; int divider = dividers[val]; info->divider = dividers[val]; //stream_set_sample_rate(info->stream, info->master_clock / divider); if (info->SmpRateFunc != NULL) info->SmpRateFunc(info->SmpRateData, get_vclk(info)); } /********************************************************************************************** okim6258_set_clock -- set the master clock ***********************************************************************************************/ //void okim6258_set_clock(running_device *device, int val) void okim6258_set_clock(UINT8 ChipID, int val) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; if (val) { info->master_clock = val; } else { info->master_clock = (info->clock_buffer[0x00] << 0) | (info->clock_buffer[0x01] << 8) | (info->clock_buffer[0x02] << 16) | (info->clock_buffer[0x03] << 24); } //stream_set_sample_rate(info->stream, info->master_clock / info->divider); if (info->SmpRateFunc != NULL) info->SmpRateFunc(info->SmpRateData, get_vclk(info)); } /********************************************************************************************** okim6258_get_vclk -- get the VCLK/sampling frequency ***********************************************************************************************/ //int okim6258_get_vclk(running_device *device) int okim6258_get_vclk(UINT8 ChipID) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; return get_vclk(info); } /********************************************************************************************** okim6258_status_r -- read the status port of an OKIM6258-compatible chip ***********************************************************************************************/ //READ8_DEVICE_HANDLER( okim6258_status_r ) /*UINT8 okim6258_status_r(UINT8 ChipID, offs_t offset) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; //stream_update(info->stream); return (info->status & STATUS_PLAYING) ? 0x00 : 0x80; }*/ /********************************************************************************************** okim6258_data_w -- write to the control port of an OKIM6258-compatible chip ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6258_data_w ) static void okim6258_data_w(UINT8 ChipID, /*offs_t offset, */UINT8 data) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; /* update the stream */ //stream_update(info->stream); //info->data_in = data; //info->nibble_shift = 0; if (info->data_empty >= 0x02) info->data_buf_pos = 0x00; info->data_in_last = data; info->data_buf[info->data_buf_pos & 0x0F] = data; info->data_buf_pos += 0x01; info->data_buf_pos &= 0xF7; if ((info->data_buf_pos >> 4) == (info->data_buf_pos & 0x0F)) { logerror("Warning: FIFO full!\n"); info->data_buf_pos = (info->data_buf_pos & 0xF0) | ((info->data_buf_pos-1) & 0x07); } info->data_empty = 0x00; } /********************************************************************************************** okim6258_ctrl_w -- write to the control port of an OKIM6258-compatible chip ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6258_ctrl_w ) static void okim6258_ctrl_w(UINT8 ChipID, /*offs_t offset, */UINT8 data) { //okim6258_state *info = get_safe_token(device); okim6258_state *info = &OKIM6258Data[ChipID]; //stream_update(info->stream); if (data & COMMAND_STOP) { info->status &= ~(STATUS_PLAYING | STATUS_RECORDING); return; } if (data & COMMAND_PLAY) { if (!(info->status & STATUS_PLAYING)) { info->status |= STATUS_PLAYING; /* Also reset the ADPCM parameters */ info->signal = -2; // Note: XM6 lets this fade to 0 when nothing is going on info->step = 0; info->nibble_shift = 0; info->data_buf[0x00] = data; info->data_buf_pos = 0x01; // write pos 01, read pos 00 info->data_empty = 0x00; } info->step = 0; // this line was verified with the source of XM6 info->nibble_shift = 0; } else { info->status &= ~STATUS_PLAYING; } if (data & COMMAND_RECORD) { logerror("M6258: Record enabled\n"); info->status |= STATUS_RECORDING; } else { info->status &= ~STATUS_RECORDING; } } static void okim6258_set_clock_byte(UINT8 ChipID, UINT8 Byte, UINT8 val) { okim6258_state *info = &OKIM6258Data[ChipID]; info->clock_buffer[Byte] = val; return; } static void okim6258_pan_w(UINT8 ChipID, UINT8 data) { okim6258_state *info = &OKIM6258Data[ChipID]; info->pan = data; return; } void okim6258_write(UINT8 ChipID, UINT8 Port, UINT8 Data) { switch(Port) { case 0x00: okim6258_ctrl_w(ChipID, /*0x00, */Data); break; case 0x01: okim6258_data_w(ChipID, /*0x00, */Data); break; case 0x02: okim6258_pan_w(ChipID, Data); break; case 0x08: case 0x09: case 0x0A: okim6258_set_clock_byte(ChipID, Port & 0x03, Data); break; case 0x0B: okim6258_set_clock_byte(ChipID, Port & 0x03, Data); okim6258_set_clock(ChipID, 0); break; case 0x0C: okim6258_set_divider(ChipID, Data); break; } return; } void okim6258_set_options(UINT16 Options) { Iternal10Bit = (Options >> 0) & 0x01; return; } void okim6258_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr) { okim6258_state *info = &OKIM6258Data[ChipID]; // set Sample Rate Change Callback routine info->SmpRateFunc = CallbackFunc; info->SmpRateData = DataPtr; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( okim6258 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6258_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(okim6258); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(okim6258); break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "OKI6258"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "OKI ADPCM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(OKIM6258, okim6258);*/ ================================================ FILE: VGMPlay/chips/okim6258.h ================================================ #pragma once //#include "devlegcy.h" /* an interface for the OKIM6258 and similar chips */ /*typedef struct _okim6258_interface okim6258_interface; struct _okim6258_interface { int divider; int adpcm_type; int output_12bits; };*/ #define FOSC_DIV_BY_1024 0 #define FOSC_DIV_BY_768 1 #define FOSC_DIV_BY_512 2 #define TYPE_3BITS 0 #define TYPE_4BITS 1 #define OUTPUT_10BITS 0 #define OUTPUT_12BITS 1 void okim6258_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_okim6258(UINT8 ChipID, int clock, int divider, int adpcm_type, int output_12bits); void device_stop_okim6258(UINT8 ChipID); void device_reset_okim6258(UINT8 ChipID); //void okim6258_set_divider(running_device *device, int val); //void okim6258_set_clock(running_device *device, int val); //int okim6258_get_vclk(running_device *device); void okim6258_set_divider(UINT8 ChipID, int val); void okim6258_set_clock(UINT8 ChipID, int val); int okim6258_get_vclk(UINT8 ChipID); //READ8_DEVICE_HANDLER( okim6258_status_r ); //WRITE8_DEVICE_HANDLER( okim6258_data_w ); //WRITE8_DEVICE_HANDLER( okim6258_ctrl_w ); /*UINT8 okim6258_status_r(UINT8 ChipID, offs_t offset); void okim6258_data_w(UINT8 ChipID, offs_t offset, UINT8 data); void okim6258_ctrl_w(UINT8 ChipID, offs_t offset, UINT8 data);*/ void okim6258_write(UINT8 ChipID, UINT8 Port, UINT8 Data); void okim6258_set_options(UINT16 Options); void okim6258_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr); //DECLARE_LEGACY_SOUND_DEVICE(OKIM6258, okim6258); ================================================ FILE: VGMPlay/chips/okim6295.c ================================================ /********************************************************************************************** * * streaming ADPCM driver * by Aaron Giles * * Library to transcode from an ADPCM source to raw PCM. * Written by Buffoni Mirko in 08/06/97 * References: various sources and documents. * * HJB 08/31/98 * modified to use an automatically selected oversampling factor * for the current sample rate * * Mish 21/7/99 * Updated to allow multiple OKI chips with different sample rates * * R. Belmont 31/10/2003 * Updated to allow a driver to use both MSM6295s and "raw" ADPCM voices (gcpinbal) * Also added some error trapping for MAME_DEBUG builds * **********************************************************************************************/ #include "mamedef.h" //#include "emu.h" //#include "streams.h" #include #include #include // for memset #include #include "okim6295.h" #define FALSE 0 #define TRUE 1 //#define MAX_SAMPLE_CHUNK 10000 #define MAX_SAMPLE_CHUNK 0x10 // that's enough for VGMPlay's update rate /* struct describing a single playing ADPCM voice */ struct ADPCMVoice { UINT8 playing; /* 1 if we are actively playing */ UINT32 base_offset; /* pointer to the base memory location */ UINT32 sample; /* current sample number */ UINT32 count; /* total samples to play */ struct adpcm_state adpcm;/* current ADPCM state */ UINT32 volume; /* output volume */ UINT8 Muted; }; typedef struct _okim6295_state okim6295_state; struct _okim6295_state { #define OKIM6295_VOICES 4 struct ADPCMVoice voice[OKIM6295_VOICES]; //running_device *device; INT16 command; //UINT8 bank_installed; INT32 bank_offs; UINT8 pin7_state; UINT8 nmk_mode; UINT8 nmk_bank[4]; //sound_stream *stream; /* which stream are we playing on? */ UINT32 master_clock; /* master clock frequency */ UINT32 initial_clock; UINT32 ROMSize; UINT8* ROM; SRATE_CALLBACK SmpRateFunc; void* SmpRateData; }; /* step size index shift table */ static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; /* lookup table for the precomputed difference */ static int diff_lookup[49*16]; /* volume lookup table. The manual lists only 9 steps, ~3dB per step. Given the dB values, that seems to map to a 5-bit volume control. Any volume parameter beyond the 9th index results in silent playback. */ static const int volume_table[16] = { 0x20, // 0 dB 0x16, // -3.2 dB 0x10, // -6.0 dB 0x0b, // -9.2 dB 0x08, // -12.0 dB 0x06, // -14.5 dB 0x04, // -18.0 dB 0x03, // -20.5 dB 0x02, // -24.0 dB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; /* tables computed? */ static int tables_computed = 0; /* useful interfaces */ //const okim6295_interface okim6295_interface_pin7high = { 1 }; //const okim6295_interface okim6295_interface_pin7low = { 0 }; /* default address map */ /*static ADDRESS_MAP_START( okim6295, 0, 8 ) AM_RANGE(0x00000, 0x3ffff) AM_ROM ADDRESS_MAP_END*/ #define MAX_CHIPS 0x02 static okim6295_state OKIM6295Data[MAX_CHIPS]; /*INLINE okim6295_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_OKIM6295); return (okim6295_state *)device->token; }*/ /********************************************************************************************** compute_tables -- compute the difference tables ***********************************************************************************************/ static void compute_tables(void) { /* nibble to bit map */ static const int nbl2bit[16][4] = { { 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}, {-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} }; int step, nib; /* loop over all possible steps */ for (step = 0; step <= 48; step++) { /* compute the step value */ int stepval = (int)floor(16.0 * pow(11.0 / 10.0, (double)step)); /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { diff_lookup[step*16 + nib] = nbl2bit[nib][0] * (stepval * nbl2bit[nib][1] + stepval/2 * nbl2bit[nib][2] + stepval/4 * nbl2bit[nib][3] + stepval/8); } } tables_computed = 1; } /********************************************************************************************** reset_adpcm -- reset the ADPCM stream ***********************************************************************************************/ void reset_adpcm(struct adpcm_state *state) { /* make sure we have our tables */ if (!tables_computed) compute_tables(); /* reset the signal/step */ state->signal = -2; state->step = 0; } /********************************************************************************************** clock_adpcm -- clock the next ADPCM byte ***********************************************************************************************/ INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble) { state->signal += diff_lookup[state->step * 16 + (nibble & 15)]; /* clamp to the maximum */ if (state->signal > 2047) state->signal = 2047; else if (state->signal < -2048) state->signal = -2048; /* adjust the step size and clamp */ state->step += index_shift[nibble & 7]; if (state->step > 48) state->step = 48; else if (state->step < 0) state->step = 0; /* return the signal */ return state->signal; } /********************************************************************************************** generate_adpcm -- general ADPCM decoding routine ***********************************************************************************************/ #define NMK_BNKTBLBITS 8 #define NMK_BNKTBLSIZE (1 << NMK_BNKTBLBITS) // 0x100 #define NMK_TABLESIZE (4 * NMK_BNKTBLSIZE) // 0x400 #define NMK_TABLEMASK (NMK_TABLESIZE - 1) // 0x3FF #define NMK_BANKBITS 16 #define NMK_BANKSIZE (1 << NMK_BANKBITS) // 0x10000 #define NMK_BANKMASK (NMK_BANKSIZE - 1) // 0xFFFF #define NMK_ROMBASE (4 * NMK_BANKSIZE) // 0x40000 static UINT8 memory_raw_read_byte(okim6295_state *chip, offs_t offset) { offs_t CurOfs; if (! chip->nmk_mode) { CurOfs = chip->bank_offs | offset; } else { UINT8 BankID; if (offset < NMK_TABLESIZE && (chip->nmk_mode & 0x80)) { // pages sample table BankID = offset >> NMK_BNKTBLBITS; CurOfs = offset & NMK_TABLEMASK; // 0x3FF, not 0xFF } else { BankID = offset >> NMK_BANKBITS; CurOfs = offset & NMK_BANKMASK; } CurOfs |= (chip->nmk_bank[BankID & 0x03] << NMK_BANKBITS); // I modified MAME to write a clean sample ROM. // (Usually it moves the data by NMK_ROMBASE.) //CurOfs += NMK_ROMBASE; } if (CurOfs < chip->ROMSize) return chip->ROM[CurOfs]; else return 0x00; } static void generate_adpcm(okim6295_state *chip, struct ADPCMVoice *voice, INT16 *buffer, int samples) { /* if this voice is active */ if (voice->playing) { offs_t base = voice->base_offset; int sample = voice->sample; int count = voice->count; /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ //int nibble = memory_raw_read_byte(chip->device->space(), base + sample / 2) >> (((sample & 1) << 2) ^ 4); UINT8 nibble = memory_raw_read_byte(chip, base + sample / 2) >> (((sample & 1) << 2) ^ 4); /* output to the buffer, scaling by the volume */ /* signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767 */ *buffer++ = clock_adpcm(&voice->adpcm, nibble) * voice->volume / 2; samples--; /* next! */ if (++sample >= count) { voice->playing = 0; break; } } /* update the parameters */ voice->sample = sample; } /* fill the rest with silence */ while (samples--) *buffer++ = 0; } /********************************************************************************************** * * OKIM 6295 ADPCM chip: * * Command bytes are sent: * * 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample number to trigger * abcd vvvv = second half of command; one of the abcd bits is set to indicate which voice * the v bits seem to be volumed * * 0abc d000 = stop playing; one or more of the abcd bits is set to indicate which voice(s) * * Status is read: * * ???? abcd = one bit per voice, set to 0 if nothing is playing, or 1 if it is active * ***********************************************************************************************/ /********************************************************************************************** okim6295_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( okim6295_update ) void okim6295_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //okim6295_state *chip = (okim6295_state *)param; okim6295_state *chip = &OKIM6295Data[ChipID]; int i; memset(outputs[0], 0, samples * sizeof(*outputs[0])); for (i = 0; i < OKIM6295_VOICES; i++) { struct ADPCMVoice *voice = &chip->voice[i]; if (! voice->Muted) { stream_sample_t *buffer = outputs[0]; INT16 sample_data[MAX_SAMPLE_CHUNK]; int remaining = samples; /* loop while we have samples remaining */ while (remaining) { int samples = (remaining > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : remaining; int samp; generate_adpcm(chip, voice, sample_data, samples); for (samp = 0; samp < samples; samp++) *buffer++ += sample_data[samp]; remaining -= samples; } } } memcpy(outputs[1], outputs[0], samples * sizeof(*outputs[0])); } /********************************************************************************************** state save support for MAME ***********************************************************************************************/ /*static void adpcm_state_save_register(struct ADPCMVoice *voice, running_device *device, int index) { state_save_register_device_item(device, index, voice->playing); state_save_register_device_item(device, index, voice->sample); state_save_register_device_item(device, index, voice->count); state_save_register_device_item(device, index, voice->adpcm.signal); state_save_register_device_item(device, index, voice->adpcm.step); state_save_register_device_item(device, index, voice->volume); state_save_register_device_item(device, index, voice->base_offset); } static STATE_POSTLOAD( okim6295_postload ) { running_device *device = (running_device *)param; okim6295_state *info = get_safe_token(device); okim6295_set_bank_base(device, info->bank_offs); } static void okim6295_state_save_register(okim6295_state *info, running_device *device) { int j; state_save_register_device_item(device, 0, info->command); state_save_register_device_item(device, 0, info->bank_offs); for (j = 0; j < OKIM6295_VOICES; j++) adpcm_state_save_register(&info->voice[j], device, j); state_save_register_postload(device->machine, okim6295_postload, (void *)device); }*/ /********************************************************************************************** DEVICE_START( okim6295 ) -- start emulation of an OKIM6295-compatible chip ***********************************************************************************************/ //static DEVICE_START( okim6295 ) int device_start_okim6295(UINT8 ChipID, int clock) { //const okim6295_interface *intf = (const okim6295_interface *)device->baseconfig().static_config; //okim6295_state *info = get_safe_token(device); okim6295_state *info; //int divisor = intf->pin7 ? 132 : 165; int divisor; //int voice; if (ChipID >= MAX_CHIPS) return 0; info = &OKIM6295Data[ChipID]; compute_tables(); info->command = -1; //info->bank_installed = FALSE; info->bank_offs = 0; info->nmk_mode = 0x00; memset(info->nmk_bank, 0x00, 4 * sizeof(UINT8)); //info->device = device; //info->master_clock = device->clock; info->initial_clock = clock; info->master_clock = clock & 0x7FFFFFFF; info->pin7_state = (clock & 0x80000000) >> 31; info->SmpRateFunc = NULL; /* generate the name and create the stream */ divisor = info->pin7_state ? 132 : 165; //info->stream = stream_create(device, 0, 1, device->clock/divisor, info, okim6295_update); // moved to device_reset /*// initialize the voices // for (voice = 0; voice < OKIM6295_VOICES; voice++) { // initialize the rest of the structure // info->voice[voice].volume = 0; reset_adpcm(&info->voice[voice].adpcm); }*/ //okim6295_state_save_register(info, device); return info->master_clock / divisor; } void device_stop_okim6295(UINT8 ChipID) { okim6295_state* chip = &OKIM6295Data[ChipID]; free(chip->ROM); chip->ROM = NULL; chip->ROMSize = 0x00; return; } /********************************************************************************************** DEVICE_RESET( okim6295 ) -- stop emulation of an OKIM6295-compatible chip ***********************************************************************************************/ //static DEVICE_RESET( okim6295 ) void device_reset_okim6295(UINT8 ChipID) { //okim6295_state *info = get_safe_token(device); okim6295_state *info = &OKIM6295Data[ChipID]; int voice; //stream_update(info->stream); info->command = -1; info->bank_offs = 0; info->nmk_mode = 0x00; memset(info->nmk_bank, 0x00, 4 * sizeof(UINT8)); info->master_clock = info->initial_clock & 0x7FFFFFFF; info->pin7_state = (info->initial_clock & 0x80000000) >> 31; for (voice = 0; voice < OKIM6295_VOICES; voice++) { info->voice[voice].volume = 0; reset_adpcm(&info->voice[voice].adpcm); info->voice[voice].playing = 0; } } /********************************************************************************************** okim6295_set_bank_base -- set the base of the bank for a given voice on a given chip ***********************************************************************************************/ //void okim6295_set_bank_base(running_device *device, int base) void okim6295_set_bank_base(okim6295_state *info, int base) { //okim6295_state *info = get_safe_token(device); //stream_update(info->stream); // if we are setting a non-zero base, and we have no bank, allocate one /*if (!info->bank_installed && base != 0) { // override our memory map with a bank //memory_install_read_bank(device->space(), 0x00000, 0x3ffff, 0, 0, device->tag()); info->bank_installed = TRUE; } // if we have a bank number, set the base pointer if (info->bank_installed) { info->bank_offs = base; //memory_set_bankptr(device->machine, device->tag(), device->region->base.u8 + base); }*/ info->bank_offs = base; } /********************************************************************************************** okim6295_set_pin7 -- adjust pin 7, which controls the internal clock division ***********************************************************************************************/ static void okim6295_clock_changed(okim6295_state *info) { int divisor; divisor = info->pin7_state ? 132 : 165; //stream_set_sample_rate(info->stream, info->master_clock/divisor); if (info->SmpRateFunc != NULL) info->SmpRateFunc(info->SmpRateData, info->master_clock / divisor); } //void okim6295_set_pin7(running_device *device, int pin7) INLINE void okim6295_set_pin7(okim6295_state *info, int pin7) { //okim6295_state *info = get_safe_token(device); info->pin7_state = pin7; okim6295_clock_changed(info); } /********************************************************************************************** okim6295_status_r -- read the status port of an OKIM6295-compatible chip ***********************************************************************************************/ //READ8_DEVICE_HANDLER( okim6295_r ) UINT8 okim6295_r(UINT8 ChipID, offs_t offset) { //okim6295_state *info = get_safe_token(device); okim6295_state *info = &OKIM6295Data[ChipID]; int i, result; result = 0xf0; /* naname expects bits 4-7 to be 1 */ /* set the bit to 1 if something is playing on a given channel */ //stream_update(info->stream); for (i = 0; i < OKIM6295_VOICES; i++) { struct ADPCMVoice *voice = &info->voice[i]; /* set the bit if it's playing */ if (voice->playing) result |= 1 << i; } return result; } /********************************************************************************************** okim6295_data_w -- write to the data port of an OKIM6295-compatible chip ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6295_w ) void okim6295_write_command(okim6295_state *info, UINT8 data) { //okim6295_state *info = get_safe_token(device); /* if a command is pending, process the second half */ if (info->command != -1) { int temp = data >> 4, i, start, stop; offs_t base; /* the manual explicitly says that it's not possible to start multiple voices at the same time */ if (temp != 0 && temp != 1 && temp != 2 && temp != 4 && temp != 8) fprintf(stderr, "OKI6295 start %x contact MAMEDEV\n", temp); /* update the stream */ //stream_update(info->stream); /* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte) */ for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1) { if (temp & 1) { struct ADPCMVoice *voice = &info->voice[i]; /* determine the start/stop positions */ base = info->command * 8; //start = memory_raw_read_byte(device->space(), base + 0) << 16; start = memory_raw_read_byte(info, base + 0) << 16; start |= memory_raw_read_byte(info, base + 1) << 8; start |= memory_raw_read_byte(info, base + 2) << 0; start &= 0x3ffff; stop = memory_raw_read_byte(info, base + 3) << 16; stop |= memory_raw_read_byte(info, base + 4) << 8; stop |= memory_raw_read_byte(info, base + 5) << 0; stop &= 0x3ffff; /* set up the voice to play this sample */ if (start < stop) { if (!voice->playing) /* fixes Got-cha and Steel Force */ { voice->playing = 1; voice->base_offset = start; voice->sample = 0; voice->count = 2 * (stop - start + 1); /* also reset the ADPCM parameters */ reset_adpcm(&voice->adpcm); voice->volume = volume_table[data & 0x0f]; } else { //logerror("OKIM6295:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command); // just displays warnings when seeking //logerror("OKIM6295: Voice %u requested to play sample %02x on non-stopped voice\n",i,info->command); } } /* invalid samples go here */ else { //logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",device->tag(),info->command); logerror("OKIM6295: Voice %u requested to play invalid sample %02x\n",i,info->command); voice->playing = 0; } } } /* reset the command */ info->command = -1; } /* if this is the start of a command, remember the sample number for next time */ else if (data & 0x80) { info->command = data & 0x7f; } /* otherwise, see if this is a silence command */ else { int temp = data >> 3, i; /* update the stream, then turn it off */ //stream_update(info->stream); /* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */ for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1) { if (temp & 1) { struct ADPCMVoice *voice = &info->voice[i]; voice->playing = 0; } } } } void okim6295_w(UINT8 ChipID, offs_t offset, UINT8 data) { okim6295_state* chip = &OKIM6295Data[ChipID]; switch(offset) { case 0x00: okim6295_write_command(chip, data); break; case 0x08: chip->master_clock &= ~0x000000FF; chip->master_clock |= data << 0; break; case 0x09: chip->master_clock &= ~0x0000FF00; chip->master_clock |= data << 8; break; case 0x0A: chip->master_clock &= ~0x00FF0000; chip->master_clock |= data << 16; break; case 0x0B: //if ((data >> 7) != chip->pin7_state) // fprintf(stderr, "Pin 7 changed!\n"); data &= 0x7F; // fix a bug in MAME VGM logs chip->master_clock &= ~0xFF000000; chip->master_clock |= data << 24; okim6295_clock_changed(chip); break; case 0x0C: okim6295_set_pin7(chip, data); break; case 0x0E: // NMK112 bank switch enable chip->nmk_mode = data; break; case 0x0F: okim6295_set_bank_base(chip, data << 18); break; case 0x10: case 0x11: case 0x12: case 0x13: chip->nmk_bank[offset & 0x03] = data; break; } return; } void okim6295_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { okim6295_state *chip = &OKIM6295Data[ChipID]; if (chip->ROMSize != ROMSize) { chip->ROM = (UINT8*)realloc(chip->ROM, ROMSize); chip->ROMSize = ROMSize; //fprintf(stderr, "OKIM6295: New ROM Size: 0x%05X\n", ROMSize); memset(chip->ROM, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->ROM + DataStart, ROMData, DataLength); return; } void okim6295_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { okim6295_state *chip = &OKIM6295Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < OKIM6295_VOICES; CurChn ++) chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } void okim6295_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr) { okim6295_state *info = &OKIM6295Data[ChipID]; // set Sample Rate Change Callback routine info->SmpRateFunc = CallbackFunc; info->SmpRateData = DataPtr; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( okim6295 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6295_state); break; case DEVINFO_INT_DATABUS_WIDTH_0: info->i = 8; break; case DEVINFO_INT_ADDRBUS_WIDTH_0: info->i = 18; break; case DEVINFO_INT_ADDRBUS_SHIFT_0: info->i = 0; break; // --- the following bits of info are returned as pointers to data --- // case DEVINFO_PTR_DEFAULT_MEMORY_MAP_0: info->default_map8 = ADDRESS_MAP_NAME(okim6295);break; // --- the following bits of info are returned as pointers to functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( okim6295 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( okim6295 ); break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "OKI6295"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "OKI ADPCM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/okim6295.h ================================================ #pragma once /* an interface for the OKIM6295 and similar chips */ /* Note about the playback frequency: the external clock is internally divided, depending on pin 7, by 132 (high) or 165 (low). */ /*typedef struct _okim6295_interface okim6295_interface; struct _okim6295_interface { int pin7; }; extern const okim6295_interface okim6295_interface_pin7high; extern const okim6295_interface okim6295_interface_pin7low;*/ //void okim6295_set_bank_base(running_device *device, int base); //void okim6295_set_pin7(running_device *device, int pin7); void okim6295_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_okim6295(UINT8 ChipID, int clock); void device_stop_okim6295(UINT8 ChipID); void device_reset_okim6295(UINT8 ChipID); //READ8_DEVICE_HANDLER( okim6295_r ); //WRITE8_DEVICE_HANDLER( okim6295_w ); void okim6295_w(UINT8 ChipID, offs_t offset, UINT8 data); void okim6295_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void okim6295_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void okim6295_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr); /* To help the various custom ADPCM generators out there, the following routines may be used. */ struct adpcm_state { INT32 signal; INT32 step; }; void reset_adpcm(struct adpcm_state *state); INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble); //DEVICE_GET_INFO( okim6295 ); //#define SOUND_OKIM6295 DEVICE_GET_INFO_NAME( okim6295 ) ================================================ FILE: VGMPlay/chips/opl.c ================================================ // IMPORTANT: This file is not meant to be compiled. It's included in adlibemu_opl?.c. /* * Copyright (C) 2002-2010 The DOSBox Team * OPL2/OPL3 emulation library * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman * Copyright (C) 1998-2001 Ken Silverman * Ken Silverman's official web site: "http://www.advsys.net/ken" */ #include #include // rand #include // for memset //#include "dosbox.h" #include "../stdbool.h" #include "opl.h" //static fltype recipsamp; // inverse of sampling rate // moved to OPL_DATA static Bit16s wavtable[WAVEPREC*3]; // wave form table // vibrato/tremolo tables static Bit32s vib_table[VIBTAB_SIZE]; static Bit32s trem_table[TREMTAB_SIZE*2]; static Bit32s vibval_const[BLOCKBUF_SIZE]; static Bit32s tremval_const[BLOCKBUF_SIZE]; // vibrato value tables (used per-operator) static Bit32s vibval_var1[BLOCKBUF_SIZE]; static Bit32s vibval_var2[BLOCKBUF_SIZE]; //static Bit32s vibval_var3[BLOCKBUF_SIZE]; //static Bit32s vibval_var4[BLOCKBUF_SIZE]; // vibrato/trmolo value table pointers //static Bit32s *vibval1, *vibval2, *vibval3, *vibval4; //static Bit32s *tremval1, *tremval2, *tremval3, *tremval4; // moved to adlib_getsample // key scale level lookup table static const fltype kslmul[4] = { 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct }; // frequency multiplicator lookup table static const fltype frqmul_tab[16] = { 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 }; // calculated frequency multiplication values (depend on sampling rate) //static fltype frqmul[16]; // moved to OPL_DATA // key scale levels static Bit8u kslev[8][16]; // map a channel number to the register offset of the modulator (=register base) static const Bit8u modulatorbase[9] = { 0,1,2, 8,9,10, 16,17,18 }; // map a register base to a modulator operator number or operator number #if defined(OPLTYPE_IS_OPL3) static const Bit8u regbase2modop[44] = { 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set }; static const Bit8u regbase2op[44] = { 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set }; #else static const Bit8u regbase2modop[22] = { 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 }; static const Bit8u regbase2op[22] = { 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 }; #endif // start of the waveform static Bit32u waveform[8] = { WAVEPREC, WAVEPREC>>1, WAVEPREC, (WAVEPREC*3)>>2, 0, 0, (WAVEPREC*5)>>2, WAVEPREC<<1 }; // length of the waveform as mask static Bit32u wavemask[8] = { WAVEPREC-1, WAVEPREC-1, (WAVEPREC>>1)-1, (WAVEPREC>>1)-1, WAVEPREC-1, ((WAVEPREC*3)>>2)-1, WAVEPREC>>1, WAVEPREC-1 }; // where the first entry resides static Bit32u wavestart[8] = { 0, WAVEPREC>>1, 0, WAVEPREC>>2, 0, 0, 0, WAVEPREC>>3 }; // envelope generator function constants static fltype attackconst[4] = { (fltype)(1/2.82624), (fltype)(1/2.25280), (fltype)(1/1.88416), (fltype)(1/1.59744) }; static fltype decrelconst[4] = { (fltype)(1/39.28064), (fltype)(1/31.41608), (fltype)(1/26.17344), (fltype)(1/22.44608) }; INLINE void operator_advance(OPL_DATA* chip, op_type* op_pt, Bit32s vib) { op_pt->wfpos = op_pt->tcount; // waveform position // advance waveform time op_pt->tcount += op_pt->tinc; op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; op_pt->generator_pos += chip->generator_add; } INLINE void operator_advance_drums(OPL_DATA* chip, op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { Bit32u c1 = op_pt1->tcount/FIXEDPT; Bit32u c3 = op_pt3->tcount/FIXEDPT; Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; Bit32u noisebit = rand()&1; Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); //Hihat Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); op_pt1->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt1->tcount += op_pt1->tinc; op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; op_pt1->generator_pos += chip->generator_add; //Snare inttm = ((1+snare_phase_bit) ^ noisebit)<<8; op_pt2->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt2->tcount += op_pt2->tinc; op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; op_pt2->generator_pos += chip->generator_add; //Cymbal inttm = (1+phasebit)<<8; op_pt3->wfpos = inttm*FIXEDPT; // waveform position // advance waveform time op_pt3->tcount += op_pt3->tinc; op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; op_pt3->generator_pos += chip->generator_add; } // output level is sustained, mode changes only when operator is turned off (->release) // or when the keep-sustained bit is turned off (->sustain_nokeep) INLINE void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { if (op_pt->op_state != OF_TYPE_OFF) { Bit32u i; op_pt->lastcval = op_pt->cval; i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); // wform: -16384 to 16383 (0x4000) // trem : 32768 to 65535 (0x10000) // step_amp: 0.0 to 1.0 // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); } } // no action, operator is off static void operator_off(op_type* op_pt) { } // output level is sustained, mode changes only when operator is turned off (->release) // or when the keep-sustained bit is turned off (->sustain_nokeep) static void operator_sustain(op_type* op_pt) { Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples Bit32u ct; for (ct=0; ctcur_env_step++; } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in release mode, if output level reaches zero the operator is turned off static void operator_release(op_type* op_pt) { Bit32u num_steps_add; Bit32u ct; // ??? boundary? if (op_pt->amp > 0.00000001) { // release phase op_pt->amp *= op_pt->releasemul; } num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (ct=0; ctcur_env_step++; // sample counter if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { if (op_pt->amp <= 0.00000001) { // release phase finished, turn off this operator op_pt->amp = 0.0; if (op_pt->op_state == OF_TYPE_REL) { op_pt->op_state = OF_TYPE_OFF; } } op_pt->step_amp = op_pt->amp; } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in decay mode, if sustain level is reached the output level is either // kept (sustain level keep enabled) or the operator is switched into release mode static void operator_decay(op_type* op_pt) { Bit32u num_steps_add; Bit32u ct; if (op_pt->amp > op_pt->sustain_level) { // decay phase op_pt->amp *= op_pt->decaymul; } num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (ct=0; ctcur_env_step++; if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { if (op_pt->amp <= op_pt->sustain_level) { // decay phase finished, sustain level reached if (op_pt->sus_keep) { // keep sustain level (until turned off) op_pt->op_state = OF_TYPE_SUS; op_pt->amp = op_pt->sustain_level; } else { // next: release phase op_pt->op_state = OF_TYPE_SUS_NOKEEP; } } op_pt->step_amp = op_pt->amp; } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } // operator in attack mode, if full output level is reached, // the operator is switched into decay mode static void operator_attack(op_type* op_pt) { Bit32u num_steps_add; Bit32u ct; op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples for (ct=0; ctcur_env_step++; // next sample if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached if (op_pt->amp > 1.0) { // attack phase finished, next: decay op_pt->op_state = OF_TYPE_DEC; op_pt->amp = 1.0; op_pt->step_amp = 1.0; } op_pt->step_skip_pos_a <<= 1; if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step op_pt->step_amp = op_pt->amp; } } } op_pt->generator_pos -= num_steps_add*FIXEDPT; } static void operator_eg_attack_check(op_type* op_pt) { if (((op_pt->cur_env_step + 1) & op_pt->env_step_a)==0) { // check if next step already reached if (op_pt->a0 >= 1.0) { // attack phase finished, next: decay op_pt->op_state = OF_TYPE_DEC; op_pt->amp = 1.0; op_pt->step_amp = 1.0; } } } typedef void (*optype_fptr)(op_type*); static optype_fptr opfuncs[6] = { operator_attack, operator_decay, operator_release, operator_sustain, // sustain phase (keeping level) operator_release, // sustain_nokeep phase (release-style) operator_off }; static void change_attackrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { Bits attackrate = chip->adlibreg[ARC_ATTR_DECR+regbase]>>4; if (attackrate) { static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; Bits step_skip; Bits steps; Bits step_num; fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*chip->recipsamp); // attack rate coefficients op_pt->a0 = (fltype)(0.0377*f); op_pt->a1 = (fltype)(10.73*f+1); op_pt->a2 = (fltype)(-17.57*f); op_pt->a3 = (fltype)(7.42*f); step_skip = attackrate*4 + op_pt->toff; steps = step_skip >> 2; op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; step_num = (step_skip<=48)?(4-(step_skip&3)):0; op_pt->env_step_skip_a = step_skip_mask[step_num]; #if defined(OPLTYPE_IS_OPL3) if (step_skip>=60) #else if (step_skip>=62) #endif { op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 op_pt->a1 = (fltype)(0.0); op_pt->a2 = (fltype)(0.0); op_pt->a3 = (fltype)(0.0); } } else { // attack disabled op_pt->a0 = 0.0; op_pt->a1 = 1.0; op_pt->a2 = 0.0; op_pt->a3 = 0.0; op_pt->env_step_a = 0; op_pt->env_step_skip_a = 0; } } static void change_decayrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { Bits decayrate = chip->adlibreg[ARC_ATTR_DECR+regbase]&15; // decaymul should be 1.0 when decayrate==0 if (decayrate) { Bits steps; fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*chip->recipsamp); op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); steps = (decayrate*4 + op_pt->toff) >> 2; op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; } else { op_pt->decaymul = 1.0; op_pt->env_step_d = 0; } } static void change_releaserate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { Bits releaserate = chip->adlibreg[ARC_SUSL_RELR+regbase]&15; // releasemul should be 1.0 when releaserate==0 if (releaserate) { Bits steps; fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*chip->recipsamp); op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); steps = (releaserate*4 + op_pt->toff) >> 2; op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; } else { op_pt->releasemul = 1.0; op_pt->env_step_r = 0; } } static void change_sustainlevel(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { Bits sustainlevel = chip->adlibreg[ARC_SUSL_RELR+regbase]>>4; // sustainlevel should be 0.0 when sustainlevel==15 (max) if (sustainlevel<15) { op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); } else { op_pt->sustain_level = 0.0; } } static void change_waveform(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { #if defined(OPLTYPE_IS_OPL3) if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 #endif // waveform selection op_pt->cur_wmask = wavemask[chip->wave_sel[regbase]]; op_pt->cur_wform = &wavtable[waveform[chip->wave_sel[regbase]]]; // (might need to be adapted to waveform type here...) } static void change_keepsustain(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { op_pt->sus_keep = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; if (op_pt->op_state==OF_TYPE_SUS) { if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; } } // enable/disable vibrato/tremolo LFO effects static void change_vibrato(OPL_DATA* chip, Bitu regbase, op_type* op_pt) { op_pt->vibrato = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; op_pt->tremolo = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; } // change amount of self-feedback static void change_feedback(OPL_DATA* chip, Bitu chanbase, op_type* op_pt) { Bits feedback = chip->adlibreg[ARC_FEEDBACK+chanbase]&14; if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); else op_pt->mfbi = 0; } static void change_frequency(OPL_DATA* chip, Bitu chanbase, Bitu regbase, op_type* op_pt) { Bit32u frn; Bit32u oct; Bit32u note_sel; fltype vol_in; // frequency frn = ((((Bit32u)chip->adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)chip->adlibreg[ARC_FREQ_NUM+chanbase]; // block number/octave oct = ((((Bit32u)chip->adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); op_pt->freq_high = (Bit32s)((frn>>7)&7); // keysplit note_sel = (chip->adlibreg[8]>>6)&1; op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); op_pt->toff += (oct<<1); // envelope scaling (KSR) if (!(chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; // 20+a0+b0: op_pt->tinc = (Bit32u)((((fltype)(frn<frqmul[chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&15])); // 40+a0+b0: vol_in = (fltype)((fltype)(chip->adlibreg[ARC_KSL_OUTLEV+regbase]&63) + kslmul[chip->adlibreg[ARC_KSL_OUTLEV+regbase]>>6]*kslev[oct][frn>>6]); op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); // operator frequency changed, care about features that depend on it change_attackrate(chip, regbase,op_pt); change_decayrate(chip, regbase,op_pt); change_releaserate(chip, regbase,op_pt); } static void enable_operator(OPL_DATA* chip, Bitu regbase, op_type* op_pt, Bit32u act_type) { // check if this is really an off-on transition if (op_pt->act_state == OP_ACT_OFF) { Bits wselbase = regbase; if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22 op_pt->tcount = wavestart[chip->wave_sel[wselbase]]*FIXEDPT; // start with attack mode op_pt->op_state = OF_TYPE_ATT; op_pt->act_state |= act_type; } } static void disable_operator(op_type* op_pt, Bit32u act_type) { // check if this is really an on-off transition if (op_pt->act_state != OP_ACT_OFF) { op_pt->act_state &= (~act_type); if (op_pt->act_state == OP_ACT_OFF) { if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; } } } //void adlib_init(Bit32u samplerate) void* ADLIBEMU(init)(UINT32 clock, UINT32 samplerate, ADL_UPDATEHANDLER UpdateHandler, void* param) { OPL_DATA* OPL; //op_type* op; Bits i, j, oct; //Bit32s trem_table_int[TREMTAB_SIZE]; static Bitu initfirstime = 0; OPL = (OPL_DATA*)malloc(sizeof(OPL_DATA)); OPL->chip_clock = clock; OPL->int_samplerate = samplerate; OPL->UpdateHandler = UpdateHandler; OPL->UpdateParam = param; OPL->generator_add = (Bit32u)(INTFREQU*FIXEDPT/OPL->int_samplerate); /*memset(OPL->adlibreg,0,sizeof(OPL->adlibreg)); memset(OPL->op,0,sizeof(op_type)*MAXOPERATORS); memset(OPL->wave_sel,0,sizeof(OPL->wave_sel)); for (i=0;iop[i]; op->op_state = OF_TYPE_OFF; op->act_state = OP_ACT_OFF; op->amp = 0.0; op->step_amp = 0.0; op->vol = 0.0; op->tcount = 0; op->tinc = 0; op->toff = 0; op->cur_wmask = wavemask[0]; op->cur_wform = &wavtable[waveform[0]]; op->freq_high = 0; op->generator_pos = 0; op->cur_env_step = 0; op->env_step_a = 0; op->env_step_d = 0; op->env_step_r = 0; op->step_skip_pos_a = 0; op->env_step_skip_a = 0; #if defined(OPLTYPE_IS_OPL3) op->is_4op = false; op->is_4op_attached = false; op->left_pan = 1; op->right_pan = 1; #endif }*/ OPL->recipsamp = 1.0 / (fltype)OPL->int_samplerate; for (i=15;i>=0;i--) { OPL->frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*OPL->recipsamp); } //OPL->status = 0; //OPL->opl_index = 0; if (!initfirstime) { // create vibrato table vib_table[0] = 8; vib_table[1] = 4; vib_table[2] = 0; vib_table[3] = -4; for (i=4; ivibtab_add = (Bit32u)(VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/OPL->int_samplerate); OPL->vibtab_pos = 0; if (!initfirstime) { Bit32s trem_table_int[TREMTAB_SIZE]; for (i=0; i -0.5/6 to 0) for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6) for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6) for (i=0; itremtab_add = (Bit32u)((fltype)TREMTAB_SIZE * TREM_FREQ * FIXEDPT_LFO / (fltype)OPL->int_samplerate); OPL->tremtab_pos = 0; if (!initfirstime) { initfirstime = 1; for (i=0; i>1);i++) { wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); wavtable[i] = wavtable[(i<<1) +WAVEPREC]; // alternative: (zero-less) /* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ } for (i=0;i<(WAVEPREC>>3);i++) { wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; } // key scale level table verified ([table in book]*8/3) kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; kslev[7][8] = 48; for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); for (j=6;j>=0;j--) { for (i=0;i<16;i++) { oct = (Bits)kslev[j+1][i]-8; if (oct < 0) oct = 0; kslev[j][i] = (Bit8u)oct; } } } return OPL; } void ADLIBEMU(stop)(void *chip) { free(chip); return; } void ADLIBEMU(reset)(void *chip) { OPL_DATA* OPL = (OPL_DATA*)chip; Bits i; op_type* op; memset(OPL->adlibreg, 0x00, sizeof(OPL->adlibreg)); memset(OPL->op, 0x00, sizeof(op_type) * MAXOPERATORS); memset(OPL->wave_sel, 0x00, sizeof(OPL->wave_sel)); for (i=0;iop[i]; op->op_state = OF_TYPE_OFF; op->act_state = OP_ACT_OFF; op->amp = 0.0; op->step_amp = 0.0; op->vol = 0.0; op->tcount = 0; op->tinc = 0; op->toff = 0; op->cur_wmask = wavemask[0]; op->cur_wform = &wavtable[waveform[0]]; op->freq_high = 0; op->generator_pos = 0; op->cur_env_step = 0; op->env_step_a = 0; op->env_step_d = 0; op->env_step_r = 0; op->step_skip_pos_a = 0; op->env_step_skip_a = 0; #if defined(OPLTYPE_IS_OPL3) op->is_4op = false; op->is_4op_attached = false; op->left_pan = 1; op->right_pan = 1; #endif } OPL->status = 0; OPL->opl_index = 0; OPL->opl_addr = 0; return; } void ADLIBEMU(writeIO)(void *chip, UINT32 addr, UINT8 val) { OPL_DATA* OPL = (OPL_DATA*)chip; if (addr & 1) adlib_write(OPL, OPL->opl_addr, val); else #if defined(OPLTYPE_IS_OPL3) OPL->opl_addr = val | ((addr & 2) << 7); #else OPL->opl_addr = val; #endif } static void adlib_write(void *chip, Bitu idx, Bit8u val) { OPL_DATA* OPL = (OPL_DATA*)chip; Bit32u second_set = idx&0x100; OPL->adlibreg[idx] = val; switch (idx&0xf0) { case ARC_CONTROL: // here we check for the second set registers, too: switch (idx) { case 0x02: // timer1 counter case 0x03: // timer2 counter break; case 0x04: // IRQ reset, timer mask/start if (val&0x80) { // clear IRQ bits in status register OPL->status &= ~0x60; } else { OPL->status = 0; } break; #if defined(OPLTYPE_IS_OPL3) case 0x04|ARC_SECONDSET: // 4op enable/disable switches for each possible channel OPL->op[0].is_4op = (val&1)>0; OPL->op[3].is_4op_attached = OPL->op[0].is_4op; OPL->op[1].is_4op = (val&2)>0; OPL->op[4].is_4op_attached = OPL->op[1].is_4op; OPL->op[2].is_4op = (val&4)>0; OPL->op[5].is_4op_attached = OPL->op[2].is_4op; OPL->op[18].is_4op = (val&8)>0; OPL->op[21].is_4op_attached = OPL->op[18].is_4op; OPL->op[19].is_4op = (val&16)>0; OPL->op[22].is_4op_attached = OPL->op[19].is_4op; OPL->op[20].is_4op = (val&32)>0; OPL->op[23].is_4op_attached = OPL->op[20].is_4op; break; case 0x05|ARC_SECONDSET: break; #endif case 0x08: // CSW, note select break; default: break; } break; case ARC_TVS_KSR_MUL: case ARC_TVS_KSR_MUL+0x10: { // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication int num = idx&7; Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; if ((num<6) && (base<22)) { Bitu modop = regbase2modop[second_set?(base+22):base]; Bitu regbase = base+second_set; Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; // change tremolo/vibrato and sustain keeping of this operator op_type* op_ptr = &OPL->op[modop+((num<3) ? 0 : 9)]; change_keepsustain(chip, regbase,op_ptr); change_vibrato(chip, regbase,op_ptr); // change frequency calculations of this operator as // key scale rate and frequency multiplicator can be changed #if defined(OPLTYPE_IS_OPL3) if ((OPL->adlibreg[0x105]&1) && (OPL->op[modop].is_4op_attached)) { // operator uses frequency of channel change_frequency(chip, chanbase-3,regbase,op_ptr); } else { change_frequency(chip, chanbase,regbase,op_ptr); } #else change_frequency(chip, chanbase,base,op_ptr); #endif } } break; case ARC_KSL_OUTLEV: case ARC_KSL_OUTLEV+0x10: { // key scale level; output rate int num = idx&7; Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; if ((num<6) && (base<22)) { Bitu modop = regbase2modop[second_set?(base+22):base]; Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; // change frequency calculations of this operator as // key scale level and output rate can be changed op_type* op_ptr = &OPL->op[modop+((num<3) ? 0 : 9)]; #if defined(OPLTYPE_IS_OPL3) Bitu regbase = base+second_set; if ((OPL->adlibreg[0x105]&1) && (OPL->op[modop].is_4op_attached)) { // operator uses frequency of channel change_frequency(chip, chanbase-3,regbase,op_ptr); } else { change_frequency(chip, chanbase,regbase,op_ptr); } #else change_frequency(chip, chanbase,base,op_ptr); #endif } } break; case ARC_ATTR_DECR: case ARC_ATTR_DECR+0x10: { // attack/decay rates int num = idx&7; Bitu base = (idx-ARC_ATTR_DECR)&0xff; if ((num<6) && (base<22)) { Bitu regbase = base+second_set; // change attack rate and decay rate of this operator op_type* op_ptr = &OPL->op[regbase2op[second_set?(base+22):base]]; change_attackrate(chip, regbase,op_ptr); change_decayrate(chip, regbase,op_ptr); } } break; case ARC_SUSL_RELR: case ARC_SUSL_RELR+0x10: { // sustain level; release rate int num = idx&7; Bitu base = (idx-ARC_SUSL_RELR)&0xff; if ((num<6) && (base<22)) { Bitu regbase = base+second_set; // change sustain level and release rate of this operator op_type* op_ptr = &OPL->op[regbase2op[second_set?(base+22):base]]; change_releaserate(chip, regbase,op_ptr); change_sustainlevel(chip, regbase,op_ptr); } } break; case ARC_FREQ_NUM: { // 0xa0-0xa8 low8 frequency Bitu base = (idx-ARC_FREQ_NUM)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; Bits modbase; Bitu chanbase; #if defined(OPLTYPE_IS_OPL3) if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op_attached) break; #endif // regbase of modulator: modbase = modulatorbase[base]+second_set; chanbase = base+second_set; change_frequency(chip, chanbase,modbase,&OPL->op[opbase]); change_frequency(chip, chanbase,modbase+3,&OPL->op[opbase+9]); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are modified to the frequency of the channel if ((OPL->adlibreg[0x105]&1) && OPL->op[second_set?(base+18):base].is_4op) { change_frequency(chip, chanbase,modbase+8,&OPL->op[opbase+3]); change_frequency(chip, chanbase,modbase+3+8,&OPL->op[opbase+3+9]); } #endif } } break; case ARC_KON_BNUM: { Bitu base; if (OPL->UpdateHandler != NULL) // hack for DOSBox logs OPL->UpdateHandler(OPL->UpdateParam); if (idx == ARC_PERC_MODE) { #if defined(OPLTYPE_IS_OPL3) if (second_set) return; #endif if ((val&0x30) == 0x30) { // BassDrum active enable_operator(chip, 16,&OPL->op[6],OP_ACT_PERC); change_frequency(chip, 6,16,&OPL->op[6]); enable_operator(chip, 16+3,&OPL->op[6+9],OP_ACT_PERC); change_frequency(chip, 6,16+3,&OPL->op[6+9]); } else { disable_operator(&OPL->op[6],OP_ACT_PERC); disable_operator(&OPL->op[6+9],OP_ACT_PERC); } if ((val&0x28) == 0x28) { // Snare active enable_operator(chip, 17+3,&OPL->op[16],OP_ACT_PERC); change_frequency(chip, 7,17+3,&OPL->op[16]); } else { disable_operator(&OPL->op[16],OP_ACT_PERC); } if ((val&0x24) == 0x24) { // TomTom active enable_operator(chip, 18,&OPL->op[8],OP_ACT_PERC); change_frequency(chip, 8,18,&OPL->op[8]); } else { disable_operator(&OPL->op[8],OP_ACT_PERC); } if ((val&0x22) == 0x22) { // Cymbal active enable_operator(chip, 18+3,&OPL->op[8+9],OP_ACT_PERC); change_frequency(chip, 8,18+3,&OPL->op[8+9]); } else { disable_operator(&OPL->op[8+9],OP_ACT_PERC); } if ((val&0x21) == 0x21) { // Hihat active enable_operator(chip, 17,&OPL->op[7],OP_ACT_PERC); change_frequency(chip, 7,17,&OPL->op[7]); } else { disable_operator(&OPL->op[7],OP_ACT_PERC); } break; } // regular 0xb0-0xb8 base = (idx-ARC_KON_BNUM)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; // regbase of modulator: Bits modbase = modulatorbase[base]+second_set; Bitu chanbase; #if defined(OPLTYPE_IS_OPL3) if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op_attached) break; #endif if (val&32) { // operator switched on enable_operator(chip, modbase,&OPL->op[opbase],OP_ACT_NORMAL); // modulator (if 2op) enable_operator(chip, modbase+3,&OPL->op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are switched on if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op) { // turn on chan+3 operators as well enable_operator(chip, modbase+8,&OPL->op[opbase+3],OP_ACT_NORMAL); enable_operator(chip, modbase+3+8,&OPL->op[opbase+3+9],OP_ACT_NORMAL); } #endif } else { // operator switched off disable_operator(&OPL->op[opbase],OP_ACT_NORMAL); disable_operator(&OPL->op[opbase+9],OP_ACT_NORMAL); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are switched off if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op) { // turn off chan+3 operators as well disable_operator(&OPL->op[opbase+3],OP_ACT_NORMAL); disable_operator(&OPL->op[opbase+3+9],OP_ACT_NORMAL); } #endif } chanbase = base+second_set; // change frequency calculations of modulator and carrier (2op) as // the frequency of the channel has changed change_frequency(chip, chanbase,modbase,&OPL->op[opbase]); change_frequency(chip, chanbase,modbase+3,&OPL->op[opbase+9]); #if defined(OPLTYPE_IS_OPL3) // for 4op channels all four operators are modified to the frequency of the channel if ((OPL->adlibreg[0x105]&1) && OPL->op[second_set?(base+18):base].is_4op) { // change frequency calculations of chan+3 operators as well change_frequency(chip, chanbase,modbase+8,&OPL->op[opbase+3]); change_frequency(chip, chanbase,modbase+3+8,&OPL->op[opbase+3+9]); } #endif } } break; case ARC_FEEDBACK: { // 0xc0-0xc8 feedback/modulation type (AM/FM) Bitu base = (idx-ARC_FEEDBACK)&0xff; if (base<9) { Bits opbase = second_set?(base+18):base; Bitu chanbase = base+second_set; change_feedback(chip, chanbase,&OPL->op[opbase]); #if defined(OPLTYPE_IS_OPL3) // OPL3 panning OPL->op[opbase].left_pan = ((val&0x10)>>4); OPL->op[opbase].right_pan = ((val&0x20)>>5); OPL->op[opbase].left_pan += ((val&0x40)>>6); OPL->op[opbase].right_pan += ((val&0x80)>>7); #endif } } break; case ARC_WAVE_SEL: case ARC_WAVE_SEL+0x10: { int num = idx&7; Bitu base = (idx-ARC_WAVE_SEL)&0xff; if ((num<6) && (base<22)) { #if defined(OPLTYPE_IS_OPL3) Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] op_type* op_ptr; // change waveform if (OPL->adlibreg[0x105]&1) OPL->wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible else OPL->wave_sel[wselbase] = val&3; op_ptr = &OPL->op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; change_waveform(chip, wselbase,op_ptr); #else if (OPL->adlibreg[0x01]&0x20) { op_type* op_ptr; // wave selection enabled, change waveform OPL->wave_sel[base] = val&3; op_ptr = &OPL->op[regbase2modop[base]+((num<3) ? 0 : 9)]; change_waveform(chip, base,op_ptr); } #endif } } break; default: break; } } Bitu ADLIBEMU(reg_read)(void *chip, UINT32 port) { OPL_DATA* OPL = (OPL_DATA*)chip; #if defined(OPLTYPE_IS_OPL3) // opl3-detection routines require ret&6 to be zero if ((port&1)==0) { return OPL->status; } return 0x00; #else // opl2-detection routines require ret&6 to be 6 if ((port&1)==0) { return OPL->status|6; } return 0xff; #endif } void ADLIBEMU(write_index)(void *chip, UINT32 port, UINT8 val) { OPL_DATA* OPL = (OPL_DATA*)chip; OPL->opl_index = val; #if defined(OPLTYPE_IS_OPL3) if ((port&3)!=0) { // possibly second set if (((OPL->adlibreg[0x105]&1)!=0) || (OPL->opl_index==5)) OPL->opl_index |= ARC_SECONDSET; } #endif } /*static void OPL_INLINE clipit16(Bit32s ival, Bit16s* outval) { if (ival<32768) { if (ival>-32769) { *outval=(Bit16s)ival; } else { *outval = -32768; } } else { *outval = 32767; } }*/ // be careful with this // uses cptr and chanval, outputs into outbufl(/outbufr) // for opl3 check if opl3-mode is enabled (which uses stereo panning) // // Changes by Valley Bell: // - Changed to always output to both channels // - added parameter "chn" to fix panning for 4-op channels and the Rhythm Cymbal #undef CHANVAL_OUT #if defined(OPLTYPE_IS_OPL3) #define CHANVAL_OUT(chn) \ if (OPL->adlibreg[0x105]&1) { \ outbufl[i] += chanval*cptr[chn].left_pan; \ outbufr[i] += chanval*cptr[chn].right_pan; \ } else { \ outbufl[i] += chanval; \ outbufr[i] += chanval; \ } #else #define CHANVAL_OUT(chn) \ outbufl[i] += chanval; \ outbufr[i] += chanval; #endif //void adlib_getsample(Bit16s* sndptr, Bits numsamples) void ADLIBEMU(getsample)(void *chip, INT32** sndptr, INT32 numsamples) { OPL_DATA* OPL = (OPL_DATA*)chip; Bits i, endsamples; op_type* cptr; //Bit32s outbufl[BLOCKBUF_SIZE]; #if defined(OPLTYPE_IS_OPL3) // second output buffer (right channel for opl3 stereo) //Bit32s outbufr[BLOCKBUF_SIZE]; #endif Bit32s* outbufl = sndptr[0]; Bit32s* outbufr = sndptr[1]; // vibrato/tremolo lookup tables (global, to possibly be used by all operators) Bit32s vib_lut[BLOCKBUF_SIZE]; Bit32s trem_lut[BLOCKBUF_SIZE]; Bits samples_to_process = numsamples; Bits cursmp; Bit32s vib_tshift; Bitu max_channel = NUM_CHANNELS; Bits cur_ch; Bit32s *vibval1, *vibval2, *vibval3, *vibval4; Bit32s *tremval1, *tremval2, *tremval3, *tremval4; #if defined(OPLTYPE_IS_OPL3) if ((OPL->adlibreg[0x105]&1)==0) max_channel = NUM_CHANNELS/2; #endif if (! samples_to_process) { for (cur_ch = 0; cur_ch < max_channel; cur_ch ++) { if ((OPL->adlibreg[ARC_PERC_MODE] & 0x20) && (cur_ch >= 6 && cur_ch < 9)) continue; #if defined(OPLTYPE_IS_OPL3) if (cur_ch < 9) cptr = &OPL->op[cur_ch]; else cptr = &OPL->op[cur_ch+9]; // second set is operator18-operator35 if (cptr->is_4op_attached) continue; #else cptr = &OPL->op[cur_ch]; #endif if (cptr[0].op_state == OF_TYPE_ATT) operator_eg_attack_check(&cptr[0]); if (cptr[9].op_state == OF_TYPE_ATT) operator_eg_attack_check(&cptr[9]); } return; } for (cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; memset(outbufl,0,endsamples*sizeof(Bit32s)); //#if defined(OPLTYPE_IS_OPL3) // // clear second output buffer (opl3 stereo) // //if (adlibreg[0x105]&1) memset(outbufr,0,endsamples*sizeof(Bit32s)); //#endif // calculate vibrato/tremolo lookup tables vib_tshift = ((OPL->adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching for (i=0;ivibtab_pos += OPL->vibtab_add; if (OPL->vibtab_pos/FIXEDPT_LFO>=VIBTAB_SIZE) OPL->vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; vib_lut[i] = vib_table[OPL->vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents // cycle through tremolo table OPL->tremtab_pos += OPL->tremtab_add; if (OPL->tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) OPL->tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; if (OPL->adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[OPL->tremtab_pos/FIXEDPT_LFO]; else trem_lut[i] = trem_table[TREMTAB_SIZE+OPL->tremtab_pos/FIXEDPT_LFO]; } if (OPL->adlibreg[ARC_PERC_MODE]&0x20) { if (! (OPL->MuteChn[NUM_CHANNELS + 0])) { //BassDrum cptr = &OPL->op[6]; if (OPL->adlibreg[ARC_FEEDBACK+6]&1) { // additive synthesis if (cptr[9].op_state != OF_TYPE_OFF) { if (cptr[9].vibrato) { vibval1 = vibval_var1; for (i=0;iMuteChn[NUM_CHANNELS + 2]) && OPL->op[8].op_state != OF_TYPE_OFF) { cptr = &OPL->op[8]; if (cptr[0].vibrato) { vibval3 = vibval_var1; for (i=0;iop[7].op_state != OF_TYPE_OFF) || (OPL->op[16].op_state != OF_TYPE_OFF) || (OPL->op[17].op_state != OF_TYPE_OFF)) { cptr = &OPL->op[7]; if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { vibval1 = vibval_var1; for (i=0;iop[8]; if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) { vibval4 = vibval_var2; for (i=0;iop[0]; // set cptr to something useful (else it stays at op[8]) for (i=0;iop[7],vibval1[i],&OPL->op[7+9],vibval2[i],&OPL->op[8+9],vibval4[i]); if (! (OPL->MuteChn[NUM_CHANNELS + 4])) { opfuncs[OPL->op[7].op_state](&OPL->op[7]); //Hihat operator_output(&OPL->op[7],0,tremval1[i]); } else OPL->op[7].cval = 0; if (! (OPL->MuteChn[NUM_CHANNELS + 1])) { opfuncs[OPL->op[7+9].op_state](&OPL->op[7+9]); //Snare operator_output(&OPL->op[7+9],0,tremval2[i]); } else OPL->op[7+9].cval = 0; if (! (OPL->MuteChn[NUM_CHANNELS + 3])) { opfuncs[OPL->op[8+9].op_state](&OPL->op[8+9]); //Cymbal operator_output(&OPL->op[8+9],0,tremval4[i]); } else OPL->op[8+9].cval = 0; //chanval = (OPL->op[7].cval + OPL->op[7+9].cval + OPL->op[8+9].cval)*2; //CHANVAL_OUT(0) // fix panning of the snare -Valley Bell chanval = (OPL->op[7].cval + OPL->op[7+9].cval)*2; CHANVAL_OUT(7) chanval = OPL->op[8+9].cval*2; CHANVAL_OUT(8) } } } for (cur_ch=max_channel-1; cur_ch>=0; cur_ch--) { Bitu k; if (OPL->MuteChn[cur_ch]) continue; // skip drum/percussion operators if ((OPL->adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; k = cur_ch; #if defined(OPLTYPE_IS_OPL3) if (cur_ch < 9) { cptr = &OPL->op[cur_ch]; } else { cptr = &OPL->op[cur_ch+9]; // second set is operator18-operator35 k += (-9+256); // second set uses registers 0x100 onwards } // check if this operator is part of a 4-op //if ((OPL->adlibreg[0x105]&1) && cptr->is_4op_attached) continue; if (cptr->is_4op_attached) continue; // this is more correct #else cptr = &OPL->op[cur_ch]; #endif // check for FM/AM if (OPL->adlibreg[ARC_FEEDBACK+k]&1) { #if defined(OPLTYPE_IS_OPL3) //if ((OPL->adlibreg[0x105]&1) && cptr->is_4op) if (cptr->is_4op) // this is more correct { if (OPL->adlibreg[ARC_FEEDBACK+k+3]&1) { // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) if (cptr[0].op_state != OF_TYPE_OFF) { if (cptr[0].vibrato) { vibval1 = vibval_var1; for (i=0;iadlibreg[0x105]&1) && cptr->is_4op) if (cptr->is_4op) // this is more correct { if (OPL->adlibreg[ARC_FEEDBACK+k+3]&1) { // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { vibval1 = vibval_var1; for (i=0;iMuteChn[CurChn] = (MuteMask >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/opl.h ================================================ /* * Copyright (C) 2002-2010 The DOSBox Team * OPL2/OPL3 emulation library * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman * Copyright (C) 1998-2001 Ken Silverman * Ken Silverman's official web site: "http://www.advsys.net/ken" */ #define fltype double /* define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here */ /* #include typedef uintptr_t Bitu; typedef intptr_t Bits; typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; typedef int8_t Bit8s; */ typedef UINT32 Bitu; typedef INT32 Bits; typedef UINT32 Bit32u; typedef INT32 Bit32s; typedef UINT16 Bit16u; typedef INT16 Bit16s; typedef UINT8 Bit8u; typedef INT8 Bit8s; /* define attribution that inlines/forces inlining of a function (optional) */ #define OPL_INLINE INLINE #undef NUM_CHANNELS #if defined(OPLTYPE_IS_OPL3) #define NUM_CHANNELS 18 #else #define NUM_CHANNELS 9 #endif #define MAXOPERATORS (NUM_CHANNELS*2) #define FL05 ((fltype)0.5) #define FL2 ((fltype)2.0) #define PI ((fltype)3.1415926535897932384626433832795) #define FIXEDPT 0x10000 // fixed-point calculations using 16+16 #define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 #define WAVEPREC 1024 // waveform precision (10 bits) //#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip #if defined(OPLTYPE_IS_OPL3) #define INTFREQU ((fltype)(OPL->chip_clock / 288.0)) // clocking of the chip #else #define INTFREQU ((fltype)(OPL->chip_clock / 72.0)) // clocking of the chip #endif #define OF_TYPE_ATT 0 #define OF_TYPE_DEC 1 #define OF_TYPE_REL 2 #define OF_TYPE_SUS 3 #define OF_TYPE_SUS_NOKEEP 4 #define OF_TYPE_OFF 5 #define ARC_CONTROL 0x00 #define ARC_TVS_KSR_MUL 0x20 #define ARC_KSL_OUTLEV 0x40 #define ARC_ATTR_DECR 0x60 #define ARC_SUSL_RELR 0x80 #define ARC_FREQ_NUM 0xa0 #define ARC_KON_BNUM 0xb0 #define ARC_PERC_MODE 0xbd #define ARC_FEEDBACK 0xc0 #define ARC_WAVE_SEL 0xe0 #define ARC_SECONDSET 0x100 // second operator set for OPL3 #define OP_ACT_OFF 0x00 #define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) #define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) #define BLOCKBUF_SIZE 512 // vibrato constants #define VIBTAB_SIZE 8 #define VIBFAC 70/50000 // no braces, integer mul/div // tremolo constants and table #define TREMTAB_SIZE 53 #define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz /* operator struct definition For OPL2 all 9 channels consist of two operators each, carrier and modulator. Channel x has operators x as modulator and operators (9+x) as carrier. For OPL3 all 18 channels consist either of two operators (2op mode) or four operators (4op mode) which is determined through register4 of the second adlib register set. Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as 4op channels. The two additional operators for a channel y come from the 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op channel. */ typedef struct operator_struct { Bit32s cval, lastcval; // current output/last output (used for feedback) Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment fltype amp, step_amp; // and amplification (envelope) fltype vol; // volume fltype sustain_level; // sustain level Bit32s mfbi; // feedback amount fltype a0, a1, a2, a3; // attack rate function coefficients fltype decaymul, releasemul; // decay/release rate functions Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) Bit32u toff; Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations Bit16s* cur_wform; // start of selected waveform Bit32u cur_wmask; // mask for selected waveform Bit32u act_state; // activity state (regular, percussion) bool sus_keep; // keep sustain level when decay finished bool vibrato,tremolo; // vibrato/tremolo enable bits // variables used to provide non-continuous envelopes Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed Bits cur_env_step; // current (standardized) sample position Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) #if defined(OPLTYPE_IS_OPL3) bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel Bit32s left_pan,right_pan; // opl3 stereo panning amount #endif } op_type; typedef struct opl_chip { // per-chip variables //Bitu chip_num; op_type op[MAXOPERATORS]; Bit8u MuteChn[NUM_CHANNELS + 5]; Bitu chip_clock; Bits int_samplerate; Bit8u status; Bit32u opl_index; Bits opl_addr; #if defined(OPLTYPE_IS_OPL3) Bit8u adlibreg[512]; // adlib register set (including second set) Bit8u wave_sel[44]; // waveform selection #else Bit8u adlibreg[256]; // adlib register set Bit8u wave_sel[22]; // waveform selection #endif // vibrato/tremolo increment/counter Bit32u vibtab_pos; Bit32u vibtab_add; Bit32u tremtab_pos; Bit32u tremtab_add; Bit32u generator_add; // should be a chip parameter fltype recipsamp; // inverse of sampling rate fltype frqmul[16]; ADL_UPDATEHANDLER UpdateHandler; // stream update handler void* UpdateParam; // stream update parameter } OPL_DATA; // enable an operator static void enable_operator(OPL_DATA* chip, Bitu regbase, op_type* op_pt, Bit32u act_type); // functions to change parameters of an operator static void change_frequency(OPL_DATA* chip, Bitu chanbase, Bitu regbase, op_type* op_pt); static void change_attackrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_decayrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_releaserate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_sustainlevel(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_waveform(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_keepsustain(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_vibrato(OPL_DATA* chip, Bitu regbase, op_type* op_pt); static void change_feedback(OPL_DATA* chip, Bitu chanbase, op_type* op_pt); // general functions /*void* adlib_init(Bitu clock, Bit32u samplerate); void adlib_writeIO(void *chip, Bitu addr, Bit8u val); void adlib_write(void *chip, Bitu idx, Bit8u val); //void adlib_getsample(Bit16s* sndptr, Bits numsamples); void adlib_getsample(void *chip, Bit32s** sndptr, Bits numsamples); Bitu adlib_reg_read(void *chip, Bitu port); void adlib_write_index(void *chip, Bitu port, Bit8u val);*/ static void adlib_write(void *chip, Bitu idx, Bit8u val); ================================================ FILE: VGMPlay/chips/opll.c ================================================ /* * Copyright (C) 2019 Nuke.YKT * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * Yamaha YM2413 emulator * Thanks: * siliconpr0n.org(digshadow, John McMaster): * VRC VII decap and die shot. * * version: 1.0 */ #include #include "opll.h" enum { eg_num_attack = 0, eg_num_decay = 1, eg_num_sustain = 2, eg_num_release = 3 }; enum { rm_num_bd0 = 0, rm_num_hh = 1, rm_num_tom = 2, rm_num_bd1 = 3, rm_num_sd = 4, rm_num_tc = 5 }; /* logsin table */ static const uint16_t logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; /* exp table */ static const uint16_t exprom[256] = { 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 }; static const opll_patch_t patch_ds1001[opll_patch_max] = { { 0x05, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x08 },{ 0x08, 0x01 },{ 0x04, 0x02 },{ 0x02, 0x07 } }, { 0x14, 0x00, 0x01, 0x05,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x0f },{ 0x08, 0x06 },{ 0x02, 0x01 },{ 0x03, 0x02 } }, { 0x08, 0x00, 0x01, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0b },{ 0x0a, 0x02 },{ 0x02, 0x01 },{ 0x00, 0x02 } }, { 0x0c, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x06, 0x02 },{ 0x01, 0x07 } }, { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x01, 0x06 },{ 0x00, 0x02 },{ 0x01, 0x08 } }, { 0x06, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x0e },{ 0x03, 0x02 },{ 0x0f, 0x0f },{ 0x04, 0x04 } }, { 0x1d, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x07 } }, { 0x22, 0x01, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x07 },{ 0x02, 0x02 },{ 0x00, 0x01 },{ 0x01, 0x07 } }, { 0x25, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x05, 0x01 },{ 0x00, 0x00 },{ 0x04, 0x07 },{ 0x00, 0x03 },{ 0x07, 0x00 },{ 0x02, 0x01 } }, { 0x0f, 0x00, 0x01, 0x07,{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x00 },{ 0x05, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x0a },{ 0x08, 0x05 },{ 0x05, 0x00 },{ 0x01, 0x02 } }, { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x02, 0x02 } }, { 0x11, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x03 },{ 0x00, 0x00 },{ 0x06, 0x07 },{ 0x05, 0x04 },{ 0x01, 0x01 },{ 0x08, 0x06 } }, { 0x13, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x02 },{ 0x03, 0x00 },{ 0x0c, 0x09 },{ 0x09, 0x05 },{ 0x00, 0x00 },{ 0x03, 0x02 } }, { 0x0c, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x03 },{ 0x00, 0x00 },{ 0x09, 0x0c },{ 0x04, 0x00 },{ 0x03, 0x0f },{ 0x03, 0x06 } }, { 0x0d, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x01, 0x02 },{ 0x00, 0x00 },{ 0x0c, 0x0d },{ 0x01, 0x05 },{ 0x05, 0x00 },{ 0x06, 0x06 } }, { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; static const opll_patch_t patch_ym2413[opll_patch_max] = { { 0x1e, 0x01, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x07 },{ 0x00, 0x08 },{ 0x00, 0x01 },{ 0x00, 0x07 } }, { 0x1a, 0x00, 0x01, 0x05,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0d, 0x0f },{ 0x08, 0x07 },{ 0x02, 0x01 },{ 0x03, 0x03 } }, { 0x19, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0c },{ 0x02, 0x04 },{ 0x01, 0x02 },{ 0x01, 0x03 } }, { 0x0e, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x07, 0x02 },{ 0x00, 0x07 } }, { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x06 },{ 0x00, 0x02 },{ 0x00, 0x08 } }, { 0x16, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x02 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x08 } }, { 0x1d, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x07 } }, { 0x2d, 0x01, 0x00, 0x04,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x03, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x07 },{ 0x02, 0x02 },{ 0x00, 0x00 },{ 0x00, 0x07 } }, { 0x1b, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x06, 0x06 },{ 0x04, 0x05 },{ 0x01, 0x01 },{ 0x00, 0x07 } }, { 0x0b, 0x01, 0x01, 0x00,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x0f },{ 0x05, 0x07 },{ 0x07, 0x00 },{ 0x01, 0x07 } }, { 0x03, 0x01, 0x00, 0x01,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x0a, 0x04 },{ 0x01, 0x00 },{ 0x00, 0x04 } }, { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x01 },{ 0x02, 0x02 } }, { 0x0c, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x0f },{ 0x02, 0x05 },{ 0x02, 0x04 },{ 0x00, 0x02 } }, { 0x15, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x0c, 0x09 },{ 0x09, 0x05 },{ 0x00, 0x00 },{ 0x03, 0x02 } }, { 0x09, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0e },{ 0x01, 0x04 },{ 0x04, 0x01 },{ 0x00, 0x03 } }, { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; static const opll_patch_t patch_ymf281[opll_patch_max] = { { 0x1a, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x06 },{ 0x0f, 0x07 },{ 0x00, 0x01 },{ 0x00, 0x06 } }, { 0x05, 0x00, 0x00, 0x01,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x0f, 0x08 },{ 0x06, 0x03 },{ 0x08, 0x00 },{ 0x00, 0x03 } }, { 0x16, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x03, 0x01 },{ 0x02, 0x00 },{ 0x0f, 0x0d },{ 0x02, 0x03 },{ 0x01, 0x00 },{ 0x01, 0x03 } }, { 0x0b, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x06 },{ 0x08, 0x04 },{ 0x07, 0x01 },{ 0x00, 0x07 } }, { 0x1e, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x02, 0x01 },{ 0x00, 0x00 },{ 0x0e, 0x07 },{ 0x01, 0x06 },{ 0x00, 0x02 },{ 0x00, 0x08 } }, { 0x02, 0x00, 0x01, 0x06,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x02, 0x00 },{ 0x09, 0x06 },{ 0x0a, 0x01 },{ 0x02, 0x02 },{ 0x00, 0x07 } }, { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x08, 0x08 },{ 0x04, 0x03 },{ 0x01, 0x00 },{ 0x00, 0x07 } }, { 0x0a, 0x00, 0x00, 0x02,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x07, 0x02 },{ 0x03, 0x00 },{ 0x06, 0x06 },{ 0x06, 0x04 },{ 0x04, 0x02 },{ 0x00, 0x07 } }, { 0x07, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0c, 0x07 },{ 0x05, 0x07 },{ 0x05, 0x00 },{ 0x01, 0x07 } }, { 0x1e, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x00 },{ 0x06, 0x01 },{ 0x01, 0x00 },{ 0x0f, 0x0f },{ 0x02, 0x03 },{ 0x0f, 0x0f },{ 0x00, 0x03 } }, { 0x18, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x0e },{ 0x05, 0x03 },{ 0x02, 0x01 },{ 0x00, 0x03 } }, { 0x24, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x07, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x08, 0x08 },{ 0x02, 0x00 },{ 0x02, 0x03 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x00 },{ 0x05, 0x04 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x0f, 0x03 },{ 0x07, 0x0f },{ 0x00, 0x05 } }, { 0x03, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x01 },{ 0x0f, 0x01 },{ 0x00, 0x00 },{ 0x0f, 0x0e },{ 0x0c, 0x03 },{ 0x03, 0x0f },{ 0x0f, 0x0c } }, { 0x00, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0a, 0x01 },{ 0x00, 0x00 },{ 0x0b, 0x08 },{ 0x0f, 0x04 },{ 0x00, 0x0f },{ 0x00, 0x05 } }, { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; static const opll_patch_t patch_ym2423[opll_patch_max] = { { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x09, 0x05 },{ 0x04, 0x04 },{ 0x01, 0x00 },{ 0x00, 0x05 } }, { 0x12, 0x00, 0x00, 0x04,{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x01 },{ 0x01, 0x00 },{ 0x0f, 0x0f },{ 0x03, 0x02 },{ 0x0a, 0x0e },{ 0x00, 0x09 } }, { 0x11, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x02 },{ 0x0f, 0x0f },{ 0x02, 0x02 },{ 0x05, 0x07 },{ 0x00, 0x05 } }, { 0x28, 0x00, 0x00, 0x07,{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x02 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x03, 0x02 },{ 0x09, 0x0b },{ 0x00, 0x04 } }, { 0x17, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x02, 0x01 },{ 0x02, 0x00 },{ 0x05, 0x06 },{ 0x01, 0x0f },{ 0x07, 0x00 },{ 0x00, 0x09 } }, { 0x18, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x03, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x07, 0x04 },{ 0x05, 0x08 },{ 0x00, 0x05 } }, { 0x1c, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x05, 0x07 },{ 0x01, 0x01 },{ 0x02, 0x02 },{ 0x00, 0x06 } }, { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x04 },{ 0x00, 0x00 },{ 0x07, 0x03 },{ 0x03, 0x0f },{ 0x00, 0x00 },{ 0x00, 0x06 } }, { 0x0d, 0x00, 0x00, 0x03,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x04, 0x06 },{ 0x02, 0x0f },{ 0x02, 0x00 },{ 0x00, 0x06 } }, { 0x10, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x02 },{ 0x0f, 0x0f },{ 0x03, 0x03 },{ 0x02, 0x00 },{ 0x00, 0x04 } }, { 0x1b, 0x00, 0x00, 0x07,{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x0c, 0x09 },{ 0x05, 0x06 },{ 0x0f, 0x0f },{ 0x00, 0x06 } }, { 0x1b, 0x00, 0x00, 0x00,{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x09, 0x01 },{ 0x03, 0x00 },{ 0x0f, 0x0f },{ 0x05, 0x03 },{ 0x07, 0x0f },{ 0x00, 0x02 } }, { 0x11, 0x00, 0x00, 0x03,{ 0x00, 0x01 },{ 0x01, 0x00 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x02 },{ 0x02, 0x00 },{ 0x09, 0x0b },{ 0x04, 0x01 },{ 0x0e, 0x0f },{ 0x00, 0x07 } }, { 0x17, 0x00, 0x00, 0x06,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x0e },{ 0x03, 0x01 },{ 0x0b, 0x0e },{ 0x00, 0x0b } }, { 0x0d, 0x00, 0x00, 0x05,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x01 },{ 0x01, 0x01 },{ 0x01, 0x06 },{ 0x00, 0x00 },{ 0x0f, 0x0f },{ 0x02, 0x04 },{ 0x02, 0x09 },{ 0x00, 0x09 } }, { 0x18, 0x00, 0x01, 0x07,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0d, 0x00 },{ 0x0f, 0x00 },{ 0x06, 0x00 },{ 0x0a, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; static const uint32_t ch_offset[18] = { 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 0 }; static const uint32_t pg_multi[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; static const uint32_t eg_stephi[4][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 1, 0 } }; static const uint32_t eg_ksltable[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 }; void OPLL_DoIO(opll_t *chip) { /* Write signal check */ chip->write_a_en = (chip->write_a & 0x03) == 0x01; chip->write_d_en = (chip->write_d & 0x03) == 0x01; chip->write_a <<= 1; chip->write_d <<= 1; } void OPLL_DoModeWrite(opll_t *chip) { uint8_t slot; if ((chip->write_mode_address & 0x10) && chip->write_d_en) { slot = chip->write_mode_address & 0x01; switch (chip->write_mode_address & 0x0f) { case 0x00: case 0x01: chip->patch.multi[slot] = chip->write_data & 0x0f; chip->patch.ksr[slot] = (chip->write_data >> 4) & 0x01; chip->patch.et[slot] = (chip->write_data >> 5) & 0x01; chip->patch.vib[slot] = (chip->write_data >> 6) & 0x01; chip->patch.am[slot] = (chip->write_data >> 7) & 0x01; break; case 0x02: chip->patch.ksl[0] = (chip->write_data >> 6) & 0x03; chip->patch.tl = chip->write_data & 0x3f; break; case 0x03: chip->patch.ksl[1] = (chip->write_data >> 6) & 0x03; chip->patch.dc = (chip->write_data >> 4) & 0x01; chip->patch.dm = (chip->write_data >> 3) & 0x01; chip->patch.fb = chip->write_data & 0x07; break; case 0x04: case 0x05: chip->patch.dr[slot] = chip->write_data & 0x0f; chip->patch.ar[slot] = (chip->write_data >> 4) & 0x0f; break; case 0x06: case 0x07: chip->patch.rr[slot] = chip->write_data & 0x0f; chip->patch.sl[slot] = (chip->write_data >> 4) & 0x0f; break; case 0x0e: chip->rhythm = chip->write_data & 0x3f; if (chip->chip_type == opll_type_ds1001) { chip->rhythm |= 0x20; } chip->rm_enable = (chip->rm_enable & 0x7f) | ((chip->rhythm << 2) & 0x80); break; case 0x0f: chip->testmode = chip->write_data & 0x0f; break; } } } void OPLL_Reset(opll_t *chip, uint32_t chip_type, uint32_t rate, uint32_t clock) { uint32_t i; uint32_t rateratio; rateratio = chip->rateratio; memset(chip, 0, sizeof(opll_t)); chip->chip_type = chip_type; if (chip_type == opll_type_ds1001) { /* Rhythm mode is always on */ chip->rhythm = 0x20; chip->rm_enable = (int8_t)0x80; } switch (chip_type) { case opll_type_ds1001: chip->patchrom = patch_ds1001; break; case opll_type_ymf281: case opll_type_ymf281b: chip->patchrom = patch_ymf281; break; case opll_type_ym2423: chip->patchrom = patch_ym2423; break; case opll_type_ym2413: case opll_type_ym2413b: case opll_type_ym2420: default: chip->patchrom = patch_ym2413; break; } for (i = 0; i < 18; i++) { chip->eg_state[i] = eg_num_release; chip->eg_level[i] = 0x7f; chip->eg_out = 0x7f; } chip->rm_select = rm_num_tc + 1; if (rate != 0) { chip->rateratio = (uint32_t)((((uint64_t)72 * rate) << RSM_FRAC) / clock); } else { chip->rateratio = rateratio; } } void OPLL_DoRegWrite(opll_t *chip) { uint32_t channel; /* Address */ if (chip->write_a_en) { if ((chip->write_data & 0xc0) == 0x00) { /* FM Write */ chip->write_fm_address = 1; chip->address = chip->write_data; } else { chip->write_fm_address = 0; } } /* Data */ if (chip->write_fm_address && chip->write_d_en) { chip->data = chip->write_data; } /* Update registers */ if (chip->write_fm_data && !chip->write_a_en) { if ((chip->address & 0x0f) == chip->cycles && chip->cycles < 16) { channel = chip->cycles % 9; switch (chip->address & 0xf0) { case 0x10: if (chip->chip_type == opll_type_ym2420) { chip->fnum[channel] = (chip->fnum[channel] & 0x0f) | ((chip->data & 0x1f) << 4); chip->block[channel] = (chip->data >> 5) & 0x07; } else chip->fnum[channel] = (chip->fnum[channel] & 0x100) | chip->data; break; case 0x20: if (chip->chip_type == opll_type_ym2420) chip->fnum[channel] = (chip->fnum[channel] & 0x1f0) | (chip->data & 0x0f); else { chip->fnum[channel] = (chip->fnum[channel] & 0xff) | ((chip->data & 0x01) << 8); chip->block[channel] = (chip->data >> 1) & 0x07; } chip->kon[channel] = (chip->data >> 4) & 0x01; chip->son[channel] = (chip->data >> 5) & 0x01; break; case 0x30: chip->vol[channel] = chip->data & 0x0f; chip->inst[channel] = (chip->data >> 4) & 0x0f; break; } } } if (chip->write_a_en) { chip->write_fm_data = 0; } if (chip->write_fm_address && chip->write_d_en) { chip->write_fm_data = 1; } if (chip->write_a_en) { if (((chip->write_data & 0xf0) == 0x00)) { chip->write_mode_address = 0x10 | (chip->write_data & 0x0f); } else { chip->write_mode_address = 0x00; } } } void OPLL_PreparePatch1(opll_t *chip) { uint8_t instr; uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01; uint32_t instr_index; uint32_t ch = ch_offset[chip->cycles]; const opll_patch_t *patch; instr = chip->inst[ch]; if (instr > 0) { instr_index = opll_patch_1 + instr - 1; } if (chip->rm_select <= rm_num_tc) { instr_index = opll_patch_drum_0 + chip->rm_select; } if (chip->rm_select <= rm_num_tc || instr > 0) { patch = &chip->patchrom[instr_index]; } else { patch = &chip->patch; } if (chip->rm_select == rm_num_hh || chip->rm_select == rm_num_tom) { chip->c_tl = chip->inst[ch] << 2; } else if (mcsel == 1) { chip->c_tl = chip->vol[ch] << 2; } else { chip->c_tl = patch->tl; } chip->c_adrr[0] = patch->ar[mcsel]; chip->c_adrr[1] = patch->dr[mcsel]; chip->c_adrr[2] = patch->rr[mcsel]; chip->c_et = patch->et[mcsel]; chip->c_ksr = patch->ksr[mcsel]; chip->c_ksl = patch->ksl[mcsel]; chip->c_ksr_freq = (chip->block[ch] << 1) | (chip->fnum[ch] >> 8); chip->c_ksl_freq = (chip->fnum[ch]>>5); chip->c_ksl_block = (chip->block[ch]); } void OPLL_PreparePatch2(opll_t *chip) { uint8_t instr; uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01; uint32_t instr_index; const opll_patch_t *patch; instr = chip->inst[ch_offset[chip->cycles]]; if (instr > 0) { instr_index = opll_patch_1 + instr - 1; } if (chip->rm_select <= rm_num_tc) { instr_index = opll_patch_drum_0 + chip->rm_select; } if (chip->rm_select <= rm_num_tc || instr > 0) { patch = &chip->patchrom[instr_index]; } else { patch = &chip->patch; } chip->c_fnum = chip->fnum[ch_offset[chip->cycles]]; chip->c_block = chip->block[ch_offset[chip->cycles]]; chip->c_multi = patch->multi[mcsel]; chip->c_sl = patch->sl[mcsel]; chip->c_fb = patch->fb; chip->c_vib = patch->vib[mcsel]; chip->c_am = patch->am[mcsel]; chip->c_dc <<= 1; chip->c_dm <<= 1; chip->c_dc |= patch->dc; chip->c_dm |= patch->dm; } void OPLL_PhaseGenerate(opll_t *chip) { uint32_t ismod; uint32_t phase; uint8_t rm_bit; uint16_t pg_out; chip->pg_phase[(chip->cycles + 17) % 18] = chip->pg_phase_next + chip->pg_inc; if ((chip->rm_enable & 0x40) && (chip->cycles == 13 || chip->cycles == 14)) { ismod = 0; } else { ismod = ((chip->cycles + 3) / 3) & 1; } phase = chip->pg_phase[chip->cycles]; /* KeyOn event check */ if ((chip->testmode & 0x04) || (ismod && (chip->eg_dokon & 0x8000)) || (!ismod && (chip->eg_dokon & 0x01))) { chip->pg_phase_next = 0; } else { chip->pg_phase_next = phase; } /* Rhythm mode */ if (chip->cycles == 13) { chip->rm_hh_bit2 = (phase >> (2 + 9)) & 1; chip->rm_hh_bit3 = (phase >> (3 + 9)) & 1; chip->rm_hh_bit7 = (phase >> (7 + 9)) & 1; chip->rm_hh_bit8 = (phase >> (8 + 9)) & 1; } else if (chip->cycles == 17 && (chip->rm_enable & 0x80)) { chip->rm_tc_bit3 = (phase >> (3 + 9)) & 1; chip->rm_tc_bit5 = (phase >> (5 + 9)) & 1; } if ((chip->rm_enable & 0x80)) { switch (chip->cycles) { case 13: /* HH */ rm_bit = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); pg_out = rm_bit << 9; if (rm_bit ^ (chip->rm_noise & 1)) { pg_out |= 0xd0; } else { pg_out |= 0x34; } break; case 16: /* SD */ pg_out = (chip->rm_hh_bit8 << 9) | ((chip->rm_hh_bit8 ^ (chip->rm_noise & 1)) << 8); break; case 17: /* TC */ rm_bit = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); pg_out = (rm_bit << 9) | 0x100; break; default: pg_out = phase >> 9; } } else { pg_out = phase >> 9; } chip->pg_out = pg_out; } void OPLL_PhaseCalcIncrement(opll_t *chip) { uint32_t freq; uint16_t block; freq = chip->c_fnum << 1; block = chip->c_block; /* Apply vibrato */ if (chip->c_vib) { switch (chip->lfo_vib_counter) { case 0: case 4: break; case 1: case 3: freq += freq >> 8; break; case 2: freq += freq >> 7; break; case 5: case 7: freq -= freq >> 8; break; case 6: freq -= freq >> 7; break; } } /* Apply block */ freq = (freq << block) >> 1; chip->pg_inc = (freq * pg_multi[chip->c_multi]) >> 1; } void OPLL_EnvelopeKSLTL(opll_t *chip) { int32_t ksl; ksl = eg_ksltable[chip->c_ksl_freq]-((8-chip->c_ksl_block)<<3); if (ksl < 0) { ksl = 0; } ksl <<= 1; if (chip->c_ksl) { ksl = ksl >> (3-chip->c_ksl); } else { ksl = 0; } chip->eg_ksltl = ksl + (chip->c_tl<<1); } void OPLL_EnvelopeOutput(opll_t *chip) { int32_t level = chip->eg_level[(chip->cycles+17)%18]; level += chip->eg_ksltl; if (chip->c_am) { level += chip->lfo_am_out; } if (level >= 128) { level = 127; } if (chip->testmode & 0x01) { level = 0; } chip->eg_out = level; } void OPLL_EnvelopeGenerate(opll_t *chip) { uint8_t timer_inc; uint8_t timer_bit; uint8_t timer_low; uint8_t rate; uint8_t state_rate; uint8_t ksr; uint8_t sum; uint8_t rate_hi; uint8_t rate_lo; int32_t level; int32_t next_level; uint8_t zero; uint8_t state; uint8_t next_state; int32_t step; int32_t sl; uint32_t mcsel = ((chip->cycles + 1) / 3) & 0x01; /* EG timer */ if ((chip->eg_counter_state & 3) != 3) { timer_inc = 0; } else if (chip->cycles == 0) { timer_inc = 1; } else { timer_inc = chip->eg_timer_carry; } timer_low = chip->eg_timer & 3; timer_bit = chip->eg_timer & 1; timer_bit += timer_inc; chip->eg_timer_carry = timer_bit >> 1; chip->eg_timer = ((timer_bit & 1) << 17) | (chip->eg_timer >> 1); if (chip->testmode & 0x08) { chip->eg_timer &= 0x2ffff; chip->eg_timer |= (chip->write_data << (16 - 2)) & 0x10000; } if (!chip->eg_timer_shift_stop && ((chip->eg_timer >> 16) & 1)) { chip->eg_timer_shift = chip->cycles; } if (chip->cycles == 0 && (chip->eg_counter_state_prev & 1) == 1) { chip->eg_timer_low_lock = timer_low; chip->eg_timer_shift_lock = chip->eg_timer_shift; if (chip->eg_timer_shift_lock > 13) chip->eg_timer_shift_lock = 0; chip->eg_timer_shift = 0; } chip->eg_timer_shift_stop |= (chip->eg_timer >> 16) & 1; if (chip->cycles == 0) { chip->eg_timer_shift_stop = 0; } chip->eg_counter_state_prev = chip->eg_counter_state; if (chip->cycles == 17) { chip->eg_counter_state++; } level = chip->eg_level[(chip->cycles+16)%18]; next_level = level; zero = level == 0; chip->eg_silent = level == 0x7f; if (chip->eg_state[(chip->cycles+16)%18] != eg_num_attack && (chip->eg_off&2) && !(chip->eg_dokon&2)) { next_level = 0x7f; } if (chip->eg_maxrate && (chip->eg_dokon&2)) { next_level = 0x00; } state = chip->eg_state[(chip->cycles+16)%18]; next_state = eg_num_attack; step = 0; sl = chip->eg_sl; switch (state) { case eg_num_attack: if (!chip->eg_maxrate && (chip->eg_kon & 2) && !zero) { int32_t shift = chip->eg_rate_hi - 11 + chip->eg_inc_hi; if (chip->eg_inc_lo) { shift = 1; } if (shift > 0) { if (shift > 4) shift = 4; step = ~level >> (5 - shift); } } if (zero) { next_state = eg_num_decay; } else { next_state = eg_num_attack; } break; case eg_num_decay: if (!(chip->eg_off & 2) && !(chip->eg_dokon & 2) && (level >> 3) != sl) { uint8_t i0 = chip->eg_rate_hi == 15 || (chip->eg_rate_hi == 14 && chip->eg_inc_hi); uint8_t i1 = (chip->eg_rate_hi == 14 && !chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && !chip->eg_inc_hi && (chip->eg_counter_state_prev & 1)) || (chip->eg_rate_hi == 12 && chip->eg_inc_hi && (chip->eg_counter_state_prev & 1)) || (chip->eg_rate_hi == 12 && !chip->eg_inc_hi && ((chip->eg_counter_state_prev & 3) == 3)) || (chip->eg_inc_lo && ((chip->eg_counter_state_prev & 3) == 3)); step = (i0<<1) | i1; } if ((level >> 3) == sl) { next_state = eg_num_sustain; } else { next_state = eg_num_decay; } break; case eg_num_sustain: case eg_num_release: if (!(chip->eg_off & 2) && !(chip->eg_dokon & 2)) { uint8_t i0 = chip->eg_rate_hi == 15 || (chip->eg_rate_hi == 14 && chip->eg_inc_hi); uint8_t i1 = (chip->eg_rate_hi == 14 && !chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && chip->eg_inc_hi) || (chip->eg_rate_hi == 13 && !chip->eg_inc_hi && (chip->eg_counter_state_prev & 1)) || (chip->eg_rate_hi == 12 && chip->eg_inc_hi && (chip->eg_counter_state_prev & 1)) || (chip->eg_rate_hi == 12 && !chip->eg_inc_hi && ((chip->eg_counter_state_prev & 3) == 3)) || (chip->eg_inc_lo && ((chip->eg_counter_state_prev & 3) == 3)); step = (i0<<1) | i1; } next_state = state; break; } if (!(chip->eg_kon & 2)) { next_state = eg_num_release; } if (chip->eg_dokon & 2) { next_state = eg_num_attack; } chip->eg_level[(chip->cycles+16)%18] = next_level+step; chip->eg_state[(chip->cycles+16)%18] = next_state; rate_hi = chip->eg_rate >> 2; rate_lo = chip->eg_rate & 3; chip->eg_inc_hi = eg_stephi[rate_lo][chip->eg_timer_low_lock]; sum = (chip->eg_timer_shift_lock + rate_hi) & 0x0f; chip->eg_inc_lo = 0; if (rate_hi < 12 && !chip->eg_zerorate) { switch (sum) { case 12: chip->eg_inc_lo = 1; break; case 13: chip->eg_inc_lo = (rate_lo >> 1) & 1; break; case 14: chip->eg_inc_lo = rate_lo & 1; break; } } chip->eg_maxrate = rate_hi == 0x0f; chip->eg_rate_hi = rate_hi; chip->eg_kon <<= 1; chip->eg_kon |= chip->kon[ch_offset[chip->cycles]]; chip->eg_off <<= 1; chip->eg_off |= (chip->eg_level[chip->cycles] >> 2) == 0x1f; switch (chip->rm_select) { case rm_num_bd0: case rm_num_bd1: chip->eg_kon |= (chip->rhythm >> 4) & 1; break; case rm_num_sd: chip->eg_kon |= (chip->rhythm >> 3) & 1; break; case rm_num_tom: chip->eg_kon |= (chip->rhythm >> 2) & 1; break; case rm_num_tc: chip->eg_kon |= (chip->rhythm >> 1) & 1; break; case rm_num_hh: chip->eg_kon |= chip->rhythm & 1; break; } /* Calculate rate */ rate = 0; chip->eg_dokon <<= 1; state_rate = chip->eg_state[chip->cycles]; if (state_rate == eg_num_release && (chip->eg_kon&1) && (chip->eg_off&1)) { state_rate = eg_num_attack; chip->eg_dokon |= 1; } switch (state_rate) { case eg_num_attack: rate = chip->c_adrr[0]; break; case eg_num_decay: rate = chip->c_adrr[1]; break; case eg_num_sustain: if (!chip->c_et) { rate = chip->c_adrr[2]; } break; case eg_num_release: if (chip->son[ch_offset[chip->cycles]]) { rate = 5; } else { rate = chip->c_adrr[2]; } break; } if (!(chip->eg_kon&1) && !mcsel && chip->rm_select != rm_num_tom && chip->rm_select != rm_num_hh) { rate = 0; } if ((chip->eg_kon&1) && chip->eg_state[chip->cycles] == eg_num_release && !(chip->eg_off&1)) { rate = 12; } if (!(chip->eg_kon&1) && !chip->son[ch_offset[chip->cycles]] && mcsel == 1 && !chip->c_et) { rate = 7; } chip->eg_zerorate = rate == 0; ksr = chip->c_ksr_freq; if (!chip->c_ksr) ksr >>= 2; chip->eg_rate = (rate << 2) + ksr; if (chip->eg_rate & 0x40) { chip->eg_rate = 0x3c | (ksr & 3); } chip->eg_sl = chip->c_sl; } void OPLL_Channel(opll_t *chip) { int16_t sign; int16_t ch_out = chip->ch_out; uint8_t ismod = (chip->cycles / 3) & 1; uint8_t mute_m = ismod || ((chip->rm_enable&0x40) && (chip->cycles+15)%18 >= 12); uint8_t mute_r = 1; if (chip->chip_type == opll_type_ds1001) { chip->output_m = ch_out; if (chip->output_m >= 0) { chip->output_m++; } if (mute_m) { chip->output_m = 0; } chip->output_r = 0; return; } else { /* TODO: This might be incorrect */ if ((chip->rm_enable & 0x40)) { switch (chip->cycles) { case 16: /* HH */ case 17: /* TOM */ case 0: /* BD */ case 1: /* SD */ case 2: /* TC */ case 3: /* HH */ case 4: /* TOM */ case 5: /* BD */ case 9: /* TOM */ case 10: /* TOM */ mute_r = 0; break; } } if (chip->chip_type == opll_type_ym2413b || chip->chip_type == opll_type_ymf281b) { if (mute_m) chip->output_m = 0; else chip->output_m = ch_out; if (mute_r) chip->output_r = 0; else chip->output_r = ch_out; } else { sign = ch_out >> 8; if (ch_out >= 0) { ch_out++; sign++; } if (mute_m) chip->output_m = sign; else chip->output_m = ch_out; if (mute_r) chip->output_r = sign; else chip->output_r = ch_out; } } } void OPLL_Operator(opll_t *chip) { uint8_t ismod1, ismod2, ismod3; uint32_t op_mod; uint16_t exp_shift; int16_t output; uint32_t level; uint32_t phase; int16_t routput; if ((chip->rm_enable & 0x80) && (chip->cycles == 15 || chip->cycles == 16)) { ismod1 = 0; } else { ismod1 = ((chip->cycles + 1) / 3) & 1; } if ((chip->rm_enable & 0x40) && (chip->cycles == 13 || chip->cycles == 14)) { ismod2 = 0; } else { ismod2 = ((chip->cycles + 3) / 3) & 1; } if ((chip->rm_enable & 0x40) && (chip->cycles == 16 || chip->cycles == 17)) { ismod3 = 0; } else { ismod3 = (chip->cycles / 3) & 1; } op_mod = 0; if (ismod3) { op_mod |= chip->op_mod << 1; } if (ismod2 && chip->c_fb) { op_mod |= chip->op_fbsum >> (7 - chip->c_fb); } exp_shift = chip->op_exp_s; if (chip->eg_silent || ((chip->op_neg&2) && (ismod1 ? (chip->c_dm&4) : (chip->c_dc&4)))) { exp_shift |= 12; } output = chip->op_exp_m>>exp_shift; if (!chip->eg_silent && (chip->op_neg&2)) { output = ~output; } level = chip->op_logsin+(chip->eg_out<<4); if (level >= 4096) { level = 4095; } chip->op_exp_m = exprom[level & 0xff]; chip->op_exp_s = level >> 8; phase = (op_mod + chip->pg_out) & 0x3ff; if (phase & 0x100) { phase ^= 0xff; } chip->op_logsin = logsinrom[phase & 0xff]; chip->op_neg <<= 1; chip->op_neg |= phase >> 9; chip->op_fbsum = (chip->op_fb1[(chip->cycles + 3) % 9] + chip->op_fb2[(chip->cycles + 3) % 9]) >> 1; if (ismod1) { chip->op_fb2[chip->cycles%9] = chip->op_fb1[chip->cycles%9]; chip->op_fb1[chip->cycles%9] = output; } chip->op_mod = output&0x1ff; if (chip->chip_type == opll_type_ds1001) { routput = 0; } else { switch (chip->cycles) { case 2: routput = chip->ch_out_hh; break; case 3: routput = chip->ch_out_tm; break; case 4: routput = chip->ch_out_bd; break; case 8: routput = chip->ch_out_sd; break; case 9: routput = chip->ch_out_tc; break; default: routput = 0; /* TODO: Not quite true */ break; } switch (chip->cycles) { case 15: chip->ch_out_hh = output>>3; break; case 16: chip->ch_out_tm = output>>3; break; case 17: chip->ch_out_bd = output>>3; break; case 0: chip->ch_out_sd = output>>3; break; case 1: chip->ch_out_tc = output>>3; break; default: break; } } chip->ch_out = ismod1 ? routput : (output>>3); } void OPLL_DoRhythm(opll_t *chip) { uint8_t nbit; /* Noise */ nbit = (chip->rm_noise ^ (chip->rm_noise >> 14)) & 0x01; nbit |= (chip->rm_noise == 0x00) | ((chip->testmode >> 1) & 0x01); chip->rm_noise = (nbit << 22) | (chip->rm_noise >> 1); } void OPLL_DoLFO(opll_t *chip) { uint8_t vib_step; uint8_t am_inc = 0; uint8_t am_bit; /* Update counter */ if (chip->cycles == 17) { vib_step = ((chip->lfo_counter & 0x3ff) + 1) >> 10; chip->lfo_am_step = ((chip->lfo_counter & 0x3f) + 1) >> 6; vib_step |= (chip->testmode >> 3) & 0x01; chip->lfo_vib_counter += vib_step; chip->lfo_vib_counter &= 0x07; chip->lfo_counter++; } /* LFO AM */ if ((chip->lfo_am_step || (chip->testmode & 0x08)) && chip->cycles < 9) { am_inc = chip->lfo_am_dir | (chip->cycles == 0); } if (chip->cycles >= 9) { chip->lfo_am_car = 0; } if (chip->cycles == 0) { if (chip->lfo_am_dir && (chip->lfo_am_counter & 0x7f) == 0) { chip->lfo_am_dir = 0; } else if (!chip->lfo_am_dir && (chip->lfo_am_counter & 0x69) == 0x69) { chip->lfo_am_dir = 1; } } am_bit = chip->lfo_am_counter & 0x01; am_bit += am_inc + chip->lfo_am_car; chip->lfo_am_car = am_bit >> 1; am_bit &= 0x01; chip->lfo_am_counter = (am_bit << 8) | (chip->lfo_am_counter >> 1); /* Reset LFO */ if (chip->testmode & 0x02) { chip->lfo_vib_counter = 0; chip->lfo_counter = 0; chip->lfo_am_dir = 0; chip->lfo_am_counter &= 0xff; } } void OPLL_Clock(opll_t *chip, int32_t *buffer) { buffer[0] = chip->output_m; buffer[1] = chip->output_r; if (chip->cycles == 0) { chip->lfo_am_out = (chip->lfo_am_counter >> 3) & 0x0f; } chip->rm_enable >>= 1; OPLL_DoModeWrite(chip); chip->rm_select++; if (chip->rm_select > rm_num_tc) { chip->rm_select = rm_num_tc + 1; } if (chip->cycles == 11 && (chip->rm_enable & 0x80) == 0x80) { chip->rm_select = rm_num_bd0; } OPLL_PreparePatch1(chip); OPLL_Channel(chip); OPLL_PhaseGenerate(chip); OPLL_Operator(chip); OPLL_PhaseCalcIncrement(chip); OPLL_EnvelopeOutput(chip); OPLL_EnvelopeKSLTL(chip); OPLL_EnvelopeGenerate(chip); OPLL_DoLFO(chip); OPLL_DoRhythm(chip); OPLL_PreparePatch2(chip); OPLL_DoRegWrite(chip); OPLL_DoIO(chip); chip->cycles = (chip->cycles + 1) % 18; } void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data) { chip->write_data = data; if (port & 1) { /* Data */ chip->write_d |= 1; } else { /* Address */ chip->write_a |= 1; } } void OPLL_WriteBuffered(opll_t* chip, uint32_t port, uint8_t data) { uint64_t time1, time2; int32_t buffer[2]; uint64_t skip; if (chip->writebuf[chip->writebuf_last].port & 0x04) { OPLL_Write(chip, chip->writebuf[chip->writebuf_last].port & 0x03, chip->writebuf[chip->writebuf_last].data); chip->writebuf_cur = (chip->writebuf_last + 1) % OPLL_WRITEBUF_SIZE; skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; while (skip--) { OPLL_Clock(chip, buffer); } } chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; chip->writebuf[chip->writebuf_last].data = data; time1 = chip->writebuf_lasttime + OPLL_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; if (time1 < time2) { time1 = time2; } chip->writebuf[chip->writebuf_last].time = time1; chip->writebuf_lasttime = time1; chip->writebuf_last = (chip->writebuf_last + 1) % OPLL_WRITEBUF_SIZE; } #define OUTPUT_FACTOR 8 void OPLL_GenerateResampled(opll_t *chip, int32_t *buf) { uint32_t i; int32_t buffer[2]; uint32_t mute_m, mute_r; int32_t sum; while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; chip->samples[0] = chip->samples[1] = 0; for (i = 0; i < 18; i++) { switch (chip->cycles) { case 1: mute_m = chip->mute[6]; mute_r = chip->mute[9]; break; case 2: mute_m = chip->mute[7]; mute_r = chip->mute[10]; break; case 3: mute_m = chip->mute[8]; mute_r = chip->mute[12]; break; case 4: mute_m = 0; mute_r = chip->mute[13]; break; case 5: mute_m = 0; mute_r = chip->mute[11]; break; case 6: mute_m = 0; mute_r = chip->mute[9]; break; case 7: mute_m = chip->mute[0]; mute_r = 0; break; case 8: mute_m = chip->mute[1]; mute_r = 0; break; case 9: mute_m = chip->mute[2]; mute_r = 0; break; case 10: mute_m = 0; mute_r = chip->mute[10]; break; case 11: mute_m = 0; mute_r = chip->mute[12]; break; case 12: mute_m = 0; mute_r = 0; break; case 13: mute_m = chip->mute[3]; mute_r = 0; break; case 14: mute_m = chip->mute[4]; mute_r = 0; break; case 15: mute_m = chip->mute[5]; mute_r = 0; break; case 16: mute_m = 0; mute_r = 0; break; case 17: mute_m = 0; mute_r = chip->mute[13]; break; case 0: mute_m = 0; mute_r = chip->mute[11]; break; default: mute_m = 0; mute_r = 0; break; } OPLL_Clock(chip, buffer); if (!mute_m) { chip->samples[0] += buffer[0]; } if (!mute_r) { chip->samples[1] += buffer[1]; } while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) { if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) { break; } chip->writebuf[chip->writebuf_cur].port &= 0x03; OPLL_Write(chip, chip->writebuf[chip->writebuf_cur].port, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPLL_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } chip->samples[0] *= OUTPUT_FACTOR; chip->samples[1] *= OUTPUT_FACTOR; sum = chip->samples[0] + chip->samples[1]; chip->samples[1] = chip->samples[0] = sum; chip->samplecnt -= chip->rateratio; } buf[0] = (int32_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + chip->samples[0] * chip->samplecnt) / chip->rateratio); buf[1] = (int32_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + chip->samples[1] * chip->samplecnt) / chip->rateratio); chip->samplecnt += 1 << RSM_FRAC; } void OPLL_GenerateStream(opll_t* chip, int32_t** sndptr, uint32_t numsamples) { uint32_t i; int32_t* smpl, * smpr; int32_t buffer[2]; smpl = sndptr[0]; smpr = sndptr[1]; for (i = 0; i < numsamples; i++) { OPLL_GenerateResampled(chip, buffer); *smpl++ = buffer[0]; *smpr++ = buffer[1]; } } void OPLL_SetMute(opll_t* chip, uint32_t mute) { uint32_t i; for (i = 0; i < 14; i++) { chip->mute[i] = (mute >> i) & 0x01; } } ================================================ FILE: VGMPlay/chips/opll.h ================================================ /* * Copyright (C) 2019 Nuke.YKT * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * Yamaha YM2413 emulator * Thanks: * siliconpr0n.org(digshadow, John McMaster): * VRC VII decap and die shot. * * version: 1.0 */ #ifndef OPLL_H #define OPLL_H #include "emutypes.h" #define RSM_FRAC 10 #define OPLL_WRITEBUF_SIZE 2048 #define OPLL_WRITEBUF_DELAY 20 enum { opll_type_ym2413 = 0x00, /* Yamaha YM2413 */ opll_type_ds1001, /* Konami VRC VII */ opll_type_ym2413b, /* Yamaha YM2413B */ opll_type_ymf281, /* Yamaha YMF281 */ opll_type_ymf281b, /* Yamaha YMF281B */ opll_type_ym2420, /* Yamaha YM2420 */ opll_type_ym2423, /* Yamaha YM2423 */ }; enum { opll_patch_1 = 0x00, opll_patch_2, opll_patch_3, opll_patch_4, opll_patch_5, opll_patch_6, opll_patch_7, opll_patch_8, opll_patch_9, opll_patch_10, opll_patch_11, opll_patch_12, opll_patch_13, opll_patch_14, opll_patch_15, opll_patch_drum_0, opll_patch_drum_1, opll_patch_drum_2, opll_patch_drum_3, opll_patch_drum_4, opll_patch_drum_5, opll_patch_max }; typedef struct _opll_writebuf { uint64_t time; uint8_t port; uint8_t data; } opll_writebuf; typedef struct { uint8_t tl; uint8_t dc; uint8_t dm; uint8_t fb; uint8_t am[2]; uint8_t vib[2]; uint8_t et[2]; uint8_t ksr[2]; uint8_t multi[2]; uint8_t ksl[2]; uint8_t ar[2]; uint8_t dr[2]; uint8_t sl[2]; uint8_t rr[2]; } opll_patch_t; typedef struct { uint32_t chip_type; uint32_t cycles; uint32_t slot; const opll_patch_t *patchrom; /* IO */ uint8_t write_data; uint8_t write_a; uint8_t write_d; uint8_t write_a_en; uint8_t write_d_en; uint8_t write_fm_address; uint8_t write_fm_data; uint8_t write_mode_address; uint8_t address; uint8_t data; /* Envelope generator */ uint8_t eg_counter_state; uint8_t eg_counter_state_prev; uint32_t eg_timer; uint8_t eg_timer_low_lock; uint8_t eg_timer_carry; uint8_t eg_timer_shift; uint8_t eg_timer_shift_lock; uint8_t eg_timer_shift_stop; uint8_t eg_state[18]; uint8_t eg_level[18]; uint8_t eg_kon; uint32_t eg_dokon; uint8_t eg_off; uint8_t eg_rate; uint8_t eg_maxrate; uint8_t eg_zerorate; uint8_t eg_inc_lo; uint8_t eg_inc_hi; uint8_t eg_rate_hi; uint16_t eg_sl; uint16_t eg_ksltl; uint8_t eg_out; uint8_t eg_silent; /* Phase generator */ uint16_t pg_fnum; uint8_t pg_block; uint16_t pg_out; uint32_t pg_inc; uint32_t pg_phase[18]; uint32_t pg_phase_next; /* Operator */ int16_t op_fb1[9]; int16_t op_fb2[9]; int16_t op_fbsum; int16_t op_mod; uint8_t op_neg; uint16_t op_logsin; uint16_t op_exp_m; uint16_t op_exp_s; /* Channel */ int16_t ch_out; int16_t ch_out_hh; int16_t ch_out_tm; int16_t ch_out_bd; int16_t ch_out_sd; int16_t ch_out_tc; /* LFO */ uint16_t lfo_counter; uint8_t lfo_vib_counter; uint16_t lfo_am_counter; uint8_t lfo_am_step; uint8_t lfo_am_dir; uint8_t lfo_am_car; uint8_t lfo_am_out; /* Register set */ uint16_t fnum[9]; uint8_t block[9]; uint8_t kon[9]; uint8_t son[9]; uint8_t vol[9]; uint8_t inst[9]; uint8_t rhythm; uint8_t testmode; opll_patch_t patch; uint8_t c_instr; uint8_t c_op; uint8_t c_tl; uint8_t c_dc; uint8_t c_dm; uint8_t c_fb; uint8_t c_am; uint8_t c_vib; uint8_t c_et; uint8_t c_ksr; uint8_t c_ksr_freq; uint8_t c_ksl_freq; uint8_t c_ksl_block; uint8_t c_multi; uint8_t c_ksl; uint8_t c_adrr[3]; uint8_t c_sl; uint16_t c_fnum; uint16_t c_block; /* Rhythm mode */ int8_t rm_enable; uint32_t rm_noise; uint32_t rm_select; uint8_t rm_hh_bit2; uint8_t rm_hh_bit3; uint8_t rm_hh_bit7; uint8_t rm_hh_bit8; uint8_t rm_tc_bit3; uint8_t rm_tc_bit5; int16_t output_m; int16_t output_r; uint32_t mute[14]; int32_t rateratio; int32_t samplecnt; int32_t oldsamples[2]; int32_t samples[2]; uint64_t writebuf_samplecnt; uint32_t writebuf_cur; uint32_t writebuf_last; uint64_t writebuf_lasttime; opll_writebuf writebuf[OPLL_WRITEBUF_SIZE]; } opll_t; void OPLL_Reset(opll_t *chip, uint32_t chip_type, uint32_t rate, uint32_t clock); void OPLL_Clock(opll_t *chip, int32_t *buffer); void OPLL_Write(opll_t *chip, uint32_t port, uint8_t data); void OPLL_WriteBuffered(opll_t*chip, uint32_t port, uint8_t data); void OPLL_GenerateStream(opll_t*chip, int32_t **sndptr, uint32_t numsamples); void OPLL_SetMute(opll_t*chip, uint32_t mute); #endif ================================================ FILE: VGMPlay/chips/opm.c ================================================ /* Nuked OPM * Copyright (C) 2020 Nuke.YKT * * This file is part of Nuked OPM. * * Nuked OPM is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 * of the License, or (at your option) any later version. * * Nuked OPM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Nuked OPM. If not, see . * * Nuked OPM emulator. * Thanks: * siliconpr0n.org(digshadow, John McMaster): * YM2151 and other FM chip decaps and die shots. * * version: 0.9 beta */ #include #include "opm.h" enum { eg_num_attack = 0, eg_num_decay = 1, eg_num_sustain = 2, eg_num_release = 3 }; /* logsin table */ static const uint16_t logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; /* exp table */ static const uint16_t exprom[256] = { 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 }; /* Envelope generator */ static const uint32_t eg_stephi[4][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 1, 0 } }; /* Phase generator */ static const uint32_t pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; typedef struct { int32_t basefreq; int32_t approxtype; int32_t slope; } freqtable_t; static const freqtable_t pg_freqtable[64] = { 1299, 1, 19, 1318, 1, 19, 1337, 1, 19, 1356, 1, 20, 1376, 1, 20, 1396, 1, 20, 1416, 1, 21, 1437, 1, 20, 1458, 1, 21, 1479, 1, 21, 1501, 1, 22, 1523, 1, 22, 0, 0, 16, 0, 0, 16, 0, 0, 16, 0, 0, 16, 1545, 1, 22, 1567, 1, 22, 1590, 1, 23, 1613, 1, 23, 1637, 1, 23, 1660, 1, 24, 1685, 1, 24, 1709, 1, 24, 1734, 1, 25, 1759, 1, 25, 1785, 1, 26, 1811, 1, 26, 0, 0, 16, 0, 0, 16, 0, 0, 16, 0, 0, 16, 1837, 1, 26, 1864, 1, 27, 1891, 1, 27, 1918, 1, 28, 1946, 1, 28, 1975, 1, 28, 2003, 1, 29, 2032, 1, 30, 2062, 1, 30, 2092, 1, 30, 2122, 1, 31, 2153, 1, 31, 0, 0, 16, 0, 0, 16, 0, 0, 16, 0, 0, 16, 2185, 1, 31, 2216, 0, 31, 2249, 0, 31, 2281, 0, 31, 2315, 0, 31, 2348, 0, 31, 2382, 0, 30, 2417, 0, 30, 2452, 0, 30, 2488, 0, 30, 2524, 0, 30, 2561, 0, 30, 0, 0, 16, 0, 0, 16, 0, 0, 16, 0, 0, 16 }; /* FM algorithm */ static const uint32_t fm_algorithm[4][6][8] = { { { 1, 1, 1, 1, 1, 1, 1, 1 }, /* M1_0 */ { 1, 1, 1, 1, 1, 1, 1, 1 }, /* M1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* C1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ }, { { 0, 1, 0, 0, 0, 1, 0, 0 }, /* M1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* M1_1 */ { 1, 1, 1, 0, 0, 0, 0, 0 }, /* C1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ }, { { 0, 0, 0, 0, 0, 0, 0, 0 }, /* M1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* M1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* C1 */ { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ }, { { 0, 0, 1, 0, 0, 1, 0, 0 }, /* M1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* M1_1 */ { 0, 0, 0, 1, 0, 0, 0, 0 }, /* C1 */ { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ } }; static uint16_t lfo_counter2_table[] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00, 0x7f80, 0x7fc0, 0x7fe0, 0x7ff0, 0x7ff8, 0x7ffc, 0x7ffe, 0x7fff }; static int32_t OPM_KCToFNum(int32_t kcode) { int32_t kcode_h = (kcode >> 4) & 63; int32_t kcode_l = kcode & 15; int32_t i, slope, sum = 0; if (pg_freqtable[kcode_h].approxtype) { for (i = 0; i < 4; i++) { if (kcode_l & (1 << i)) { sum += (pg_freqtable[kcode_h].slope >> (3 - i)); } } } else { slope = pg_freqtable[kcode_h].slope | 1; if (kcode_l & 1) { sum += (slope >> 3) + 2; } if (kcode_l & 2) { sum += 8; } if (kcode_l & 4) { sum += slope >> 1; } if (kcode_l & 8) { sum += slope; sum++; } if ((kcode_l & 12) == 12 && (pg_freqtable[kcode_h].slope & 1) == 0) { sum += 4; } } return pg_freqtable[kcode_h].basefreq + (sum >> 1); } static int32_t OPM_LFOApplyPMS(int32_t lfo, int32_t pms) { int32_t t, out; int32_t top = (lfo >> 4) & 7; if (pms != 7) { top >>= 1; } t = (top & 6) == 6 || ((top & 3) == 3 && pms >= 6); out = top + ((top >> 2) & 1) + t; out = out * 2 + ((lfo >> 4) & 1); if (pms == 7) { out >>= 1; } out &= 15; out = (lfo & 15) + out * 16; switch (pms) { case 0: default: out = 0; break; case 1: out = (out >> 5) & 3; break; case 2: out = (out >> 4) & 7; break; case 3: out = (out >> 3) & 15; break; case 4: out = (out >> 2) & 31; break; case 5: out = (out >> 1) & 63; break; case 6: out = (out & 255) << 1; break; case 7: out = (out & 255) << 2; break; } return out; } static int32_t OPM_CalcKCode(int32_t kcf, int32_t lfo, int32_t lfo_sign, int32_t dt) { int32_t t2, t3, b0, b1, b2, b3, w2, w3, w4, w6; int32_t overflow1 = 0; int32_t overflow2 = 0; int32_t negoverflow = 0; int32_t sum, cr; if (!lfo_sign) { lfo = ~lfo; } sum = (kcf & 8191) + (lfo&8191) + (!lfo_sign); cr = ((kcf & 255) + (lfo & 255) + (!lfo_sign)) >> 8; if (sum & (1 << 13)) { overflow1 = 1; } sum &= 8191; if (lfo_sign && ((((sum >> 6) & 3) == 3) || cr)) { sum += 64; } if (!lfo_sign && !cr) { sum += (-64)&8191; negoverflow = 1; } if (sum & (1 << 13)) { overflow2 = 1; } sum &= 8191; if ((!lfo_sign && !overflow1) || (negoverflow && !overflow2)) { sum = 0; } if (lfo_sign && (overflow1 || overflow2)) { sum = 8127; } t2 = sum & 63; if (dt == 2) t2 += 20; if (dt == 2 || dt == 3) t2 += 32; b0 = (t2 >> 6) & 1; b1 = dt == 2; b2 = ((sum >> 6) & 1); b3 = ((sum >> 7) & 1); w2 = (b0 && b1 && b2); w3 = (b0 && b3); w4 = !w2 && !w3; w6 = (b0 && !w2 && !w3) || (b3 && !b0 && b1); t2 &= 63; t3 = (sum >> 6) + w6 + b1 + (w2 || w3) * 2 + (dt == 3) * 4 + (dt != 0) * 8; if (t3 & 128) { t2 = 63; t3 = 126; } sum = t3 * 64 + t2; return sum; } static void OPM_PhaseCalcFNumBlock(opm_t *chip) { uint32_t slot = (chip->cycles + 7) & 31; uint32_t channel = slot & 7; uint32_t kcf = (chip->ch_kc[channel] << 6) + chip->ch_kf[channel]; uint32_t lfo = chip->lfo_pmd ? chip->lfo_pm_lock : 0; uint32_t pms = chip->ch_pms[channel]; uint32_t dt = chip->sl_dt2[slot]; int32_t lfo_pm = OPM_LFOApplyPMS(lfo & 127, pms); uint32_t kcode = OPM_CalcKCode(kcf, lfo_pm, (lfo & 0x80) != 0 && pms != 0 ? 0 : 1, dt); uint32_t fnum = OPM_KCToFNum(kcode); uint32_t kcode_h = kcode >> 8; chip->pg_fnum[slot] = fnum; chip->pg_kcode[slot] = kcode_h; } static void OPM_PhaseCalcIncrement(opm_t *chip) { uint32_t slot = chip->cycles; uint32_t channel = slot & 7; uint32_t dt = chip->sl_dt1[slot]; uint32_t dt_l = dt & 3; uint32_t detune = 0; uint32_t multi = chip->sl_mul[slot]; uint32_t kcode = chip->pg_kcode[slot]; uint32_t fnum = chip->pg_fnum[slot]; uint32_t block = kcode >> 2; uint32_t basefreq = (fnum << block) >> 2; uint32_t note, sum, sum_h, sum_l, inc; /* Apply detune */ if (dt_l) { if (kcode > 0x1c) { kcode = 0x1c; } block = kcode >> 2; note = kcode & 0x03; sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); sum_h = sum >> 1; sum_l = sum & 0x01; detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); } if (dt & 0x04) { basefreq -= detune; } else { basefreq += detune; } basefreq &= 0x1ffff; if (multi) { inc = basefreq * multi; } else { inc = basefreq >> 1; } inc &= 0xfffff; chip->pg_inc[slot] = inc; } static void OPM_PhaseGenerate(opm_t *chip) { uint32_t slot = (chip->cycles + 27) & 31; chip->pg_reset_latch[slot] = chip->pg_reset[slot]; slot = (chip->cycles + 25) & 31; /* Mask increment */ if (chip->pg_reset_latch[slot]) { chip->pg_inc[slot] = 0; } /* Phase step */ slot = (chip->cycles + 24) & 31; if (chip->pg_reset_latch[slot] || chip->mode_test[3]) { chip->pg_phase[slot] = 0; } chip->pg_phase[slot] += chip->pg_inc[slot]; chip->pg_phase[slot] &= 0xfffff; } static void OPM_PhaseDebug(opm_t *chip) { chip->pg_serial >>= 1; if (chip->cycles == 5) { chip->pg_serial |= (chip->pg_phase[29] & 0x3ff); } } static void OPM_KeyOn1(opm_t *chip) { uint32_t cycles = (chip->cycles + 1) & 31; chip->kon_chanmatch = 0; if (chip->mode_kon_channel + 24 == cycles) { chip->kon_chanmatch = 1; } } static void OPM_KeyOn2(opm_t *chip) { uint32_t slot = (chip->cycles + 8) & 31; if (chip->kon_chanmatch) { chip->mode_kon[(slot + 0) & 31] = chip->mode_kon_operator[0]; chip->mode_kon[(slot + 8) & 31] = chip->mode_kon_operator[2]; chip->mode_kon[(slot + 16) & 31] = chip->mode_kon_operator[1]; chip->mode_kon[(slot + 24) & 31] = chip->mode_kon_operator[3]; } } static void OPM_EnvelopePhase1(opm_t *chip) { uint32_t slot = (chip->cycles + 2) & 31; uint32_t kon = chip->mode_kon[slot] | chip->kon_csm; uint32_t konevent = !chip->kon[slot] && kon; if (konevent) { chip->eg_state[slot] = eg_num_attack; } chip->kon2[slot] = chip->kon[slot]; chip->kon[slot] = kon; } static void OPM_EnvelopePhase2(opm_t *chip) { uint32_t slot = chip->cycles; uint32_t chan = slot & 7; uint8_t rate = 0, ksv, zr, ams; switch (chip->eg_state[slot]) { case eg_num_attack: rate = chip->sl_ar[slot]; break; case eg_num_decay: rate = chip->sl_d1r[slot]; break; case eg_num_sustain: rate = chip->sl_d2r[slot]; break; case eg_num_release: rate = chip->sl_rr[slot] * 2 + 1; break; default: break; } if (chip->ic) { rate = 31; } zr = rate == 0; ksv = chip->pg_kcode[slot] >> (chip->sl_ks[slot] ^ 3); if (chip->sl_ks[slot] == 0 && zr) { ksv &= ~3; } rate = rate * 2 + ksv; if (rate & 64) { rate = 63; } chip->eg_tl[2] = chip->eg_tl[1]; chip->eg_tl[1] = chip->eg_tl[0]; chip->eg_tl[0] = chip->sl_tl[slot]; chip->eg_sl[1] = chip->eg_sl[0]; chip->eg_sl[0] = chip->sl_d1l[slot]; if (chip->sl_d1l[slot] == 15) { chip->eg_sl[0] = 31; } chip->eg_zr[1] = chip->eg_zr[0]; chip->eg_zr[0] = zr; chip->eg_rate[1] = chip->eg_rate[0]; chip->eg_rate[0] = rate; chip->eg_ratemax[1] = chip->eg_ratemax[0]; chip->eg_ratemax[0] = (rate >> 1) == 31; ams = chip->sl_am_e[slot] ? chip->ch_ams[chan] : 0; switch (ams) { default: case 0: chip->eg_am = 0; break; case 1: chip->eg_am = chip->lfo_am_lock << 0; break; case 2: chip->eg_am = chip->lfo_am_lock << 1; break; case 3: chip->eg_am = chip->lfo_am_lock << 2; break; } } static void OPM_EnvelopePhase3(opm_t *chip) { uint32_t slot = (chip->cycles + 31) & 31; chip->eg_shift = (chip->eg_timershift_lock + (chip->eg_rate[0] >> 2)) & 15; chip->eg_inchi = eg_stephi[chip->eg_rate[0] & 3][chip->eg_timer_lock & 3]; chip->eg_outtemp[1] = chip->eg_outtemp[0]; chip->eg_outtemp[0] = chip->eg_level[slot] + chip->eg_am; if (chip->eg_outtemp[0] & 1024) { chip->eg_outtemp[0] = 1023; } } static void OPM_EnvelopePhase4(opm_t *chip) { uint32_t slot = (chip->cycles + 30) & 31; uint8_t inc = 0; uint8_t kon, eg_off, eg_zero, slreach; if (chip->eg_clock & 2) { if (chip->eg_rate[1] >= 48) { inc = chip->eg_inchi + (chip->eg_rate[1] >> 2) - 11; if (inc > 4) { inc = 4; } } else if (!chip->eg_zr[1]) { switch (chip->eg_shift) { case 12: inc = chip->eg_rate[1] != 0; break; case 13: inc = (chip->eg_rate[1] >> 1) & 1; break; case 14: inc = chip->eg_rate[1] & 1; break; } } } chip->eg_inc = inc; kon = chip->kon[slot] && !chip->kon2[slot]; chip->pg_reset[slot] = kon; chip->eg_instantattack = chip->eg_ratemax[1] && (kon || !chip->eg_ratemax[1]); eg_off = (chip->eg_level[slot] & 0x3f0) == 0x3f0; slreach = (chip->eg_level[slot] >> 5) == chip->eg_sl[1]; eg_zero = chip->eg_level[slot] == 0; chip->eg_mute = eg_off && chip->eg_state[slot] != eg_num_attack && !kon; chip->eg_inclinear = 0; if (!kon && !eg_off) { switch (chip->eg_state[slot]) { case eg_num_decay: if (!slreach) chip->eg_inclinear = 1; break; case eg_num_sustain: case eg_num_release: chip->eg_inclinear = 1; break; } } chip->eg_incattack = chip->eg_state[slot] == eg_num_attack && !chip->eg_ratemax[1] && chip->kon[slot] && !eg_zero; // Update state if (kon) { chip->eg_state[slot] = eg_num_attack; } else if (!chip->kon[slot]) { chip->eg_state[slot] = eg_num_release; } else { switch (chip->eg_state[slot]) { case eg_num_attack: if (eg_zero) { chip->eg_state[slot] = eg_num_decay; } break; case eg_num_decay: if (eg_off) { chip->eg_state[slot] = eg_num_release; } else if (slreach) { chip->eg_state[slot] = eg_num_sustain; } break; case eg_num_sustain: if (eg_off) { chip->eg_state[slot] = eg_num_release; } break; case eg_num_release: break; } } if (chip->ic) { chip->eg_state[slot] = eg_num_release; } } static void OPM_EnvelopePhase5(opm_t *chip) { uint32_t slot = (chip->cycles + 29) & 31; uint32_t level = chip->eg_level[slot]; uint32_t step = 0; if (chip->eg_instantattack) { level = 0; } if (chip->eg_mute || chip->ic) { level = 0x3ff; } if (chip->eg_inc) { if (chip->eg_inclinear) { step |= 1 << (chip->eg_inc - 1); } if (chip->eg_incattack) { step |= ((~(int32_t)chip->eg_level[slot]) << chip->eg_inc) >> 5; } } level += step; chip->eg_level[slot] = (uint16_t)level; chip->eg_out[0] = chip->eg_outtemp[1] + (chip->eg_tl[2] << 3); if (chip->eg_out[0] & 1024) { chip->eg_out[0] = 1023; } if (chip->eg_test) { chip->eg_out[0] = 0; } chip->eg_test = chip->mode_test[5]; } static void OPM_EnvelopePhase6(opm_t *chip) { uint32_t slot = (chip->cycles + 28) & 31; chip->eg_serial_bit = (chip->eg_serial >> 9) & 1; if (chip->cycles == 3) { chip->eg_serial = chip->eg_out[0] ^ 1023; } else { chip->eg_serial <<= 1; } chip->eg_out[1] = chip->eg_out[0]; } static void OPM_EnvelopeClock(opm_t *chip) { chip->eg_clock <<= 1; if ((chip->eg_clockcnt & 2) != 0 || chip->mode_test[0]) { chip->eg_clock |= 1; } if (chip->ic || (chip->cycles == 31 && (chip->eg_clockcnt & 2) != 0)) { chip->eg_clockcnt = 0; } else if (chip->cycles == 31) { chip->eg_clockcnt++; } } static void OPM_EnvelopeTimer(opm_t *chip) { uint32_t cycle = (chip->cycles + 31) & 15; uint32_t cycle2; uint8_t inc = ((chip->cycles + 31) & 31) < 16 && (chip->eg_clock & 1) != 0 && (cycle == 0 || chip->eg_timercarry); uint8_t timerbit = (chip->eg_timer >> cycle) & 1; uint8_t sum = timerbit + inc; uint8_t sum0 = (sum & 1) && !chip->ic; chip->eg_timercarry = sum >> 1; chip->eg_timer = (chip->eg_timer & (~(1 << cycle))) | (sum0 << cycle); cycle2 = (chip->cycles + 30) & 15; chip->eg_timer2 <<= 1; if ((chip->eg_timer & (1 << cycle2)) != 0 && !chip->eg_timerbstop) { chip->eg_timer2 |= 1; } if (chip->eg_timer & (1 << cycle2)) { chip->eg_timerbstop = 1; } if (cycle == 0 || chip->ic2) { chip->eg_timerbstop = 0; } if (chip->cycles == 1 && (chip->eg_clock & 1) != 0) { chip->eg_timershift_lock = 0; if (chip->eg_timer2 & (8 + 32 + 128 + 512 + 2048 + 8192 + 32768)) { chip->eg_timershift_lock |= 1; } if (chip->eg_timer2 & (4 + 32 + 64 + 512 + 1024 + 8192 + 16384)) { chip->eg_timershift_lock |= 2; } if (chip->eg_timer2 & (4 + 8 + 16 + 512 + 1024 + 2048 + 4096)) { chip->eg_timershift_lock |= 4; } if (chip->eg_timer2 & (4 + 8 + 16 + 32 + 64 + 128 + 256)) { chip->eg_timershift_lock |= 8; } chip->eg_timer_lock = chip->eg_timer; } } static void OPM_OperatorPhase1(opm_t *chip) { uint32_t slot = chip->cycles; int16_t mod = chip->op_mod[2]; chip->op_phase_in = chip->pg_phase[slot] >> 10; if (chip->op_fbshift & 8) { if (chip->op_fb[1] == 0) { mod = 0; } else { mod = mod >> (9 - chip->op_fb[1]); } } chip->op_mod_in = mod; } static void OPM_OperatorPhase2(opm_t *chip) { uint32_t slot = (chip->cycles + 31) & 31; chip->op_phase = (chip->op_phase_in + chip->op_mod_in) & 1023; } static void OPM_OperatorPhase3(opm_t *chip) { uint32_t slot = (chip->cycles + 30) & 31; uint16_t phase = chip->op_phase & 255; if (chip->op_phase & 256) { phase ^= 255; } chip->op_logsin[0] = logsinrom[phase]; chip->op_sign <<= 1; chip->op_sign |= (chip->op_phase >> 9) & 1; } static void OPM_OperatorPhase4(opm_t *chip) { uint32_t slot = (chip->cycles + 29) & 31; chip->op_logsin[1] = chip->op_logsin[0]; } static void OPM_OperatorPhase5(opm_t *chip) { uint32_t slot = (chip->cycles + 28) & 31; chip->op_logsin[2] = chip->op_logsin[1]; } static void OPM_OperatorPhase6(opm_t *chip) { uint32_t slot = (chip->cycles + 27) & 31; chip->op_atten = chip->op_logsin[2] + (chip->eg_out[1] << 2); if (chip->op_atten & 4096) { chip->op_atten = 4095; } } static void OPM_OperatorPhase7(opm_t *chip) { uint32_t slot = (chip->cycles + 26) & 31; chip->op_exp[0] = exprom[chip->op_atten & 255]; chip->op_pow[0] = chip->op_atten >> 8; } static void OPM_OperatorPhase8(opm_t *chip) { uint32_t slot = (chip->cycles + 25) & 31; chip->op_exp[1] = chip->op_exp[0]; chip->op_pow[1] = chip->op_pow[0]; } static void OPM_OperatorPhase9(opm_t *chip) { uint32_t slot = (chip->cycles + 24) & 31; int16_t out = (chip->op_exp[1] << 2) >> (chip->op_pow[1]); if (chip->op_sign & 32) { out = -out; } chip->op_out[0] = out; } static void OPM_OperatorPhase10(opm_t *chip) { uint32_t slot = (chip->cycles + 23) & 31; chip->op_out[1] = chip->op_out[0]; } static void OPM_OperatorPhase11(opm_t *chip) { uint32_t slot = (chip->cycles + 22) & 31; chip->op_out[2] = chip->op_out[1]; } static void OPM_OperatorPhase12(opm_t *chip) { uint32_t slot = (chip->cycles + 21) & 31; chip->op_out[3] = chip->op_out[2]; } static void OPM_OperatorPhase13(opm_t *chip) { uint32_t slot = (chip->cycles + 20) & 31; chip->op_out[4] = chip->op_out[3]; chip->op_connect = chip->ch_connect[slot & 7]; } static void OPM_OperatorPhase14(opm_t *chip) { uint32_t slot = (chip->cycles + 19) & 31; uint32_t channel = slot & 7; chip->op_mix = chip->op_out[5] = chip->op_out[4]; chip->op_fbupdate = (chip->op_counter == 0); chip->op_c1update = (chip->op_counter == 2); chip->op_fbshift <<= 1; chip->op_fbshift |= (chip->op_counter == 2); chip->op_modtable[0] = fm_algorithm[(chip->op_counter + 2) & 3][0][chip->op_connect]; chip->op_modtable[1] = fm_algorithm[(chip->op_counter + 2) & 3][1][chip->op_connect]; chip->op_modtable[2] = fm_algorithm[(chip->op_counter + 2) & 3][2][chip->op_connect]; chip->op_modtable[3] = fm_algorithm[(chip->op_counter + 2) & 3][3][chip->op_connect]; chip->op_modtable[4] = fm_algorithm[(chip->op_counter + 2) & 3][4][chip->op_connect]; chip->op_mixl = fm_algorithm[chip->op_counter][5][chip->op_connect] && (chip->ch_rl[channel] & 1) != 0; chip->op_mixr = fm_algorithm[chip->op_counter][5][chip->op_connect] && (chip->ch_rl[channel] & 2) != 0; if (chip->mute[channel]) { chip->op_mixl = chip->op_mixr = 0; } } static void OPM_OperatorPhase15(opm_t *chip) { uint32_t slot = (chip->cycles + 18) & 31; int16_t mod, mod1 = 0, mod2 = 0; if (chip->op_modtable[0]) { mod2 |= chip->op_m1[slot & 7][0]; } if (chip->op_modtable[1]) { mod1 |= chip->op_m1[slot & 7][1]; } if (chip->op_modtable[2]) { mod1 |= chip->op_c1[slot & 7]; } if (chip->op_modtable[3]) { mod2 |= chip->op_out[5]; } if (chip->op_modtable[4]) { mod1 |= chip->op_out[5]; } mod = (mod1 + mod2) >> 1; chip->op_mod[0] = mod; if (chip->op_fbupdate) { chip->op_m1[slot & 7][1] = chip->op_m1[slot & 7][0]; chip->op_m1[slot & 7][0] = chip->op_out[5]; } if (chip->op_c1update) { chip->op_c1[slot & 7] = chip->op_out[5]; } } static void OPM_OperatorPhase16(opm_t *chip) { uint32_t slot = (chip->cycles + 17) & 31; // hack chip->op_mod[2] = chip->op_mod[1]; chip->op_fb[1] = chip->op_fb[0]; chip->op_mod[1] = chip->op_mod[0]; chip->op_fb[0] = chip->ch_fb[slot & 7]; } static void OPM_OperatorCounter(opm_t *chip) { if ((chip->cycles & 7) == 4) { chip->op_counter++; } if (chip->cycles == 12) { chip->op_counter = 0; } } static void OPM_Mixer2(opm_t *chip) { uint32_t cycles = (chip->cycles + 30) & 31; uint8_t bit; uint8_t top, ex; if (cycles < 16) { bit = chip->mix_serial[0] & 1; } else { bit = chip->mix_serial[1] & 1; } if ((chip->cycles & 15) == 1) { chip->mix_sign_lock = bit ^ 1; chip->mix_top_bits_lock = (chip->mix_bits >> 15) & 63; } chip->mix_bits >>= 1; chip->mix_bits |= bit << 20; if ((chip->cycles & 15) == 10) { top = chip->mix_top_bits_lock; if (chip->mix_sign_lock) { top ^= 63; } if (top & 32) { ex = 7; } else if (top & 16) { ex = 6; } else if (top & 8) { ex = 5; } else if (top & 4) { ex = 4; } else if (top & 2) { ex = 3; } else if (top & 1) { ex = 2; } else { ex = 1; } chip->mix_sign_lock2 = chip->mix_sign_lock; chip->mix_exp_lock = ex; } chip->mix_out_bit <<= 1; switch ((chip->cycles + 1) & 15) { case 0: chip->mix_out_bit |= chip->mix_sign_lock2 ^ 1; break; case 1: chip->mix_out_bit |= (chip->mix_exp_lock >> 0) & 1; break; case 2: chip->mix_out_bit |= (chip->mix_exp_lock >> 1) & 1; break; case 3: chip->mix_out_bit |= (chip->mix_exp_lock >> 2) & 1; break; default: if (chip->mix_exp_lock) { chip->mix_out_bit |= (chip->mix_bits >> (chip->mix_exp_lock - 1)) & 1; } break; } } static void OPM_Output(opm_t *chip) { uint32_t slot = (chip->cycles + 27) & 31; chip->smp_so = (chip->mix_out_bit & 4) != 0; chip->smp_sh1 = (slot & 24) == 8 && !chip->ic; chip->smp_sh2 = (slot & 24) == 24 && !chip->ic; } static void OPM_DAC(opm_t *chip) { int32_t exp, mant; if (chip->dac_osh1 && !chip->smp_sh1) { exp = (chip->dac_bits >> 10) & 7; mant = (chip->dac_bits >> 0) & 1023; mant -= 512; chip->dac_output[1] = (mant << exp) >> 1; } if (chip->dac_osh2 && !chip->smp_sh2) { exp = (chip->dac_bits >> 10) & 7; mant = (chip->dac_bits >> 0) & 1023; mant -= 512; chip->dac_output[0] = (mant << exp) >> 1; } chip->dac_bits >>= 1; chip->dac_bits |= chip->smp_so << 12; chip->dac_osh1 = chip->smp_sh1; chip->dac_osh2 = chip->smp_sh2; } static void OPM_Mixer(opm_t *chip) { uint32_t slot = (chip->cycles + 18) & 31; uint32_t channel = (slot & 7); // Right channel chip->mix_serial[1] >>= 1; if (chip->cycles == 13) { chip->mix_serial[1] |= (chip->mix[1] & 1023) << 4; } if (chip->cycles == 14) { chip->mix_serial[1] |= ((chip->mix2[1] >> 10) & 31) << 13; chip->mix_serial[1] |= (((chip->mix2[1] >> 17) & 1) ^ 1) << 18; chip->mix_clamp_low[1] = 0; chip->mix_clamp_high[1] = 0; switch ((chip->mix2[1]>>15) & 7) { case 0: default: break; case 1: chip->mix_clamp_high[1] = 1; break; case 2: chip->mix_clamp_high[1] = 1; break; case 3: chip->mix_clamp_high[1] = 1; break; case 4: chip->mix_clamp_low[1] = 1; break; case 5: chip->mix_clamp_low[1] = 1; break; case 6: chip->mix_clamp_low[1] = 1; break; case 7: break; } } if (chip->mix_clamp_low[1]) { chip->mix_serial[1] &= ~2; } if (chip->mix_clamp_high[1]) { chip->mix_serial[1] |= 2; } // Left channel chip->mix_serial[0] >>= 1; if (chip->cycles == 29) { chip->mix_serial[0] |= (chip->mix[0] & 1023) << 4; } if (chip->cycles == 30) { chip->mix_serial[0] |= ((chip->mix2[0] >> 10) & 31) << 13; chip->mix_serial[0] |= (((chip->mix2[0] >> 17) & 1) ^ 1) << 18; chip->mix_clamp_low[0] = 0; chip->mix_clamp_high[0] = 0; switch ((chip->mix2[0]>>15) & 7) { case 0: default: break; case 1: chip->mix_clamp_high[0] = 1; break; case 2: chip->mix_clamp_high[0] = 1; break; case 3: chip->mix_clamp_high[0] = 1; break; case 4: chip->mix_clamp_low[0] = 1; break; case 5: chip->mix_clamp_low[0] = 1; break; case 6: chip->mix_clamp_low[0] = 1; break; case 7: break; } } if (chip->mix_clamp_low[0]) { chip->mix_serial[0] &= ~2; } if (chip->mix_clamp_high[0]) { chip->mix_serial[0] |= 2; } chip->mix2[0] = chip->mix[0]; chip->mix2[1] = chip->mix[1]; if (chip->cycles == 13) { chip->mix[1] = 0; } if (chip->cycles == 29) { chip->mix[0] = 0; } chip->mix[0] += chip->op_mix * chip->op_mixl; chip->mix[1] += chip->op_mix * chip->op_mixr; } static void OPM_Noise(opm_t *chip) { uint8_t w1 = !chip->ic && !chip->noise_update; uint8_t xr = ((chip->noise_lfsr >> 2) & 1) ^ chip->noise_temp; uint8_t w2t = (chip->noise_lfsr & 0xffff) == 0xffff && chip->noise_temp == 0; uint8_t w2 = !w2t && !xr; uint8_t w3 = !chip->ic && !w1 && !w2; uint8_t w4 = ((chip->noise_lfsr & 1) == 0 || !w1) && !w3; if (!w1) { chip->noise_temp = (chip->noise_lfsr & 1) == 0; } chip->noise_lfsr >>= 1; chip->noise_lfsr |= w4 << 15; } static void OPM_NoiseTimer(opm_t *chip) { uint32_t timer = chip->noise_timer; chip->noise_update = chip->noise_timer_of; if ((chip->cycles & 15) == 15) { timer++; timer &= 31; } if (chip->ic || (chip->noise_timer_of && ((chip->cycles & 15) == 15))) { timer = 0; } chip->noise_timer_of = chip->noise_timer == (chip->noise_freq ^ 31); chip->noise_timer = timer; } static void OPM_DoTimerA(opm_t *chip) { uint16_t value = chip->timer_a_val; value += chip->timer_a_inc; chip->timer_a_of = (value >> 10) & 1; if (chip->timer_a_do_reset) { value = 0; } if (chip->timer_a_do_load) { value = chip->timer_a_reg; } chip->timer_a_val = value & 1023; } static void OPM_DoTimerA2(opm_t *chip) { if (chip->cycles == 1) { chip->timer_a_load = chip->timer_loada; } chip->timer_a_inc = chip->mode_test[2] || (chip->timer_a_load && chip->cycles == 0); chip->timer_a_do_load = chip->timer_a_of || (chip->timer_a_load && chip->timer_a_temp); chip->timer_a_do_reset = chip->timer_a_temp; chip->timer_a_temp = !chip->timer_a_load; if (chip->timer_reseta || chip->ic) { chip->timer_a_status = 0; } else { chip->timer_a_status |= chip->timer_irqa && chip->timer_a_of; } chip->timer_reseta = 0; } static void OPM_DoTimerB(opm_t *chip) { uint16_t value = chip->timer_b_val; value += chip->timer_b_inc; chip->timer_b_of = (value >> 8) & 1; if (chip->timer_b_do_reset) { value = 0; } if (chip->timer_b_do_load) { value = chip->timer_b_reg; } chip->timer_b_val = value & 255; if (chip->cycles == 0) { chip->timer_b_sub++; } chip->timer_b_sub_of = (chip->timer_b_sub >> 4) & 1; chip->timer_b_sub &= 15; if (chip->ic) { chip->timer_b_sub = 0; } } static void OPM_DoTimerB2(opm_t *chip) { chip->timer_b_inc = chip->mode_test[2] || (chip->timer_loadb && chip->timer_b_sub_of); chip->timer_b_do_load = chip->timer_b_of || (chip->timer_loadb && chip->timer_b_temp); chip->timer_b_do_reset = chip->timer_b_temp; chip->timer_b_temp = !chip->timer_loadb; if (chip->timer_resetb || chip->ic) { chip->timer_b_status = 0; } else { chip->timer_b_status |= chip->timer_irqb && chip->timer_b_of; } chip->timer_resetb = 0; } static void OPM_DoTimerIRQ(opm_t *chip) { chip->timer_irq = chip->timer_a_status || chip->timer_b_status; } static void OPM_DoLFOMult(opm_t *chip) { uint8_t ampm_sel = (chip->lfo_bit_counter & 8) != 0; uint8_t dp = ampm_sel ? chip->lfo_pmd : chip->lfo_amd; uint8_t bit = 0, b1, b2; uint8_t sum; chip->lfo_out2_b = chip->lfo_out2; switch (chip->lfo_bit_counter & 7) { case 0: bit = (dp & 64) != 0 && (chip->lfo_out1 & 64) == 0; break; case 1: bit = (dp & 32) != 0 && (chip->lfo_out1 & 32) == 0; break; case 2: bit = (dp & 16) != 0 && (chip->lfo_out1 & 16) == 0; break; case 3: bit = (dp & 8) != 0 && (chip->lfo_out1 & 8) == 0; break; case 4: bit = (dp & 4) != 0 && (chip->lfo_out1 & 4) == 0; break; case 5: bit = (dp & 2) != 0 && (chip->lfo_out1 & 2) == 0; break; case 6: bit = (dp & 1) != 0 && (chip->lfo_out1 & 1) == 0; break; } b1 = (chip->lfo_out2 & 1) != 0; if ((chip->lfo_bit_counter & 7) == 0) { b1 = 0; } b2 = chip->lfo_mult_carry; if ((chip->cycles & 15) == 15) { b2 = 0; } sum = bit + b1 + b2; chip->lfo_out2 >>= 1; chip->lfo_out2 |= (sum & 1) << 15; chip->lfo_mult_carry = sum >> 1; } static void OPM_DoLFO1(opm_t *chip) { uint16_t counter2 = chip->lfo_counter2; uint8_t of_old = chip->lfo_counter2_of; uint8_t lfo_bit, noise, sum, carry, w[10]; uint8_t lfo_pm_sign; uint8_t ampm_sel = (chip->lfo_bit_counter & 8) != 0; counter2 += (chip->lfo_counter1_of1 & 2) != 0 || chip->mode_test[3]; chip->lfo_counter2_of = (counter2 >> 15) & 1; if (chip->ic) { counter2 = 0; } if (chip->lfo_counter2_load) { counter2 = lfo_counter2_table[chip->lfo_freq_hi]; } chip->lfo_counter2 = counter2 & 32767; chip->lfo_counter2_load = chip->lfo_frq_update || of_old; chip->lfo_frq_update = 0; if ((chip->cycles & 15) == 12) { chip->lfo_counter1++; } chip->lfo_counter1_of1 <<= 1; chip->lfo_counter1_of1 |= (chip->lfo_counter1 >> 4) & 1; chip->lfo_counter1 &= 15; if (chip->ic) { chip->lfo_counter1 = 0; } if ((chip->cycles & 15) == 5) { chip->lfo_counter2_of_lock2 = chip->lfo_counter2_of_lock; } chip->lfo_counter3 += chip->lfo_counter3_clock; if (chip->ic) { chip->lfo_counter3 = 0; } chip->lfo_counter3_clock = (chip->cycles & 15) == 13 && chip->lfo_counter2_of_lock2; if ((chip->cycles & 15) == 15) { chip->lfo_trig_sign = (chip->lfo_val & 0x80) != 0; chip->lfo_saw_sign = (chip->lfo_val & 0x100) != 0; } lfo_pm_sign = chip->lfo_wave == 2 ? chip->lfo_trig_sign : chip->lfo_saw_sign; w[5] = ampm_sel ? chip->lfo_saw_sign : (chip->lfo_wave != 2 || chip->lfo_trig_sign); w[1] = !chip->lfo_clock || chip->lfo_wave == 3 || (chip->cycles & 15) != 15; w[2] = chip->lfo_wave == 2 && !w[1]; w[4] = chip->lfo_clock_lock && chip->lfo_wave == 3; w[3] = !chip->ic && !chip->mode_test[1] && !w[4] && (chip->lfo_val & 0x8000) != 0; w[7] = ((chip->cycles + 1) & 15) < 8; w[6] = w[5] ^ w[3]; w[9] = ampm_sel ? ((chip->cycles & 15) == 6) : !chip->lfo_saw_sign; w[8] = chip->lfo_wave == 1 ? w[9] : w[6]; w[8] &= w[7]; chip->lfo_out1 <<= 1; chip->lfo_out1 |= !w[8]; carry = !w[1] || ((chip->cycles & 15) != 15 && chip->lfo_val_carry != 0 && chip->lfo_wave != 3); sum = carry + w[2] + w[3]; noise = chip->lfo_clock_lock && (chip->noise_lfsr & 1) != 0; lfo_bit = sum & 1; lfo_bit |= (chip->lfo_wave == 3) && noise; chip->lfo_val_carry = sum >> 1; chip->lfo_val <<= 1; chip->lfo_val |= lfo_bit; if ((chip->cycles & 15) == 15 && (chip->lfo_bit_counter & 7) == 7) { if (ampm_sel) { chip->lfo_pm_lock = (chip->lfo_out2_b >> 8) & 255; chip->lfo_pm_lock ^= lfo_pm_sign << 7; } else { chip->lfo_am_lock = (chip->lfo_out2_b >> 8) & 255; } } if ((chip->cycles & 15) == 14) { chip->lfo_bit_counter++; } if ((chip->cycles & 15) != 12 && chip->lfo_counter1_of2) { chip->lfo_bit_counter = 0; } chip->lfo_counter1_of2 = chip->lfo_counter1 == 2; } static void OPM_DoLFO2(opm_t *chip) { uint8_t c3_step = 0; chip->lfo_clock_test = chip->lfo_clock; chip->lfo_clock = (chip->lfo_counter2_of || chip->lfo_test || chip->lfo_counter3_step); if ((chip->cycles & 15) == 14) { chip->lfo_counter2_of_lock = chip->lfo_counter2_of; chip->lfo_clock_lock = chip->lfo_clock; } chip->lfo_counter3_step = 0; if (chip->lfo_counter3_clock) { if ((chip->lfo_counter3 & 1) == 0) { chip->lfo_counter3_step = (chip->lfo_freq_lo & 8) != 0; } else if ((chip->lfo_counter3 & 2) == 0) { chip->lfo_counter3_step = (chip->lfo_freq_lo & 4) != 0; } else if ((chip->lfo_counter3 & 4) == 0) { chip->lfo_counter3_step = (chip->lfo_freq_lo & 2) != 0; } else if ((chip->lfo_counter3 & 8) == 0) { chip->lfo_counter3_step = (chip->lfo_freq_lo & 1) != 0; } } chip->lfo_test = chip->mode_test[2]; } static void OPM_CSM(opm_t *chip) { chip->kon_csm = chip->kon_csm_lock; if (chip->cycles == 1) { chip->kon_csm_lock = chip->timer_a_do_load && chip->mode_csm; } } static void OPM_NoiseChannel(opm_t *chip) { chip->nc_active |= chip->eg_serial_bit & 1; if (chip->cycles == 13) { chip->nc_active = 0; } chip->nc_out <<= 1; chip->nc_out |= chip->nc_sign ^ chip->eg_serial_bit; chip->nc_sign = !chip->nc_sign_lock; if (chip->cycles == 12) { chip->nc_active_lock = chip->nc_active; chip->nc_sign_lock2 = chip->nc_active_lock && !chip->nc_sign_lock; chip->nc_sign_lock = (chip->noise_lfsr & 1); if (chip->noise_en) { if (chip->nc_sign_lock2) { chip->op_mix = ((chip->nc_out & ~1) << 2) | -4089; } else { chip->op_mix = ((chip->nc_out & ~1) << 2); } } } } static void OPM_DoIO(opm_t *chip) { // Busy chip->write_busy_cnt += chip->write_busy; chip->write_busy = (!(chip->write_busy_cnt >> 5) && chip->write_busy && !chip->ic) | chip->write_d_en; chip->write_busy_cnt &= 0x1f; if (chip->ic) { chip->write_busy_cnt = 0; } // Write signal check chip->write_a_en = chip->write_a; chip->write_d_en = chip->write_d; chip->write_a = 0; chip->write_d = 0; } static void OPM_DoRegWrite(opm_t *chip) { int32_t i; uint32_t channel = chip->cycles & 7; uint32_t slot = chip->cycles; // Register write if (chip->reg_data_ready) { // Channel if ((chip->reg_address & 0xe7) == (0x20 | channel)) { switch (chip->reg_address & 0x18) { case 0x00: // RL, FB, CONNECT chip->ch_rl[channel] = chip->reg_data >> 6; chip->ch_fb[channel] = (chip->reg_data >> 3) & 0x07; chip->ch_connect[channel] = chip->reg_data & 0x07; break; case 0x08: // KC chip->ch_kc[channel] = chip->reg_data & 0x7f; break; case 0x10: // KF chip->ch_kf[channel] = chip->reg_data >> 2; break; case 0x18: // PMS, AMS chip->ch_pms[channel] = (chip->reg_data >> 4) & 0x07; chip->ch_ams[channel] = chip->reg_data & 0x03; break; default: break; } } // Slot if ((chip->reg_address & 0x1f) == slot) { switch (chip->reg_address & 0xe0) { case 0x40: // DT1, MUL chip->sl_dt1[slot] = (chip->reg_data >> 4) & 0x07; chip->sl_mul[slot] = chip->reg_data & 0x0f; break; case 0x60: // TL chip->sl_tl[slot] = chip->reg_data & 0x7f; break; case 0x80: // KS, AR chip->sl_ks[slot] = chip->reg_data >> 6; chip->sl_ar[slot] = chip->reg_data & 0x1f; break; case 0xa0: // AMS-EN, D1R chip->sl_am_e[slot] = chip->reg_data >> 7; chip->sl_d1r[slot] = chip->reg_data & 0x1f; break; case 0xc0: // DT2, D2R chip->sl_dt2[slot] = chip->reg_data >> 6; chip->sl_d2r[slot] = chip->reg_data & 0x1f; break; case 0xe0: // D1L, RR chip->sl_d1l[slot] = chip->reg_data >> 4; chip->sl_rr[slot] = chip->reg_data & 0x0f; break; default: break; } } } // Mode write if (chip->write_d_en) { switch (chip->mode_address) { case 0x01: for (i = 0; i < 8; i++) { chip->mode_test[i] = (chip->write_data >> i) & 0x01; } break; case 0x08: for (i = 0; i < 4; i++) { chip->mode_kon_operator[i] = (chip->write_data >> (i + 3)) & 0x01; } chip->mode_kon_channel = chip->write_data & 0x07; break; case 0x0f: chip->noise_en = chip->write_data >> 7; chip->noise_freq = chip->write_data & 0x1f; break; case 0x10: chip->timer_a_reg &= 0x03; chip->timer_a_reg |= chip->write_data << 2; break; case 0x11: chip->timer_a_reg &= 0x3fc; chip->timer_a_reg |= chip->write_data & 0x03; break; case 0x12: chip->timer_b_reg = chip->write_data; break; case 0x14: chip->mode_csm = (chip->write_data >> 7) & 1; chip->timer_irqb = (chip->write_data >> 3) & 1; chip->timer_irqa = (chip->write_data >> 2) & 1; chip->timer_resetb = (chip->write_data >> 3) & 1; chip->timer_reseta = (chip->write_data >> 2) & 1; chip->timer_loadb = (chip->write_data >> 1) & 1; chip->timer_loada = (chip->write_data >> 0) & 1; break; case 0x18: chip->lfo_freq_hi = chip->write_data >> 4; chip->lfo_freq_lo = chip->write_data & 0x0f; chip->lfo_frq_update = 1; break; case 0x19: if (chip->write_data & 0x80) { chip->lfo_pmd = chip->write_data & 0x7f; } else { chip->lfo_amd = chip->write_data; } break; case 0x1b: chip->lfo_wave = chip->write_data & 0x03; chip->io_ct1 = chip->write_data >> 7; chip->io_ct2 = (chip->write_data >> 6) & 0x01; break; } } // Register data write chip->reg_data_ready = chip->reg_data_ready && !chip->write_a_en; if (chip->reg_address_ready && chip->write_d_en) { chip->reg_data = chip->write_data; chip->reg_data_ready = 1; } // Register address write chip->reg_address_ready = chip->reg_address_ready && !chip->write_a_en; if (chip->write_a_en && (chip->write_data & 0xe0) != 0) { chip->reg_address = chip->write_data; chip->reg_address_ready = 1; } if (chip->write_a_en) { chip->mode_address = chip->write_data; } } static void OPM_DoIC(opm_t *chip) { uint32_t channel = chip->cycles & 7; uint32_t slot = chip->cycles; if (chip->ic) { chip->ch_rl[channel] = 0; chip->ch_fb[channel] = 0; chip->ch_connect[channel] = 0; chip->ch_kc[channel] = 0; chip->ch_kf[channel] = 0; chip->ch_pms[channel] = 0; chip->ch_ams[channel] = 0; chip->sl_dt1[slot] = 0; chip->sl_mul[slot] = 0; chip->sl_tl[slot] = 0; chip->sl_ks[slot] = 0; chip->sl_ar[slot] = 0; chip->sl_am_e[slot] = 0; chip->sl_d1r[slot] = 0; chip->sl_dt2[slot] = 0; chip->sl_d2r[slot] = 0; chip->sl_d1l[slot] = 0; chip->sl_rr[slot] = 0; chip->timer_a_reg = 0; chip->timer_b_reg = 0; chip->timer_irqa = 0; chip->timer_irqb = 0; chip->timer_loada = 0; chip->timer_loadb = 0; chip->mode_csm = 0; chip->mode_test[0] = 0; chip->mode_test[1] = 0; chip->mode_test[2] = 0; chip->mode_test[3] = 0; chip->mode_test[4] = 0; chip->mode_test[5] = 0; chip->mode_test[6] = 0; chip->mode_test[7] = 0; chip->noise_en = 0; chip->noise_freq = 0; chip->mode_kon_channel = 0; chip->mode_kon_operator[0] = 0; chip->mode_kon_operator[1] = 0; chip->mode_kon_operator[2] = 0; chip->mode_kon_operator[3] = 0; chip->mode_kon[(slot + 8) & 31] = 0; chip->lfo_pmd = 0; chip->lfo_amd = 0; chip->lfo_wave = 0; chip->lfo_freq_hi = 0; chip->lfo_freq_lo = 0; chip->io_ct1 = 0; chip->io_ct2 = 0; chip->reg_address = 0; chip->reg_data = 0; } chip->ic2 = chip->ic; } void OPM_Clock(opm_t *chip, int32_t *output, uint8_t *sh1, uint8_t *sh2, uint8_t *so) { OPM_Mixer2(chip); OPM_Mixer(chip); OPM_OperatorPhase16(chip); OPM_OperatorPhase15(chip); OPM_OperatorPhase14(chip); OPM_OperatorPhase13(chip); OPM_OperatorPhase12(chip); OPM_OperatorPhase11(chip); OPM_OperatorPhase10(chip); OPM_OperatorPhase9(chip); OPM_OperatorPhase8(chip); OPM_OperatorPhase7(chip); OPM_OperatorPhase6(chip); OPM_OperatorPhase5(chip); OPM_OperatorPhase4(chip); OPM_OperatorPhase3(chip); OPM_OperatorPhase2(chip); OPM_OperatorPhase1(chip); OPM_OperatorCounter(chip); OPM_EnvelopeTimer(chip); OPM_EnvelopePhase6(chip); OPM_EnvelopePhase5(chip); OPM_EnvelopePhase4(chip); OPM_EnvelopePhase3(chip); OPM_EnvelopePhase2(chip); OPM_EnvelopePhase1(chip); OPM_PhaseDebug(chip); OPM_PhaseGenerate(chip); OPM_PhaseCalcIncrement(chip); OPM_PhaseCalcFNumBlock(chip); OPM_DoTimerIRQ(chip); OPM_DoTimerA(chip); OPM_DoTimerB(chip); OPM_DoLFOMult(chip); OPM_DoLFO1(chip); OPM_Noise(chip); OPM_KeyOn2(chip); OPM_DoRegWrite(chip); OPM_EnvelopeClock(chip); OPM_NoiseTimer(chip); OPM_KeyOn1(chip); OPM_DoIO(chip); OPM_DoTimerA2(chip); OPM_DoTimerB2(chip); OPM_DoLFO2(chip); OPM_CSM(chip); OPM_NoiseChannel(chip); OPM_Output(chip); OPM_DAC(chip); OPM_DoIC(chip); if (sh1) { *sh1 = chip->smp_sh1; } if (sh2) { *sh2 = chip->smp_sh2; } if (so) { *so = chip->smp_so; } if (output) { output[0] = chip->dac_output[0]; output[1] = chip->dac_output[1]; } chip->cycles = (chip->cycles + 1) & 31; } void OPM_Write(opm_t *chip, uint32_t port, uint8_t data) { chip->write_data = data; if (chip->ic) { return; } if (port & 0x01) { chip->write_d = 1; } else { chip->write_a = 1; } } uint8_t OPM_Read(opm_t *chip, uint32_t port) { uint16_t testdata; if (chip->mode_test[6]) { testdata = chip->op_out[5] | ((chip->eg_serial_bit ^ 1) << 14) | ((chip->pg_serial & 1) << 15); if (chip->mode_test[7]) { return testdata & 255; } else { return testdata >> 8; } } return (chip->write_busy << 7) | (chip->timer_b_status << 1) | chip->timer_a_status; } uint8_t OPM_ReadIRQ(opm_t *chip, uint32_t port) { return chip->timer_irq; } uint8_t OPM_ReadCT1(opm_t *chip) { return chip->io_ct1; } uint8_t OPM_ReadCT2(opm_t *chip) { if(chip->mode_test[3]) { return chip->lfo_clock_test; } return chip->io_ct2; } void OPM_SetIC(opm_t *chip, uint8_t ic) { if (chip->ic != ic) { chip->ic = ic; if (!ic) { chip->cycles = 0; } } } #if 0 void OPM_Reset(opm_t *chip) { uint32_t i; memset(chip, 0, sizeof(opm_t)); OPM_SetIC(chip, 1); for (i = 0; i < 32 * 64; i++) { OPM_Clock(chip, NULL, NULL, NULL, NULL); } OPM_SetIC(chip, 0); } #endif void OPM_WriteBuffered(opm_t *chip, uint32_t port, uint8_t data) { uint64_t time1, time2; int32_t buffer[2]; uint64_t skip; if (chip->writebuf[chip->writebuf_last].port & 0x02) { OPM_Write(chip, chip->writebuf[chip->writebuf_last].port & 0x01, chip->writebuf[chip->writebuf_last].data); chip->writebuf[chip->writebuf_last].port &= 0x01; chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; while (skip--) { OPM_Clock(chip, buffer, NULL, NULL, NULL); } } chip->writebuf[chip->writebuf_last].port = (port & 0x01) | 0x02; chip->writebuf[chip->writebuf_last].data = data; time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; if (time1 < time2) { time1 = time2; } chip->writebuf[chip->writebuf_last].time = time1; chip->writebuf_lasttime = time1; chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; } void OPM_GenerateResampled(opm_t *chip, int32_t *buf) { uint32_t i; int32_t buffer[2]; while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; chip->samples[0] = chip->samples[1] = 0; for (i = 0; i < 32; i++) { OPM_Clock(chip, buffer, NULL, NULL, NULL); if (i == 0) { chip->samples[0] += buffer[0]; chip->samples[1] += buffer[1]; } while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) { if (!(chip->writebuf[chip->writebuf_cur].port & 0x02)) { break; } chip->writebuf[chip->writebuf_cur].port &= 0x01; OPM_Write(chip, chip->writebuf[chip->writebuf_cur].port, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } chip->samplecnt -= chip->rateratio; } buf[0] = (int32_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + chip->samples[0] * chip->samplecnt) / chip->rateratio); buf[1] = (int32_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + chip->samples[1] * chip->samplecnt) / chip->rateratio); chip->samplecnt += 1 << RSM_FRAC; } void OPM_GenerateStream(opm_t *chip, int32_t **sndptr, uint32_t numsamples) { uint32_t i; int32_t *smpl, *smpr; int32_t buffer[2]; smpl = sndptr[0]; smpr = sndptr[1]; for (i = 0; i < numsamples; i++) { OPM_GenerateResampled(chip, buffer); *smpl++ = buffer[0]; *smpr++ = buffer[1]; } } void OPM_Reset(opm_t* chip, uint32_t rate, uint32_t clock) { uint32_t i, rateratio; rateratio = chip->rateratio; memset(chip, 0, sizeof(opm_t)); OPM_SetIC(chip, 1); for (i = 0; i < 32 * 64; i++) { OPM_Clock(chip, NULL, NULL, NULL, NULL); } OPM_SetIC(chip, 0); if (rate != 0) { chip->rateratio = (uint32_t)((((uint64_t)64 * rate) << RSM_FRAC) / clock); } else { chip->rateratio = rateratio; } } void OPM_SetMute(opm_t *chip, uint32_t mute) { uint32_t i; for (i = 0; i < 8; i++) { chip->mute[i] = (mute >> i) & 0x01; } } ================================================ FILE: VGMPlay/chips/opm.h ================================================ /* Nuked OPM * Copyright (C) 2020 Nuke.YKT * * This file is part of Nuked OPM. * * Nuked OPM is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 * of the License, or (at your option) any later version. * * Nuked OPM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Nuked OPM. If not, see . * * Nuked OPM emulator. * Thanks: * siliconpr0n.org(digshadow, John McMaster): * YM2151 and other FM chip decaps and die shots. * * version: 0.9 beta */ #ifndef _OPM_H_ #define _OPM_H_ #include "emutypes.h" #define RSM_FRAC 10 #define OPN_WRITEBUF_SIZE 2048 #define OPN_WRITEBUF_DELAY 36 typedef struct { uint64_t time; uint8_t port; uint8_t data; } opm_writebuf; typedef struct { uint32_t cycles; uint8_t ic; uint8_t ic2; // IO uint8_t write_data; uint8_t write_a; uint8_t write_a_en; uint8_t write_d; uint8_t write_d_en; uint8_t write_busy; uint8_t write_busy_cnt; uint8_t mode_address; uint8_t io_ct1; uint8_t io_ct2; // LFO uint8_t lfo_am_lock; uint8_t lfo_pm_lock; uint8_t lfo_counter1; uint8_t lfo_counter1_of1; uint8_t lfo_counter1_of2; uint16_t lfo_counter2; uint8_t lfo_counter2_load; uint8_t lfo_counter2_of; uint8_t lfo_counter2_of_lock; uint8_t lfo_counter2_of_lock2; uint8_t lfo_counter3_clock; uint16_t lfo_counter3; uint8_t lfo_counter3_step; uint8_t lfo_frq_update; uint8_t lfo_clock; uint8_t lfo_clock_lock; uint8_t lfo_clock_test; uint8_t lfo_test; uint32_t lfo_val; uint8_t lfo_val_carry; uint32_t lfo_out1; uint32_t lfo_out2; uint32_t lfo_out2_b; uint8_t lfo_mult_carry; uint8_t lfo_trig_sign; uint8_t lfo_saw_sign; uint8_t lfo_bit_counter; // Env Gen uint8_t eg_state[32]; uint16_t eg_level[32]; uint8_t eg_rate[2]; uint8_t eg_sl[2]; uint8_t eg_tl[3]; uint8_t eg_zr[2]; uint8_t eg_timershift_lock; uint8_t eg_timer_lock; uint8_t eg_inchi; uint8_t eg_shift; uint8_t eg_clock; uint8_t eg_clockcnt; uint8_t eg_clockquotinent; uint8_t eg_inc; uint8_t eg_ratemax[2]; uint8_t eg_instantattack; uint8_t eg_inclinear; uint8_t eg_incattack; uint8_t eg_mute; uint16_t eg_outtemp[2]; uint16_t eg_out[2]; uint8_t eg_am; uint8_t eg_ams[2]; uint8_t eg_timercarry; uint32_t eg_timer; uint32_t eg_timer2; uint8_t eg_timerbstop; uint32_t eg_serial; uint8_t eg_serial_bit; uint8_t eg_test; // Phase Gen uint16_t pg_fnum[32]; uint8_t pg_kcode[32]; uint32_t pg_inc[32]; uint32_t pg_phase[32]; uint8_t pg_reset[32]; uint8_t pg_reset_latch[32]; uint32_t pg_serial; // Operator uint16_t op_phase_in; uint16_t op_mod_in; uint16_t op_phase; uint16_t op_logsin[3]; uint16_t op_atten; uint16_t op_exp[2]; uint8_t op_pow[2]; uint32_t op_sign; int16_t op_out[6]; uint32_t op_connect; uint8_t op_counter; uint8_t op_fbupdate; uint8_t op_fbshift; uint8_t op_c1update; uint8_t op_modtable[5]; int16_t op_m1[8][2]; int16_t op_c1[8]; int16_t op_mod[3]; int16_t op_fb[2]; uint8_t op_mixl; uint8_t op_mixr; // Mixer int32_t mix[2]; int32_t mix2[2]; int32_t mix_op; uint32_t mix_serial[2]; uint32_t mix_bits; uint32_t mix_top_bits_lock; uint8_t mix_sign_lock; uint8_t mix_sign_lock2; uint8_t mix_exp_lock; uint8_t mix_clamp_low[2]; uint8_t mix_clamp_high[2]; uint8_t mix_out_bit; // Output uint8_t smp_so; uint8_t smp_sh1; uint8_t smp_sh2; // Noise uint32_t noise_lfsr; uint32_t noise_timer; uint8_t noise_timer_of; uint8_t noise_update; uint8_t noise_temp; // Register set uint8_t mode_test[8]; uint8_t mode_kon_operator[4]; uint8_t mode_kon_channel; uint8_t reg_address; uint8_t reg_address_ready; uint8_t reg_data; uint8_t reg_data_ready; uint8_t ch_rl[8]; uint8_t ch_fb[8]; uint8_t ch_connect[8]; uint8_t ch_kc[8]; uint8_t ch_kf[8]; uint8_t ch_pms[8]; uint8_t ch_ams[8]; uint8_t sl_dt1[32]; uint8_t sl_mul[32]; uint8_t sl_tl[32]; uint8_t sl_ks[32]; uint8_t sl_ar[32]; uint8_t sl_am_e[32]; uint8_t sl_d1r[32]; uint8_t sl_dt2[32]; uint8_t sl_d2r[32]; uint8_t sl_d1l[32]; uint8_t sl_rr[32]; uint8_t noise_en; uint8_t noise_freq; // Timer uint16_t timer_a_reg; uint8_t timer_b_reg; uint8_t timer_a_temp; uint8_t timer_a_do_reset, timer_a_do_load; uint8_t timer_a_inc; uint16_t timer_a_val; uint8_t timer_a_of; uint8_t timer_a_load; uint8_t timer_a_status; uint8_t timer_b_sub; uint8_t timer_b_sub_of; uint8_t timer_b_inc; uint16_t timer_b_val; uint8_t timer_b_of; uint8_t timer_b_do_reset, timer_b_do_load; uint8_t timer_b_temp; uint8_t timer_b_status; uint8_t timer_irq; uint8_t lfo_freq_hi; uint8_t lfo_freq_lo; uint8_t lfo_pmd; uint8_t lfo_amd; uint8_t lfo_wave; uint8_t timer_irqa, timer_irqb; uint8_t timer_loada, timer_loadb; uint8_t timer_reseta, timer_resetb; uint8_t mode_csm; uint8_t nc_active, nc_active_lock, nc_sign, nc_sign_lock, nc_sign_lock2; uint8_t nc_bit; uint16_t nc_out; int16_t op_mix; uint8_t kon_csm, kon_csm_lock; uint8_t kon_do; uint8_t kon_chanmatch; uint8_t kon[32]; uint8_t kon2[32]; uint8_t mode_kon[32]; // DAC uint8_t dac_osh1, dac_osh2; uint16_t dac_bits; int32_t dac_output[2]; uint32_t mute[8]; int32_t rateratio; int32_t samplecnt; int32_t oldsamples[2]; int32_t samples[2]; uint64_t writebuf_samplecnt; uint32_t writebuf_cur; uint32_t writebuf_last; uint64_t writebuf_lasttime; opm_writebuf writebuf[OPN_WRITEBUF_SIZE]; } opm_t; void OPM_GenerateStream(opm_t *chip, int32_t **sndptr, uint32_t numsamples); void OPM_Reset(opm_t* chip, uint32_t rate, uint32_t clock); void OPM_WriteBuffered(opm_t* chip, uint32_t port, uint8_t data); void OPM_SetMute(opm_t *chip, uint32_t mute); #endif ================================================ FILE: VGMPlay/chips/panning.c ================================================ #include #include #include "panning.h" #ifndef PI #define PI 3.14159265359 #endif #ifndef SQRT2 #define SQRT2 1.414213562 #endif #define RANGE 512 //----------------------------------------------------------------- // Set the panning values for the two stereo channels (L,R) // for a position -256..0..256 L..C..R //----------------------------------------------------------------- void calc_panning(float channels[2], int position) { if ( position > RANGE / 2 ) position = RANGE / 2; else if ( position < -RANGE / 2 ) position = -RANGE / 2; position += RANGE / 2; // make -256..0..256 -> 0..256..512 // Equal power law: equation is // right = sin( position / range * pi / 2) * sqrt( 2 ) // left is equivalent to right with position = range - position // position is in the range 0 .. RANGE // RANGE / 2 = centre, result = 1.0f channels[1] = (float)( sin( (double)position / RANGE * PI / 2 ) * SQRT2 ); position = RANGE - position; channels[0] = (float)( sin( (double)position / RANGE * PI / 2 ) * SQRT2 ); } //----------------------------------------------------------------- // Reset the panning values to the centre position //----------------------------------------------------------------- void centre_panning(float channels[2]) { channels[0] = channels[1] = 1.0f; } /*//----------------------------------------------------------------- // Generate a stereo position in the range 0..RANGE // with Gaussian distribution, mean RANGE/2, S.D. RANGE/5 //----------------------------------------------------------------- int random_stereo() { int n = (int)(RANGE/2 + gauss_rand() * (RANGE * 0.2) ); if ( n > RANGE ) n = RANGE; if ( n < 0 ) n = 0; return n; } //----------------------------------------------------------------- // Generate a Gaussian random number with mean 0, variance 1 // Copied from an ancient C newsgroup FAQ //----------------------------------------------------------------- double gauss_rand() { static double V1, V2, S; static int phase = 0; double X; if(phase == 0) { do { double U1 = (double)rand() / RAND_MAX; double U2 = (double)rand() / RAND_MAX; V1 = 2 * U1 - 1; V2 = 2 * U2 - 1; S = V1 * V1 + V2 * V2; } while(S >= 1 || S == 0); X = V1 * sqrt(-2 * log(S) / S); } else X = V2 * sqrt(-2 * log(S) / S); phase = 1 - phase; return X; }*/ ================================================ FILE: VGMPlay/chips/panning.h ================================================ /* panning.h by Maxim in 2006 Implements "simple equal power" panning using sine distribution I am not an expert on this stuff, but this is the best sounding of the methods I've tried */ #ifndef PANNING_H #define PANNING_H void calc_panning(float channels[2], int position); void centre_panning(float channels[2]); #endif ================================================ FILE: VGMPlay/chips/pokey.c ================================================ /***************************************************************************** * * POKEY chip emulator 4.51 * Copyright Nicola Salmoria and the MAME Team * * Based on original info found in Ron Fries' Pokey emulator, * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller, * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual. * Polynome algorithms according to info supplied by Perry McFarlane. * * This code is subject to the MAME license, which besides other * things means it is distributed as is, no warranties whatsoever. * For more details read mame.txt that comes with MAME. * * 4.51: * - changed to use the attotime datatype * 4.5: * - changed the 9/17 bit polynomial formulas such that the values * required for the Tempest Pokey protection will be found. * Tempest expects the upper 4 bits of the RNG to appear in the * lower 4 bits after four cycles, so there has to be a shift * of 1 per cycle (which was not the case before). Bits #6-#13 of the * new RNG give this expected result now, bits #0-7 of the 9 bit poly. * - reading the RNG returns the shift register contents ^ 0xff. * That way resetting the Pokey with SKCTL (which resets the * polynome shifters to 0) returns the expected 0xff value. * 4.4: * - reversed sample values to make OFF channels produce a zero signal. * actually de-reversed them; don't remember that I reversed them ;-/ * 4.3: * - for POT inputs returning zero, immediately assert the ALLPOT * bit after POTGO is written, otherwise start trigger timer * depending on SK_PADDLE mode, either 1-228 scanlines or 1-2 * scanlines, depending on the SK_PADDLE bit of SKCTL. * 4.2: * - half volume for channels which are inaudible (this should be * close to the real thing). * 4.1: * - default gain increased to closely match the old code. * - random numbers repeat rate depends on POLY9 flag too! * - verified sound output with many, many Atari 800 games, * including the SUPPRESS_INAUDIBLE optimizations. * 4.0: * - rewritten from scratch. * - 16bit stream interface. * - serout ready/complete delayed interrupts. * - reworked pot analog/digital conversion timing. * - optional non-indexing pokey update functions. * *****************************************************************************/ #include "mamedef.h" //#include "emu.h" #ifdef _DEBUG #include #endif #include "pokey.h" /* * Defining this produces much more (about twice as much) * but also more efficient code. Ideally this should be set * for processors with big code cache and for healthy compilers :) */ #ifndef BIG_SWITCH #ifndef HEAVY_MACRO_USAGE #define HEAVY_MACRO_USAGE 1 #endif #else #define HEAVY_MACRO_USAGE BIG_SWITCH #endif #define SUPPRESS_INAUDIBLE 1 /* Four channels with a range of 0..32767 and volume 0..15 */ //#define POKEY_DEFAULT_GAIN (32767/15/4) /* * But we raise the gain and risk clipping, the old Pokey did * this too. It defined POKEY_DEFAULT_GAIN 6 and this was * 6 * 15 * 4 = 360, 360/256 = 1.40625 * I use 15/11 = 1.3636, so this is a little lower. */ #define POKEY_DEFAULT_GAIN (32767/11/4) #define VERBOSE 0 #define VERBOSE_SOUND 0 #define VERBOSE_TIMER 0 #define VERBOSE_POLY 0 #define VERBOSE_RAND 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0) #define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0) #define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0) #define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0) #define CHAN1 0 #define CHAN2 1 #define CHAN3 2 #define CHAN4 3 #define TIMER1 0 #define TIMER2 1 #define TIMER4 2 /* values to add to the divisors for the different modes */ #define DIVADD_LOCLK 1 #define DIVADD_HICLK 4 #define DIVADD_HICLK_JOINED 7 /* AUDCx */ #define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */ #define POLY4 0x40 /* selects POLY4 or POLY17 */ #define PURE 0x20 /* selects POLY4/17 or PURE tone */ #define VOLUME_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */ #define VOLUME_MASK 0x0f /* volume mask */ /* AUDCTL */ #define POLY9 0x80 /* selects POLY9 or POLY17 */ #define CH1_HICLK 0x40 /* selects 1.78979 MHz for Ch 1 */ #define CH3_HICLK 0x20 /* selects 1.78979 MHz for Ch 3 */ #define CH12_JOINED 0x10 /* clocks channel 1 w/channel 2 */ #define CH34_JOINED 0x08 /* clocks channel 3 w/channel 4 */ #define CH1_FILTER 0x04 /* selects channel 1 high pass filter */ #define CH2_FILTER 0x02 /* selects channel 2 high pass filter */ #define CLK_15KHZ 0x01 /* selects 15.6999 kHz or 63.9211 kHz */ /* IRQEN (D20E) */ #define IRQ_BREAK 0x80 /* BREAK key pressed interrupt */ #define IRQ_KEYBD 0x40 /* keyboard data ready interrupt */ #define IRQ_SERIN 0x20 /* serial input data ready interrupt */ #define IRQ_SEROR 0x10 /* serial output register ready interrupt */ #define IRQ_SEROC 0x08 /* serial output complete interrupt */ #define IRQ_TIMR4 0x04 /* timer channel #4 interrupt */ #define IRQ_TIMR2 0x02 /* timer channel #2 interrupt */ #define IRQ_TIMR1 0x01 /* timer channel #1 interrupt */ /* SKSTAT (R/D20F) */ #define SK_FRAME 0x80 /* serial framing error */ #define SK_OVERRUN 0x40 /* serial overrun error */ #define SK_KBERR 0x20 /* keyboard overrun error */ #define SK_SERIN 0x10 /* serial input high */ #define SK_SHIFT 0x08 /* shift key pressed */ #define SK_KEYBD 0x04 /* keyboard key pressed */ #define SK_SEROUT 0x02 /* serial output active */ /* SKCTL (W/D20F) */ #define SK_BREAK 0x80 /* serial out break signal */ #define SK_BPS 0x70 /* bits per second */ #define SK_FM 0x08 /* FM mode */ #define SK_PADDLE 0x04 /* fast paddle a/d conversion */ #define SK_RESET 0x03 /* reset serial/keyboard interface */ #define DIV_64 28 /* divisor for 1.78979 MHz clock to 63.9211 kHz */ #define DIV_15 114 /* divisor for 1.78979 MHz clock to 15.6999 kHz */ typedef struct _pokey_state pokey_state; struct _pokey_state { INT32 counter[4]; /* channel counter */ INT32 divisor[4]; /* channel divisor (modulo value) */ UINT32 volume[4]; /* channel volume - derived */ UINT8 output[4]; /* channel output signal (1 active, 0 inactive) */ UINT8 audible[4]; /* channel plays an audible tone/effect */ UINT8 Muted[4]; UINT32 samplerate_24_8; /* sample rate in 24.8 format */ UINT32 samplepos_fract; /* sample position fractional part */ UINT32 samplepos_whole; /* sample position whole part */ UINT32 polyadjust; /* polynome adjustment */ UINT32 p4; /* poly4 index */ UINT32 p5; /* poly5 index */ UINT32 p9; /* poly9 index */ UINT32 p17; /* poly17 index */ UINT32 r9; /* rand9 index */ UINT32 r17; /* rand17 index */ UINT32 clockmult; /* clock multiplier */ //device_t *device; //sound_stream * channel; /* streams channel */ //emu_timer *timer[3]; /* timers for channel 1,2 and 4 events */ //attotime timer_period[3]; /* computed periods for these timers */ //int timer_param[3]; /* computed parameters for these timers */ //emu_timer *rtimer; /* timer for calculating the random offset */ //emu_timer *ptimer[8]; /* pot timers */ //devcb_resolved_read8 pot_r[8]; //devcb_resolved_read8 allpot_r; //devcb_resolved_read8 serin_r; //devcb_resolved_write8 serout_w; //void (*interrupt_cb)(device_t *device, int mask); UINT8 AUDF[4]; /* AUDFx (D200, D202, D204, D206) */ UINT8 AUDC[4]; /* AUDCx (D201, D203, D205, D207) */ UINT8 POTx[8]; /* POTx (R/D200-D207) */ UINT8 AUDCTL; /* AUDCTL (W/D208) */ UINT8 ALLPOT; /* ALLPOT (R/D208) */ UINT8 KBCODE; /* KBCODE (R/D209) */ UINT8 RANDOM; /* RANDOM (R/D20A) */ UINT8 SERIN; /* SERIN (R/D20D) */ UINT8 SEROUT; /* SEROUT (W/D20D) */ UINT8 IRQST; /* IRQST (R/D20E) */ UINT8 IRQEN; /* IRQEN (W/D20E) */ UINT8 SKSTAT; /* SKSTAT (R/D20F) */ UINT8 SKCTL; /* SKCTL (W/D20F) */ //pokey_interface intf; //attotime clock_period; double clock_period; //attotime ad_time_fast; //attotime ad_time_slow; UINT8 poly4[0x0f]; UINT8 poly5[0x1f]; UINT8 poly9[0x1ff]; UINT8 poly17[0x1ffff]; UINT8 rand9[0x1ff]; UINT8 rand17[0x1ffff]; }; #define P4(chip) chip->poly4[chip->p4] #define P5(chip) chip->poly5[chip->p5] #define P9(chip) chip->poly9[chip->p9] #define P17(chip) chip->poly17[chip->p17] //static TIMER_CALLBACK( pokey_timer_expire ); //static TIMER_CALLBACK( pokey_pot_trigger ); #define SAMPLE -1 #define ADJUST_EVENT(chip) \ chip->counter[CHAN1] -= event; \ chip->counter[CHAN2] -= event; \ chip->counter[CHAN3] -= event; \ chip->counter[CHAN4] -= event; \ chip->samplepos_whole -= event; \ chip->polyadjust += event #if SUPPRESS_INAUDIBLE #define PROCESS_CHANNEL(chip,ch) \ int toggle = 0; \ ADJUST_EVENT(chip); \ /* reset the channel counter */ \ if( chip->audible[ch] ) \ chip->counter[ch] = chip->divisor[ch]; \ else \ chip->counter[ch] = 0x7fffffff; \ chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \ chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \ chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \ chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \ chip->polyadjust = 0; \ if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \ { \ if( chip->AUDC[ch] & PURE ) \ toggle = 1; \ else \ if( chip->AUDC[ch] & POLY4 ) \ toggle = chip->output[ch] == !P4(chip); \ else \ if( chip->AUDCTL & POLY9 ) \ toggle = chip->output[ch] == !P9(chip); \ else \ toggle = chip->output[ch] == !P17(chip); \ } \ if( toggle ) \ { \ if( chip->audible[ch] && ! chip->Muted[ch] ) \ { \ if( chip->output[ch] ) \ sum -= chip->volume[ch]; \ else \ sum += chip->volume[ch]; \ } \ chip->output[ch] ^= 1; \ } \ /* is this a filtering channel (3/4) and is the filter active? */ \ if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \ { \ if( chip->output[ch-2] ) \ { \ chip->output[ch-2] = 0; \ if( chip->audible[ch] && ! chip->Muted[ch] ) \ sum -= chip->volume[ch-2]; \ } \ } \ #else #define PROCESS_CHANNEL(chip,ch) \ int toggle = 0; \ ADJUST_EVENT(chip); \ /* reset the channel counter */ \ chip->counter[ch] = p[chip].divisor[ch]; \ chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \ chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \ chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \ chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \ chip->polyadjust = 0; \ if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \ { \ if( chip->AUDC[ch] & PURE ) \ toggle = 1; \ else \ if( chip->AUDC[ch] & POLY4 ) \ toggle = chip->output[ch] == !P4(chip); \ else \ if( chip->AUDCTL & POLY9 ) \ toggle = chip->output[ch] == !P9(chip); \ else \ toggle = chip->output[ch] == !P17(chip); \ } \ if( toggle && ! chip->Muted[ch] ) \ { \ if( chip->output[ch] ) \ sum -= chip->volume[ch]; \ else \ sum += chip->volume[ch]; \ chip->output[ch] ^= 1; \ } \ /* is this a filtering channel (3/4) and is the filter active? */ \ if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \ { \ if( chip->output[ch-2] && ! chip->Muted[ch] ) \ { \ chip->output[ch-2] = 0; \ sum -= chip->volume[ch-2]; \ } \ } \ #endif #define PROCESS_SAMPLE(chip) \ ADJUST_EVENT(chip); \ /* adjust the sample position */ \ chip->samplepos_whole++; \ /* store sum of output signals into the buffer */ \ /* *buffer++ = (sum > 0x7fff) ? 0x7fff : sum; */ \ *bufL++ = *bufR++ = sum; \ samples-- #if HEAVY_MACRO_USAGE /* * This version of PROCESS_POKEY repeats the search for the minimum * event value without using an index to the channel. That way the * PROCESS_CHANNEL macros can be called with fixed values and expand * to much more efficient code */ #define PROCESS_POKEY(chip) \ UINT32 sum = 0; \ if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \ sum += chip->volume[CHAN1]; \ if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \ sum += chip->volume[CHAN2]; \ if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \ sum += chip->volume[CHAN3]; \ if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \ sum += chip->volume[CHAN4]; \ while( samples > 0 ) \ { \ if( chip->counter[CHAN1] < chip->samplepos_whole ) \ { \ if( chip->counter[CHAN2] < chip->counter[CHAN1] ) \ { \ if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \ { \ if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN3]; \ PROCESS_CHANNEL(chip,CHAN3); \ } \ } \ else \ if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN2]; \ PROCESS_CHANNEL(chip,CHAN2); \ } \ } \ else \ if( chip->counter[CHAN3] < chip->counter[CHAN1] ) \ { \ if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN3]; \ PROCESS_CHANNEL(chip,CHAN3); \ } \ } \ else \ if( chip->counter[CHAN4] < chip->counter[CHAN1] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN1]; \ PROCESS_CHANNEL(chip,CHAN1); \ } \ } \ else \ if( chip->counter[CHAN2] < chip->samplepos_whole ) \ { \ if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \ { \ if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN3]; \ PROCESS_CHANNEL(chip,CHAN3); \ } \ } \ else \ if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN2]; \ PROCESS_CHANNEL(chip,CHAN2); \ } \ } \ else \ if( chip->counter[CHAN3] < chip->samplepos_whole ) \ { \ if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->counter[CHAN3]; \ PROCESS_CHANNEL(chip,CHAN3); \ } \ } \ else \ if( chip->counter[CHAN4] < chip->samplepos_whole ) \ { \ UINT32 event = chip->counter[CHAN4]; \ PROCESS_CHANNEL(chip,CHAN4); \ } \ else \ { \ UINT32 event = chip->samplepos_whole; \ PROCESS_SAMPLE(chip); \ } \ }/* \ chip->rtimer->adjust(attotime::never)*/ #else /* no HEAVY_MACRO_USAGE */ /* * And this version of PROCESS_POKEY uses event and channel variables * so that the PROCESS_CHANNEL macro needs to index memory at runtime. */ #define PROCESS_POKEY(chip) \ UINT32 sum = 0; \ if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \ sum += chip->volume[CHAN1]; \ if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \ sum += chip->volume[CHAN2]; \ if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \ sum += chip->volume[CHAN3]; \ if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \ sum += chip->volume[CHAN4]; \ while( samples > 0 ) \ { \ UINT32 event = chip->samplepos_whole; \ UINT32 channel = SAMPLE; \ if( chip->counter[CHAN1] < event ) \ { \ event = chip->counter[CHAN1]; \ channel = CHAN1; \ } \ if( chip->counter[CHAN2] < event ) \ { \ event = chip->counter[CHAN2]; \ channel = CHAN2; \ } \ if( chip->counter[CHAN3] < event ) \ { \ event = chip->counter[CHAN3]; \ channel = CHAN3; \ } \ if( chip->counter[CHAN4] < event ) \ { \ event = chip->counter[CHAN4]; \ channel = CHAN4; \ } \ if( channel == SAMPLE ) \ { \ PROCESS_SAMPLE(chip); \ } \ else \ { \ PROCESS_CHANNEL(chip,channel); \ } \ }/* \ chip->rtimer->adjust(attotime::never)*/ #endif /*INLINE pokey_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == POKEY); return (pokey_state *)downcast(device)->token(); }*/ #define MAX_CHIPS 0x02 static pokey_state PokeyData[MAX_CHIPS]; //static STREAM_UPDATE( pokey_update ) void pokey_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //pokey_state *chip = (pokey_state *)param; pokey_state *chip = &PokeyData[ChipID]; //stream_sample_t *buffer = outputs[0]; stream_sample_t *bufL = outputs[0]; stream_sample_t *bufR = outputs[1]; PROCESS_POKEY(chip); } static void poly_init(UINT8 *poly, int size, int left, int right, int add) { int mask = (1 << size) - 1; int i, x = 0; LOG_POLY(("poly %d\n", size)); for( i = 0; i < mask; i++ ) { *poly++ = x & 1; LOG_POLY(("%05x: %d\n", x, x&1)); /* calculate next bit */ x = ((x << left) + (x >> right) + add) & mask; } } static void rand_init(UINT8 *rng, int size, int left, int right, int add) { int mask = (1 << size) - 1; int i, x = 0; LOG_RAND(("rand %d\n", size)); for( i = 0; i < mask; i++ ) { if (size == 17) *rng = x >> 6; /* use bits 6..13 */ else *rng = x; /* use bits 0..7 */ LOG_RAND(("%05x: %02x\n", x, *rng)); rng++; /* calculate next bit */ x = ((x << left) + (x >> right) + add) & mask; } } /*static void register_for_save(pokey_state *chip, device_t *device) { device->save_item(NAME(chip->counter)); device->save_item(NAME(chip->divisor)); device->save_item(NAME(chip->volume)); device->save_item(NAME(chip->output)); device->save_item(NAME(chip->audible)); device->save_item(NAME(chip->samplepos_fract)); device->save_item(NAME(chip->samplepos_whole)); device->save_item(NAME(chip->polyadjust)); device->save_item(NAME(chip->p4)); device->save_item(NAME(chip->p5)); device->save_item(NAME(chip->p9)); device->save_item(NAME(chip->p17)); device->save_item(NAME(chip->r9)); device->save_item(NAME(chip->r17)); device->save_item(NAME(chip->clockmult)); device->save_item(NAME(chip->timer_period[0])); device->save_item(NAME(chip->timer_period[1])); device->save_item(NAME(chip->timer_period[2])); device->save_item(NAME(chip->timer_param)); device->save_item(NAME(chip->AUDF)); device->save_item(NAME(chip->AUDC)); device->save_item(NAME(chip->POTx)); device->save_item(NAME(chip->AUDCTL)); device->save_item(NAME(chip->ALLPOT)); device->save_item(NAME(chip->KBCODE)); device->save_item(NAME(chip->RANDOM)); device->save_item(NAME(chip->SERIN)); device->save_item(NAME(chip->SEROUT)); device->save_item(NAME(chip->IRQST)); device->save_item(NAME(chip->IRQEN)); device->save_item(NAME(chip->SKSTAT)); device->save_item(NAME(chip->SKCTL)); }*/ //static DEVICE_START( pokey ) int device_start_pokey(UINT8 ChipID, int clock) { //pokey_state *chip = get_safe_token(device); pokey_state *chip; //int sample_rate = device->clock(); int sample_rate = clock; //int i; if (ChipID >= MAX_CHIPS) return 0; chip = &PokeyData[ChipID]; //if (device->static_config()) // memcpy(&chip->intf, device->static_config(), sizeof(pokey_interface)); //chip->device = device; //chip->clock_period = attotime::from_hz(device->clock()); chip->clock_period = 1.0 / clock; /* calculate the A/D times * In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion * takes N scanlines, where N is the paddle value. A single scanline * takes approximately 64us to finish (1.78979MHz clock). * In quick mode (SK_PADDLE set) the conversion is done very fast * (takes two scanlines) but the result is not as accurate. */ //chip->ad_time_fast = (attotime::from_nsec(64000*2/228) * FREQ_17_EXACT) / device->clock(); //chip->ad_time_slow = (attotime::from_nsec(64000 ) * FREQ_17_EXACT) / device->clock(); /* initialize the poly counters */ poly_init(chip->poly4, 4, 3, 1, 0x00004); poly_init(chip->poly5, 5, 3, 2, 0x00008); poly_init(chip->poly9, 9, 8, 1, 0x00180); poly_init(chip->poly17, 17,16, 1, 0x1c000); /* initialize the random arrays */ rand_init(chip->rand9, 9, 8, 1, 0x00180); rand_init(chip->rand17, 17,16, 1, 0x1c000); //chip->samplerate_24_8 = (device->clock() << 8) / sample_rate; chip->samplerate_24_8 = (clock << 8) / sample_rate; chip->divisor[CHAN1] = 4; chip->divisor[CHAN2] = 4; chip->divisor[CHAN3] = 4; chip->divisor[CHAN4] = 4; chip->clockmult = DIV_64; chip->KBCODE = 0x09; /* Atari 800 'no key' */ chip->SKCTL = SK_RESET; /* let the RNG run after reset */ //chip->rtimer = device->machine().scheduler().timer_alloc(FUNC_NULL); //chip->timer[0] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); //chip->timer[1] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); //chip->timer[2] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); /*for (i=0; i<8; i++) { chip->ptimer[i] = device->machine().scheduler().timer_alloc(FUNC(pokey_pot_trigger), chip); chip->pot_r[i].resolve(chip->intf.pot_r[i], *device); } chip->allpot_r.resolve(chip->intf.allpot_r, *device); chip->serin_r.resolve(chip->intf.serin_r, *device); chip->serout_w.resolve(chip->intf.serout_w, *device); chip->interrupt_cb = chip->intf.interrupt_cb;*/ //chip->channel = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, chip, pokey_update); //register_for_save(chip, device); return sample_rate; } void device_stop_pokey(UINT8 ChipID) { pokey_state *chip = &PokeyData[ChipID]; return; } void device_reset_pokey(UINT8 ChipID) { pokey_state *chip = &PokeyData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) { chip->counter[CurChn] = 0; chip->divisor[CurChn] = 4; chip->volume[CurChn] = 0; chip->output[CurChn] = 0; chip->audible[CurChn] = 0; } chip->samplepos_fract = 0; chip->samplepos_whole = 0; chip->polyadjust = 0; chip->p4 = 0; chip->p5 = 0; chip->p9 = 0; chip->p17 = 0; chip->r9 = 0; chip->r17 = 0; chip->clockmult = DIV_64; return; } /*static TIMER_CALLBACK( pokey_timer_expire ) { pokey_state *p = (pokey_state *)ptr; int timers = param; LOG_TIMER(("POKEY #%p timer %d with IRQEN $%02x\n", p, timers, p->IRQEN)); // check if some of the requested timer interrupts are enabled // timers &= p->IRQEN; if( timers ) { // set the enabled timer irq status bits // p->IRQST |= timers; // call back an application supplied function to handle the interrupt // if( p->interrupt_cb ) (*p->interrupt_cb)(p->device, timers); } }*/ /*static char *audc2str(int val) { static char buff[80]; if( val & NOTPOLY5 ) { if( val & PURE ) strcpy(buff,"pure"); else if( val & POLY4 ) strcpy(buff,"poly4"); else strcpy(buff,"poly9/17"); } else { if( val & PURE ) strcpy(buff,"poly5"); else if( val & POLY4 ) strcpy(buff,"poly4+poly5"); else strcpy(buff,"poly9/17+poly5"); } return buff; } static char *audctl2str(int val) { static char buff[80]; if( val & POLY9 ) strcpy(buff,"poly9"); else strcpy(buff,"poly17"); if( val & CH1_HICLK ) strcat(buff,"+ch1hi"); if( val & CH3_HICLK ) strcat(buff,"+ch3hi"); if( val & CH12_JOINED ) strcat(buff,"+ch1/2"); if( val & CH34_JOINED ) strcat(buff,"+ch3/4"); if( val & CH1_FILTER ) strcat(buff,"+ch1filter"); if( val & CH2_FILTER ) strcat(buff,"+ch2filter"); if( val & CLK_15KHZ ) strcat(buff,"+clk15"); return buff; }*/ /*static TIMER_CALLBACK( pokey_serin_ready_cb ) { pokey_state *p = (pokey_state *)ptr; if( p->IRQEN & IRQ_SERIN ) { // set the enabled timer irq status bits // p->IRQST |= IRQ_SERIN; // call back an application supplied function to handle the interrupt // if( p->interrupt_cb ) (*p->interrupt_cb)(p->device, IRQ_SERIN); } } static TIMER_CALLBACK( pokey_serout_ready_cb ) { pokey_state *p = (pokey_state *)ptr; if( p->IRQEN & IRQ_SEROR ) { p->IRQST |= IRQ_SEROR; if( p->interrupt_cb ) (*p->interrupt_cb)(p->device, IRQ_SEROR); } } static TIMER_CALLBACK( pokey_serout_complete ) { pokey_state *p = (pokey_state *)ptr; if( p->IRQEN & IRQ_SEROC ) { p->IRQST |= IRQ_SEROC; if( p->interrupt_cb ) (*p->interrupt_cb)(p->device, IRQ_SEROC); } } static TIMER_CALLBACK( pokey_pot_trigger ) { pokey_state *p = (pokey_state *)ptr; int pot = param; LOG(("POKEY #%p POT%d triggers after %dus\n", p, pot, (int)(1000000 * p->ptimer[pot]->elapsed().as_double()))); p->ALLPOT &= ~(1 << pot); // set the enabled timer irq status bits // }*/ /*#define AD_TIME ((p->SKCTL & SK_PADDLE) ? p->ad_time_fast : p->ad_time_slow) static void pokey_potgo(pokey_state *p) { int pot; LOG(("POKEY #%p pokey_potgo\n", p)); p->ALLPOT = 0xff; for( pot = 0; pot < 8; pot++ ) { p->POTx[pot] = 0xff; if( !p->pot_r[pot].isnull() ) { int r = p->pot_r[pot](pot); LOG(("POKEY %s pot_r(%d) returned $%02x\n", p->device->tag(), pot, r)); if( r != -1 ) { if (r > 228) r = 228; // final value // p->POTx[pot] = r; p->ptimer[pot]->adjust(AD_TIME * r, pot); } } } }*/ //READ8_DEVICE_HANDLER( pokey_r ) UINT8 pokey_r(UINT8 ChipID, offs_t offset) { //pokey_state *p = get_safe_token(device); pokey_state *p = &PokeyData[ChipID]; int data = 0, pot; UINT32 adjust = 0; switch (offset & 15) { case POT0_C: case POT1_C: case POT2_C: case POT3_C: case POT4_C: case POT5_C: case POT6_C: case POT7_C: pot = offset & 7; /*if( !p->pot_r[pot].isnull() ) { // * If the conversion is not yet finished (ptimer running), * get the current value by the linear interpolation of * the final value using the elapsed time. // if( p->ALLPOT & (1 << pot) ) { //data = p->ptimer[pot]->elapsed().attoseconds / AD_TIME.attoseconds; data = p->POTx[pot]; LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", p->device->tag(), pot, data)); } else { data = p->POTx[pot]; LOG(("POKEY '%s' read POT%d (final value) $%02x\n", p->device->tag(), pot, data)); } } else logerror("%s: warning - read '%s' POT%d\n", p->device->machine().describe_context(), p->device->tag(), pot);*/ break; case ALLPOT_C: /**************************************************************** * If the 2 least significant bits of SKCTL are 0, the ALLPOTs * are disabled (SKRESET). Thanks to MikeJ for pointing this out. ****************************************************************/ /*if( (p->SKCTL & SK_RESET) == 0) { data = 0; LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", p->device->tag(), data)); } else if( !p->allpot_r.isnull() ) { data = p->allpot_r(offset); LOG(("POKEY '%s' ALLPOT callback $%02x\n", p->device->tag(), data)); } else { data = p->ALLPOT; LOG(("POKEY '%s' ALLPOT internal $%02x\n", p->device->tag(), data)); }*/ break; case KBCODE_C: data = p->KBCODE; break; case RANDOM_C: /**************************************************************** * If the 2 least significant bits of SKCTL are 0, the random * number generator is disabled (SKRESET). Thanks to Eric Smith * for pointing out this critical bit of info! If the random * number generator is enabled, get a new random number. Take * the time gone since the last read into account and read the * new value from an appropriate offset in the rand17 table. ****************************************************************/ if( p->SKCTL & SK_RESET ) { //adjust = p->rtimer->elapsed().as_double() / p->clock_period.as_double(); adjust = 0; p->r9 = (p->r9 + adjust) % 0x001ff; p->r17 = (p->r17 + adjust) % 0x1ffff; } else { adjust = 1; p->r9 = 0; p->r17 = 0; //LOG_RAND(("POKEY '%s' rand17 frozen (SKCTL): $%02x\n", p->device->tag(), p->RANDOM)); } if( p->AUDCTL & POLY9 ) { p->RANDOM = p->rand9[p->r9]; //LOG_RAND(("POKEY '%s' adjust %u rand9[$%05x]: $%02x\n", p->device->tag(), adjust, p->r9, p->RANDOM)); } else { p->RANDOM = p->rand17[p->r17]; //LOG_RAND(("POKEY '%s' adjust %u rand17[$%05x]: $%02x\n", p->device->tag(), adjust, p->r17, p->RANDOM)); } //if (adjust > 0) // p->rtimer->adjust(attotime::never); data = p->RANDOM ^ 0xff; break; case SERIN_C: //if( !p->serin_r.isnull() ) // p->SERIN = p->serin_r(offset); data = p->SERIN; //LOG(("POKEY '%s' SERIN $%02x\n", p->device->tag(), data)); break; case IRQST_C: /* IRQST is an active low input port; we keep it active high */ /* internally to ease the (un-)masking of bits */ data = p->IRQST ^ 0xff; //LOG(("POKEY '%s' IRQST $%02x\n", p->device->tag(), data)); break; case SKSTAT_C: /* SKSTAT is also an active low input port */ data = p->SKSTAT ^ 0xff; //LOG(("POKEY '%s' SKSTAT $%02x\n", p->device->tag(), data)); break; default: //LOG(("POKEY '%s' register $%02x\n", p->device->tag(), offset)); break; } return data; } /*READ8_HANDLER( quad_pokey_r ) { static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" }; int pokey_num = (offset >> 3) & ~0x04; int control = (offset & 0x20) >> 2; int pokey_reg = (offset % 8) | control; return pokey_r(space->machine().device(devname[pokey_num]), pokey_reg); }*/ //WRITE8_DEVICE_HANDLER( pokey_w ) void pokey_w(UINT8 ChipID, offs_t offset, UINT8 data) { //pokey_state *p = get_safe_token(device); pokey_state *p = &PokeyData[ChipID]; int ch_mask = 0, new_val; //p->channel->update(); /* determine which address was changed */ switch (offset & 15) { case AUDF1_C: if( data == p->AUDF[CHAN1] ) return; //LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", p->device->tag(), data)); p->AUDF[CHAN1] = data; ch_mask = 1 << CHAN1; if( p->AUDCTL & CH12_JOINED ) /* if ch 1&2 tied together */ ch_mask |= 1 << CHAN2; /* then also change on ch2 */ break; case AUDC1_C: if( data == p->AUDC[CHAN1] ) return; //LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); p->AUDC[CHAN1] = data; ch_mask = 1 << CHAN1; break; case AUDF2_C: if( data == p->AUDF[CHAN2] ) return; //LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", p->device->tag(), data)); p->AUDF[CHAN2] = data; ch_mask = 1 << CHAN2; break; case AUDC2_C: if( data == p->AUDC[CHAN2] ) return; //LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); p->AUDC[CHAN2] = data; ch_mask = 1 << CHAN2; break; case AUDF3_C: if( data == p->AUDF[CHAN3] ) return; //LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", p->device->tag(), data)); p->AUDF[CHAN3] = data; ch_mask = 1 << CHAN3; if( p->AUDCTL & CH34_JOINED ) /* if ch 3&4 tied together */ ch_mask |= 1 << CHAN4; /* then also change on ch4 */ break; case AUDC3_C: if( data == p->AUDC[CHAN3] ) return; //LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); p->AUDC[CHAN3] = data; ch_mask = 1 << CHAN3; break; case AUDF4_C: if( data == p->AUDF[CHAN4] ) return; //LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", p->device->tag(), data)); p->AUDF[CHAN4] = data; ch_mask = 1 << CHAN4; break; case AUDC4_C: if( data == p->AUDC[CHAN4] ) return; //LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); p->AUDC[CHAN4] = data; ch_mask = 1 << CHAN4; break; case AUDCTL_C: if( data == p->AUDCTL ) return; //LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", p->device->tag(), data, audctl2str(data))); p->AUDCTL = data; ch_mask = 15; /* all channels */ /* determine the base multiplier for the 'div by n' calculations */ p->clockmult = (p->AUDCTL & CLK_15KHZ) ? DIV_15 : DIV_64; break; case STIMER_C: /*// first remove any existing timers // LOG_TIMER(("POKEY '%s' STIMER $%02x\n", p->device->tag(), data)); p->timer[TIMER1]->adjust(attotime::never, p->timer_param[TIMER1]); p->timer[TIMER2]->adjust(attotime::never, p->timer_param[TIMER2]); p->timer[TIMER4]->adjust(attotime::never, p->timer_param[TIMER4]); // reset all counters to zero (side effect) // p->polyadjust = 0; p->counter[CHAN1] = 0; p->counter[CHAN2] = 0; p->counter[CHAN3] = 0; p->counter[CHAN4] = 0; // joined chan#1 and chan#2 ? // if( p->AUDCTL & CH12_JOINED ) { if( p->divisor[CHAN2] > 4 ) { LOG_TIMER(("POKEY '%s' timer1+2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2])); // set timer #1 _and_ #2 event after timer_div clocks of joined CHAN1+CHAN2 // p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2]; p->timer_param[TIMER2] = IRQ_TIMR2|IRQ_TIMR1; p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]); } } else { if( p->divisor[CHAN1] > 4 ) { LOG_TIMER(("POKEY '%s' timer1 after %d clocks\n", p->device->tag(), p->divisor[CHAN1])); // set timer #1 event after timer_div clocks of CHAN1 // p->timer_period[TIMER1] = p->clock_period * p->divisor[CHAN1]; p->timer_param[TIMER1] = IRQ_TIMR1; p->timer[TIMER1]->adjust(p->timer_period[TIMER1], p->timer_param[TIMER1], p->timer_period[TIMER1]); } if( p->divisor[CHAN2] > 4 ) { LOG_TIMER(("POKEY '%s' timer2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2])); // set timer #2 event after timer_div clocks of CHAN2 // p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2]; p->timer_param[TIMER2] = IRQ_TIMR2; p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]); } } // Note: p[chip] does not have a timer #3 // if( p->AUDCTL & CH34_JOINED ) { // not sure about this: if audc4 == 0000xxxx don't start timer 4 ? // if( p->AUDC[CHAN4] & 0xf0 ) { if( p->divisor[CHAN4] > 4 ) { LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4])); // set timer #4 event after timer_div clocks of CHAN4 // p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4]; p->timer_param[TIMER4] = IRQ_TIMR4; p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]); } } } else { if( p->divisor[CHAN4] > 4 ) { LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4])); // set timer #4 event after timer_div clocks of CHAN4 // p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4]; p->timer_param[TIMER4] = IRQ_TIMR4; p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]); } } p->timer[TIMER1]->enable(p->IRQEN & IRQ_TIMR1); p->timer[TIMER2]->enable(p->IRQEN & IRQ_TIMR2); p->timer[TIMER4]->enable(p->IRQEN & IRQ_TIMR4);*/ break; case SKREST_C: /* reset SKSTAT */ //LOG(("POKEY '%s' SKREST $%02x\n", p->device->tag(), data)); p->SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR); break; case POTGO_C: //LOG(("POKEY '%s' POTGO $%02x\n", p->device->tag(), data)); //pokey_potgo(p); break; case SEROUT_C: //LOG(("POKEY '%s' SEROUT $%02x\n", p->device->tag(), data)); //p->serout_w(offset, data); //p->SKSTAT |= SK_SEROUT; /* * These are arbitrary values, tested with some custom boot * loaders from Ballblazer and Escape from Fractalus * The real times are unknown */ //device->machine().scheduler().timer_set(attotime::from_usec(200), FUNC(pokey_serout_ready_cb), 0, p); /* 10 bits (assumption 1 start, 8 data and 1 stop bit) take how long? */ //device->machine().scheduler().timer_set(attotime::from_usec(2000), FUNC(pokey_serout_complete), 0, p); break; case IRQEN_C: //LOG(("POKEY '%s' IRQEN $%02x\n", p->device->tag(), data)); /* acknowledge one or more IRQST bits ? */ if( p->IRQST & ~data ) { /* reset IRQST bits that are masked now */ p->IRQST &= data; } else { /* enable/disable timers now to avoid unneeded breaking of the CPU cores for masked timers */ /*if( p->timer[TIMER1] && ((p->IRQEN^data) & IRQ_TIMR1) ) p->timer[TIMER1]->enable(data & IRQ_TIMR1); if( p->timer[TIMER2] && ((p->IRQEN^data) & IRQ_TIMR2) ) p->timer[TIMER2]->enable(data & IRQ_TIMR2); if( p->timer[TIMER4] && ((p->IRQEN^data) & IRQ_TIMR4) ) p->timer[TIMER4]->enable(data & IRQ_TIMR4);*/ } /* store irq enable */ p->IRQEN = data; break; case SKCTL_C: if( data == p->SKCTL ) return; //LOG(("POKEY '%s' SKCTL $%02x\n", p->device->tag(), data)); p->SKCTL = data; if( !(data & SK_RESET) ) { pokey_w(ChipID, IRQEN_C, 0); pokey_w(ChipID, SKREST_C, 0); } break; } /************************************************************ * As defined in the manual, the exact counter values are * different depending on the frequency and resolution: * 64 kHz or 15 kHz - AUDF + 1 * 1.79 MHz, 8-bit - AUDF + 4 * 1.79 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 ************************************************************/ /* only reset the channels that have changed */ if( ch_mask & (1 << CHAN1) ) { /* process channel 1 frequency */ if( p->AUDCTL & CH1_HICLK ) new_val = p->AUDF[CHAN1] + DIVADD_HICLK; else new_val = (p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan1 %d\n", p->device->tag(), new_val)); p->volume[CHAN1] = (p->AUDC[CHAN1] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; p->divisor[CHAN1] = new_val; if( new_val < p->counter[CHAN1] ) p->counter[CHAN1] = new_val; //if( p->interrupt_cb && p->timer[TIMER1] ) // p->timer[TIMER1]->adjust(p->clock_period * new_val, p->timer_param[TIMER1], p->timer_period[TIMER1]); p->audible[CHAN1] = !( (p->AUDC[CHAN1] & VOLUME_ONLY) || (p->AUDC[CHAN1] & VOLUME_MASK) == 0 || ((p->AUDC[CHAN1] & PURE) && new_val < (p->samplerate_24_8 >> 8))); if( !p->audible[CHAN1] ) { p->output[CHAN1] = 1; p->counter[CHAN1] = 0x7fffffff; /* 50% duty cycle should result in half volume */ p->volume[CHAN1] >>= 1; } } if( ch_mask & (1 << CHAN2) ) { /* process channel 2 frequency */ if( p->AUDCTL & CH12_JOINED ) { if( p->AUDCTL & CH1_HICLK ) new_val = p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_HICLK_JOINED; else new_val = (p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan1+2 %d\n", p->device->tag(), new_val)); } else { new_val = (p->AUDF[CHAN2] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan2 %d\n", p->device->tag(), new_val)); } p->volume[CHAN2] = (p->AUDC[CHAN2] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; p->divisor[CHAN2] = new_val; if( new_val < p->counter[CHAN2] ) p->counter[CHAN2] = new_val; //if( p->interrupt_cb && p->timer[TIMER2] ) // p->timer[TIMER2]->adjust(p->clock_period * new_val, p->timer_param[TIMER2], p->timer_period[TIMER2]); p->audible[CHAN2] = !( (p->AUDC[CHAN2] & VOLUME_ONLY) || (p->AUDC[CHAN2] & VOLUME_MASK) == 0 || ((p->AUDC[CHAN2] & PURE) && new_val < (p->samplerate_24_8 >> 8))); if( !p->audible[CHAN2] ) { p->output[CHAN2] = 1; p->counter[CHAN2] = 0x7fffffff; /* 50% duty cycle should result in half volume */ p->volume[CHAN2] >>= 1; } } if( ch_mask & (1 << CHAN3) ) { /* process channel 3 frequency */ if( p->AUDCTL & CH3_HICLK ) new_val = p->AUDF[CHAN3] + DIVADD_HICLK; else new_val = (p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan3 %d\n", p->device->tag(), new_val)); p->volume[CHAN3] = (p->AUDC[CHAN3] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; p->divisor[CHAN3] = new_val; if( new_val < p->counter[CHAN3] ) p->counter[CHAN3] = new_val; /* channel 3 does not have a timer associated */ p->audible[CHAN3] = !( (p->AUDC[CHAN3] & VOLUME_ONLY) || (p->AUDC[CHAN3] & VOLUME_MASK) == 0 || ((p->AUDC[CHAN3] & PURE) && new_val < (p->samplerate_24_8 >> 8))) || (p->AUDCTL & CH1_FILTER); if( !p->audible[CHAN3] ) { p->output[CHAN3] = 1; p->counter[CHAN3] = 0x7fffffff; /* 50% duty cycle should result in half volume */ p->volume[CHAN3] >>= 1; } } if( ch_mask & (1 << CHAN4) ) { /* process channel 4 frequency */ if( p->AUDCTL & CH34_JOINED ) { if( p->AUDCTL & CH3_HICLK ) new_val = p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_HICLK_JOINED; else new_val = (p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan3+4 %d\n", p->device->tag(), new_val)); } else { new_val = (p->AUDF[CHAN4] + DIVADD_LOCLK) * p->clockmult; //LOG_SOUND(("POKEY '%s' chan4 %d\n", p->device->tag(), new_val)); } p->volume[CHAN4] = (p->AUDC[CHAN4] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; p->divisor[CHAN4] = new_val; if( new_val < p->counter[CHAN4] ) p->counter[CHAN4] = new_val; //if( p->interrupt_cb && p->timer[TIMER4] ) // p->timer[TIMER4]->adjust(p->clock_period * new_val, p->timer_param[TIMER4], p->timer_period[TIMER4]); p->audible[CHAN4] = !( (p->AUDC[CHAN4] & VOLUME_ONLY) || (p->AUDC[CHAN4] & VOLUME_MASK) == 0 || ((p->AUDC[CHAN4] & PURE) && new_val < (p->samplerate_24_8 >> 8))) || (p->AUDCTL & CH2_FILTER); if( !p->audible[CHAN4] ) { p->output[CHAN4] = 1; p->counter[CHAN4] = 0x7fffffff; /* 50% duty cycle should result in half volume */ p->volume[CHAN4] >>= 1; } } } /*WRITE8_HANDLER( quad_pokey_w ) { static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" }; int pokey_num = (offset >> 3) & ~0x04; int control = (offset & 0x20) >> 2; int pokey_reg = (offset % 8) | control; pokey_w(space->machine().device(devname[pokey_num]), pokey_reg, data); } void pokey_serin_ready(device_t *device, int after) { pokey_state *p = get_safe_token(device); device->machine().scheduler().timer_set(p->clock_period * after, FUNC(pokey_serin_ready_cb), 0, p); } void pokey_break_w(device_t *device, int shift) { //pokey_state *p = get_safe_token(device); if( shift ) // shift code ? // p->SKSTAT |= SK_SHIFT; else p->SKSTAT &= ~SK_SHIFT; // check if the break IRQ is enabled // if( p->IRQEN & IRQ_BREAK ) { // set break IRQ status and call back the interrupt handler // p->IRQST |= IRQ_BREAK; if( p->interrupt_cb ) (*p->interrupt_cb)(device, IRQ_BREAK); } } void pokey_kbcode_w(device_t *device, int kbcode, int make) { pokey_state *p = get_safe_token(device); // make code ? // if( make ) { p->KBCODE = kbcode; p->SKSTAT |= SK_KEYBD; if( kbcode & 0x40 ) // shift code ? // p->SKSTAT |= SK_SHIFT; else p->SKSTAT &= ~SK_SHIFT; if( p->IRQEN & IRQ_KEYBD ) { // last interrupt not acknowledged ? // if( p->IRQST & IRQ_KEYBD ) p->SKSTAT |= SK_KBERR; p->IRQST |= IRQ_KEYBD; if( p->interrupt_cb ) (*p->interrupt_cb)(device, IRQ_KEYBD); } } else { p->KBCODE = kbcode; p->SKSTAT &= ~SK_KEYBD; } }*/ void pokey_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { pokey_state *chip = &PokeyData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( pokey ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pokey_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( pokey ); break; case DEVINFO_FCT_STOP: // Nothing // break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "POKEY"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Atari custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "4.51"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(POKEY, pokey);*/ ================================================ FILE: VGMPlay/chips/pokey.h ================================================ /***************************************************************************** * * POKEY chip emulator 4.3 * Copyright Nicola Salmoria and the MAME Team * * Based on original info found in Ron Fries' Pokey emulator, * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller. * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual. * Polynome algorithms according to info supplied by Perry McFarlane. * * This code is subject to the MAME license, which besides other * things means it is distributed as is, no warranties whatsoever. * For more details read mame.txt that comes with MAME. * *****************************************************************************/ #pragma once //#include "devlegcy.h" /* CONSTANT DEFINITIONS */ /* POKEY WRITE LOGICALS */ #define AUDF1_C 0x00 #define AUDC1_C 0x01 #define AUDF2_C 0x02 #define AUDC2_C 0x03 #define AUDF3_C 0x04 #define AUDC3_C 0x05 #define AUDF4_C 0x06 #define AUDC4_C 0x07 #define AUDCTL_C 0x08 #define STIMER_C 0x09 #define SKREST_C 0x0A #define POTGO_C 0x0B #define SEROUT_C 0x0D #define IRQEN_C 0x0E #define SKCTL_C 0x0F /* POKEY READ LOGICALS */ #define POT0_C 0x00 #define POT1_C 0x01 #define POT2_C 0x02 #define POT3_C 0x03 #define POT4_C 0x04 #define POT5_C 0x05 #define POT6_C 0x06 #define POT7_C 0x07 #define ALLPOT_C 0x08 #define KBCODE_C 0x09 #define RANDOM_C 0x0A #define SERIN_C 0x0D #define IRQST_C 0x0E #define SKSTAT_C 0x0F /* exact 1.79 MHz clock freq (of the Atari 800 that is) */ #define FREQ_17_EXACT 1789790 /***************************************************************************** * pot0_r to pot7_r: * Handlers for reading the pot values. Some Atari games use * ALLPOT to return dipswitch settings and other things. * serin_r, serout_w, interrupt_cb: * New function pointers for serial input/output and a interrupt callback. *****************************************************************************/ /*typedef struct _pokey_interface pokey_interface; struct _pokey_interface { devcb_read8 pot_r[8]; devcb_read8 allpot_r; devcb_read8 serin_r; devcb_write8 serout_w; void (*interrupt_cb)(device_t *device, int mask); };*/ void pokey_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_pokey(UINT8 ChipID, int clock); void device_stop_pokey(UINT8 ChipID); void device_reset_pokey(UINT8 ChipID); //READ8_DEVICE_HANDLER( pokey_r ); //WRITE8_DEVICE_HANDLER( pokey_w ); UINT8 pokey_r(UINT8 ChipID, offs_t offset); void pokey_w(UINT8 ChipID, offs_t offset, UINT8 data); /* fix me: eventually this should be a single device with pokey subdevices */ //READ8_HANDLER( quad_pokey_r ); //WRITE8_HANDLER( quad_pokey_w ); /*void pokey_serin_ready (device_t *device, int after); void pokey_break_w (device_t *device, int shift); void pokey_kbcode_w (device_t *device, int kbcode, int make);*/ void pokey_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(POKEY, pokey); ================================================ FILE: VGMPlay/chips/pwm.c ================================================ /*************************************************************************** * Gens: PWM audio emulator. * * * * Copyright (c) 1999-2002 by Stphane Dallongeville * * Copyright (c) 2003-2004 by Stphane Akhoun * * Copyright (c) 2008-2009 by David Korth * * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your * * option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "mamedef.h" #include "pwm.h" #include //#include "gens_core/mem/mem_sh2.h" //#include "gens_core/cpu/sh2/sh2.h" #define CHILLY_WILLY_SCALE 1 #if PWM_BUF_SIZE == 8 unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, }; #elif PWM_BUF_SIZE == 4 unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = { 0x40, 0x00, 0x00, 0x80, 0x80, 0x40, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x80, 0x40, }; #else #error PWM_BUF_SIZE must equal 4 or 8. #endif /* PWM_BUF_SIZE */ typedef struct _pwm_chip { unsigned short PWM_FIFO_R[8]; unsigned short PWM_FIFO_L[8]; unsigned int PWM_RP_R; unsigned int PWM_WP_R; unsigned int PWM_RP_L; unsigned int PWM_WP_L; unsigned int PWM_Cycles; unsigned int PWM_Cycle; unsigned int PWM_Cycle_Cnt; unsigned int PWM_Int; unsigned int PWM_Int_Cnt; unsigned int PWM_Mode; //unsigned int PWM_Enable; unsigned int PWM_Out_R; unsigned int PWM_Out_L; unsigned int PWM_Cycle_Tmp; unsigned int PWM_Cycles_Tmp; unsigned int PWM_Int_Tmp; unsigned int PWM_FIFO_L_Tmp; unsigned int PWM_FIFO_R_Tmp; #if CHILLY_WILLY_SCALE // TODO: Fix Chilly Willy's new scaling algorithm. /* PWM scaling variables. */ int PWM_Offset; int PWM_Scale; //int PWM_Loudness; #endif int clock; } pwm_chip; #if CHILLY_WILLY_SCALE // TODO: Fix Chilly Willy's new scaling algorithm. #define PWM_Loudness 0 #endif void PWM_Init(pwm_chip* chip); void PWM_Recalc_Scale(pwm_chip* chip); void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle); void PWM_Set_Int(pwm_chip* chip, unsigned int int_time); void PWM_Update(pwm_chip* chip, int **buf, int length); extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static pwm_chip PWM_Chip[MAX_CHIPS]; /** * PWM_Init(): Initialize the PWM audio emulator. */ void PWM_Init(pwm_chip* chip) { chip->PWM_Mode = 0; chip->PWM_Out_R = 0; chip->PWM_Out_L = 0; memset(chip->PWM_FIFO_R, 0x00, sizeof(chip->PWM_FIFO_R)); memset(chip->PWM_FIFO_L, 0x00, sizeof(chip->PWM_FIFO_L)); chip->PWM_RP_R = 0; chip->PWM_WP_R = 0; chip->PWM_RP_L = 0; chip->PWM_WP_L = 0; chip->PWM_Cycle_Tmp = 0; chip->PWM_Int_Tmp = 0; chip->PWM_FIFO_L_Tmp = 0; chip->PWM_FIFO_R_Tmp = 0; //PWM_Loudness = 0; PWM_Set_Cycle(chip, 0); PWM_Set_Int(chip, 0); } #if CHILLY_WILLY_SCALE // TODO: Fix Chilly Willy's new scaling algorithm. void PWM_Recalc_Scale(pwm_chip* chip) { chip->PWM_Offset = (chip->PWM_Cycle / 2) + 1; chip->PWM_Scale = 0x7FFF00 / chip->PWM_Offset; } #endif void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle) { cycle--; chip->PWM_Cycle = (cycle & 0xFFF); chip->PWM_Cycle_Cnt = chip->PWM_Cycles; #if CHILLY_WILLY_SCALE // TODO: Fix Chilly Willy's new scaling algorithm. PWM_Recalc_Scale(chip); #endif } void PWM_Set_Int(pwm_chip* chip, unsigned int int_time) { int_time &= 0x0F; if (int_time) chip->PWM_Int = chip->PWM_Int_Cnt = int_time; else chip->PWM_Int = chip->PWM_Int_Cnt = 16; } void PWM_Clear_Timer(pwm_chip* chip) { chip->PWM_Cycle_Cnt = 0; } /** * PWM_SHIFT(): Shift PWM data. * @param src: Channel (L or R) with the source data. * @param dest Channel (L or R) for the destination. */ #define PWM_SHIFT(src, dest) \ { \ /* Make sure the source FIFO isn't empty. */ \ if (PWM_RP_##src != PWM_WP_##src) \ { \ /* Get destination channel output from the source channel FIFO. */ \ PWM_Out_##dest = PWM_FIFO_##src[PWM_RP_##src]; \ \ /* Increment the source channel read pointer, resetting to 0 if it overflows. */ \ PWM_RP_##src = (PWM_RP_##src + 1) & (PWM_BUF_SIZE - 1); \ } \ } /*static void PWM_Shift_Data(void) { switch (PWM_Mode & 0x0F) { case 0x01: case 0x0D: // Rx_LL: Right -> Ignore, Left -> Left PWM_SHIFT(L, L); break; case 0x02: case 0x0E: // Rx_LR: Right -> Ignore, Left -> Right PWM_SHIFT(L, R); break; case 0x04: case 0x07: // RL_Lx: Right -> Left, Left -> Ignore PWM_SHIFT(R, L); break; case 0x05: case 0x09: // RR_LL: Right -> Right, Left -> Left PWM_SHIFT(L, L); PWM_SHIFT(R, R); break; case 0x06: case 0x0A: // RL_LR: Right -> Left, Left -> Right PWM_SHIFT(L, R); PWM_SHIFT(R, L); break; case 0x08: case 0x0B: // RR_Lx: Right -> Right, Left -> Ignore PWM_SHIFT(R, R); break; case 0x00: case 0x03: case 0x0C: case 0x0F: default: // Rx_Lx: Right -> Ignore, Left -> Ignore break; } } void PWM_Update_Timer(unsigned int cycle) { // Don't do anything if PWM is disabled in the Sound menu. // Don't do anything if PWM isn't active. if ((PWM_Mode & 0x0F) == 0x00) return; if (PWM_Cycle == 0x00 || (PWM_Cycle_Cnt > cycle)) return; PWM_Shift_Data(); PWM_Cycle_Cnt += PWM_Cycle; PWM_Int_Cnt--; if (PWM_Int_Cnt == 0) { PWM_Int_Cnt = PWM_Int; if (PWM_Mode & 0x0080) { // RPT => generate DREQ1 as well as INT SH2_DMA1_Request(&M_SH2, 1); SH2_DMA1_Request(&S_SH2, 1); } if (_32X_MINT & 1) SH2_Interrupt(&M_SH2, 6); if (_32X_SINT & 1) SH2_Interrupt(&S_SH2, 6); } }*/ INLINE int PWM_Update_Scale(pwm_chip* chip, int PWM_In) { if (PWM_In == 0) return 0; // TODO: Chilly Willy's new scaling algorithm breaks drx's Sonic 1 32X (with PWM drums). #ifdef CHILLY_WILLY_SCALE //return (((PWM_In & 0xFFF) - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); // Knuckles' Chaotix: Tachy Touch uses the values 0xF?? for negative values // This small modification fixes the terrible pops. PWM_In &= 0xFFF; if (PWM_In & 0x800) PWM_In |= ~0xFFF; return ((PWM_In - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); #else const int PWM_adjust = ((chip->PWM_Cycle >> 1) + 1); int PWM_Ret = ((chip->PWM_In & 0xFFF) - PWM_adjust); // Increase PWM volume so it's audible. PWM_Ret <<= (5+2); // Make sure the PWM isn't oversaturated. if (PWM_Ret > 32767) PWM_Ret = 32767; else if (PWM_Ret < -32768) PWM_Ret = -32768; return PWM_Ret; #endif } void PWM_Update(pwm_chip* chip, int **buf, int length) { int tmpOutL; int tmpOutR; int i; //if (!PWM_Enable) // return; if (chip->PWM_Out_L == 0 && chip->PWM_Out_R == 0) { memset(buf[0], 0x00, length * sizeof(int)); memset(buf[1], 0x00, length * sizeof(int)); return; } // New PWM scaling algorithm provided by Chilly Willy on the Sonic Retro forums. tmpOutL = PWM_Update_Scale(chip, (int)chip->PWM_Out_L); tmpOutR = PWM_Update_Scale(chip, (int)chip->PWM_Out_R); for (i = 0; i < length; i ++) { buf[0][i] = tmpOutL; buf[1][i] = tmpOutR; } } void pwm_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { pwm_chip *chip = &PWM_Chip[ChipID]; PWM_Update(chip, outputs, samples); } int device_start_pwm(UINT8 ChipID, int clock) { /* allocate memory for the chip */ //pwm_state *chip = get_safe_token(device); pwm_chip *chip; int rate; if (ChipID >= MAX_CHIPS) return 0; chip = &PWM_Chip[ChipID]; rate = 22020; // that's the rate the PWM is mostly used if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; chip->clock = clock; PWM_Init(chip); /* allocate the stream */ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); return rate; } void device_stop_pwm(UINT8 ChipID) { //pwm_chip *chip = &PWM_Chip[ChipID]; //free(chip->ram); return; } void device_reset_pwm(UINT8 ChipID) { pwm_chip *chip = &PWM_Chip[ChipID]; PWM_Init(chip); } void pwm_chn_w(UINT8 ChipID, UINT8 Channel, UINT16 data) { pwm_chip *chip = &PWM_Chip[ChipID]; if (chip->clock == 1) { // old-style commands switch(Channel) { case 0x00: chip->PWM_Out_L = data; break; case 0x01: chip->PWM_Out_R = data; break; case 0x02: PWM_Set_Cycle(chip, data); break; case 0x03: chip->PWM_Out_L = data; chip->PWM_Out_R = data; break; } } else { switch(Channel) { case 0x00/2: // control register PWM_Set_Int(chip, data >> 8); break; case 0x02/2: // cycle register PWM_Set_Cycle(chip, data); break; case 0x04/2: // l ch chip->PWM_Out_L = data; break; case 0x06/2: // r ch chip->PWM_Out_R = data; if (! chip->PWM_Mode) { if (chip->PWM_Out_L == chip->PWM_Out_R) { // fixes these terrible pops when // starting/stopping/pausing the song chip->PWM_Offset = data; chip->PWM_Mode = 0x01; } } break; case 0x08/2: // mono ch chip->PWM_Out_L = data; chip->PWM_Out_R = data; if (! chip->PWM_Mode) { chip->PWM_Offset = data; chip->PWM_Mode = 0x01; } break; } } return; } ================================================ FILE: VGMPlay/chips/pwm.h ================================================ /*************************************************************************** * Gens: PWM audio emulator. * * * * Copyright (c) 1999-2002 by Stphane Dallongeville * * Copyright (c) 2003-2004 by Stphane Akhoun * * Copyright (c) 2008 by David Korth * * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your * * option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #define PWM_BUF_SIZE 4 /*extern unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE]; extern unsigned short PWM_FIFO_R[8]; extern unsigned short PWM_FIFO_L[8]; extern unsigned int PWM_RP_R; extern unsigned int PWM_WP_R; extern unsigned int PWM_RP_L; extern unsigned int PWM_WP_L; extern unsigned int PWM_Cycles; extern unsigned int PWM_Cycle; extern unsigned int PWM_Cycle_Cnt; extern unsigned int PWM_Int; extern unsigned int PWM_Int_Cnt; extern unsigned int PWM_Mode; extern unsigned int PWM_Enable; extern unsigned int PWM_Out_R; extern unsigned int PWM_Out_L;*/ //void PWM_Init(void); //void PWM_Recalc_Scale(void); /* Functions called by x86 asm. */ //void PWM_Set_Cycle(unsigned int cycle); //void PWM_Set_Int(unsigned int int_time); /* Functions called by C/C++ code only. */ //void PWM_Clear_Timer(void); //void PWM_Update_Timer(unsigned int cycle); //void PWM_Update(int **buf, int length); void pwm_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_pwm(UINT8 ChipID, int clock); void device_stop_pwm(UINT8 ChipID); void device_reset_pwm(UINT8 ChipID); void pwm_chn_w(UINT8 ChipID, UINT8 Channel, UINT16 data); ================================================ FILE: VGMPlay/chips/qsound_ctr.c ================================================ /* Capcom DL-1425 QSound emulator ============================== by superctr (Ian Karlsson) with thanks to Valley Bell 2018-05-12 - 2018-05-15 */ //#include "emu.h" #include "mamedef.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include #include "qsound_ctr.h" #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) struct qsound_voice { UINT16 bank; INT16 addr; // top word is the sample address UINT16 phase; UINT16 rate; INT16 loop_len; INT16 end_addr; INT16 volume; INT16 echo; }; struct qsound_adpcm { UINT16 start_addr; UINT16 end_addr; UINT16 bank; INT16 volume; UINT16 flag; INT16 cur_vol; INT16 step_size; UINT16 cur_addr; }; // Q1 Filter struct qsound_fir { int tap_count; // usually 95 int delay_pos; INT16 table_pos; INT16 taps[95]; INT16 delay_line[95]; }; // Delay line struct qsound_delay { INT16 delay; INT16 volume; INT16 write_pos; INT16 read_pos; INT16 delay_line[51]; }; struct qsound_echo { UINT16 end_pos; INT16 feedback; INT16 length; INT16 last_sample; INT16 delay_line[1024]; INT16 delay_pos; }; struct qsound_chip { UINT8* romData; UINT32 romSize; UINT32 romMask; UINT32 muteMask; // ==================================================== // UINT16 data_latch; INT16 out[2]; INT16 pan_tables[2][2][98]; struct qsound_voice voice[16]; struct qsound_adpcm adpcm[3]; UINT16 voice_pan[16+3]; INT16 voice_output[16+3]; struct qsound_echo echo; struct qsound_fir filter[2]; struct qsound_fir alt_filter[2]; struct qsound_delay wet[2]; struct qsound_delay dry[2]; UINT16 state; UINT16 next_state; UINT16 delay_update; int state_counter; UINT8 ready_flag; UINT16 *register_map[256]; }; static void init_pan_tables(struct qsound_chip *chip); static void init_register_map(struct qsound_chip *chip); static void update_sample(struct qsound_chip *chip); static void state_init(struct qsound_chip *chip); static void state_refresh_filter_1(struct qsound_chip *chip); static void state_refresh_filter_2(struct qsound_chip *chip); static void state_normal_update(struct qsound_chip *chip); INLINE INT16 get_sample(struct qsound_chip *chip, UINT16 bank,UINT16 address); INLINE const INT16* get_filter_table(struct qsound_chip *chip, UINT16 offset); INLINE INT16 pcm_update(struct qsound_chip *chip, int voice_no, INT32 *echo_out); INLINE void adpcm_update(struct qsound_chip *chip, int voice_no, int nibble); INLINE INT16 echo(struct qsound_echo *r,INT32 input); INLINE INT32 fir(struct qsound_fir *f, INT16 input); INLINE INT32 delay(struct qsound_delay *d, INT32 input); INLINE void delay_update(struct qsound_delay *d); // **************************************************************************** #define MAX_CHIPS 0x02 static struct qsound_chip QSoundData[MAX_CHIPS]; int device_start_qsound_ctr(UINT8 ChipID, int clock) { struct qsound_chip* chip = &QSoundData[ChipID]; memset(chip,0,sizeof(*chip)); chip->romData = NULL; chip->romSize = 0x00; chip->romMask = 0x00; qsoundc_set_mute_mask(ChipID, 0x00000); init_pan_tables(chip); init_register_map(chip); return clock / 2 / 1248; } void device_stop_qsound_ctr(UINT8 ChipID) { struct qsound_chip* chip = &QSoundData[ChipID]; free(chip->romData); return; } void device_reset_qsound_ctr(UINT8 ChipID) { struct qsound_chip* chip = &QSoundData[ChipID]; chip->ready_flag = 0; chip->out[0] = chip->out[1] = 0; chip->state = 0; chip->state_counter = 0; return; } UINT8 qsoundc_r(UINT8 ChipID, offs_t offset) { struct qsound_chip* chip = &QSoundData[ChipID]; return chip->ready_flag; } void qsoundc_w(UINT8 ChipID, offs_t offset, UINT8 data) { struct qsound_chip* chip = &QSoundData[ChipID]; switch (offset) { case 0: chip->data_latch = (chip->data_latch & 0x00ff) | (data << 8); break; case 1: chip->data_latch = (chip->data_latch & 0xff00) | data; break; case 2: qsoundc_write_data(ChipID, data, chip->data_latch); break; default: break; } return; } void qsoundc_write_data(UINT8 ChipID, UINT8 address, UINT16 data) { struct qsound_chip* chip = &QSoundData[ChipID]; UINT16 *destination = chip->register_map[address]; if(destination) *destination = data; chip->ready_flag = 0; return; } void qsoundc_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { struct qsound_chip* chip = &QSoundData[ChipID]; UINT32 curSmpl; memset(outputs[0], 0, samples * sizeof(*outputs[0])); memset(outputs[1], 0, samples * sizeof(*outputs[1])); for (curSmpl = 0; curSmpl < samples; curSmpl ++) { update_sample(chip); outputs[0][curSmpl] = chip->out[0]; outputs[1][curSmpl] = chip->out[1]; } return; } void qsoundc_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { struct qsound_chip* chip = &QSoundData[ChipID]; if (chip->romSize != ROMSize) { chip->romData = (UINT8*)realloc(chip->romData, ROMSize); chip->romSize = ROMSize; memset(chip->romData, 0xFF, ROMSize); chip->romMask = -1; } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->romData + DataStart, ROMData, DataLength); return; } void qsoundc_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { struct qsound_chip* chip = &QSoundData[ChipID]; chip->muteMask = MuteMask; return; } void qsoundc_wait_busy(UINT8 ChipID) { struct qsound_chip* chip = &QSoundData[ChipID]; while(chip->ready_flag == 0) { update_sample(chip); } } // ============================================================================ static const INT16 qsound_dry_mix_table[33] = { -16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, -16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, -16384,-14746,-13107,-11633,-10486,-9175,-8520,-7209, -6226,-5226,-4588,-3768,-3277,-2703,-2130,-1802, 0 }; static const INT16 qsound_wet_mix_table[33] = { 0,-1638,-1966,-2458,-2949,-3441,-4096,-4669, -4915,-5120,-5489,-6144,-7537,-8831,-9339,-9830, -10240,-10322,-10486,-10568,-10650,-11796,-12288,-12288, -12534,-12648,-12780,-12829,-12943,-13107,-13418,-14090, -16384 }; static const INT16 qsound_linear_mix_table[33] = { -16379,-16338,-16257,-16135,-15973,-15772,-15531,-15251, -14934,-14580,-14189,-13763,-13303,-12810,-12284,-11729, -11729,-11144,-10531,-9893,-9229,-8543,-7836,-7109, -6364,-5604,-4829,-4043,-3246,-2442,-1631,-817, 0 }; static const INT16 qsound_filter_data[5][95] = { { // d53 - 0 0,0,0,6,44,-24,-53,-10,59,-40,-27,1,39,-27,56,127,174,36,-13,49, 212,142,143,-73,-20,66,-108,-117,-399,-265,-392,-569,-473,-71,95,-319,-218,-230,331,638, 449,477,-180,532,1107,750,9899,3828,-2418,1071,-176,191,-431,64,117,-150,-274,-97,-238,165, 166,250,-19,4,37,204,186,-6,140,-77,-1,1,18,-10,-151,-149,-103,-9,55,23, -102,-97,-11,13,-48,-27,5,18,-61,-30,64,72,0,0,0, }, { // db2 - 1 - default left filter 0,0,0,85,24,-76,-123,-86,-29,-14,-20,-7,6,-28,-87,-89,-5,100,154,160, 150,118,41,-48,-78,-23,59,83,-2,-176,-333,-344,-203,-66,-39,2,224,495,495,280, 432,1340,2483,5377,1905,658,0,97,347,285,35,-95,-78,-82,-151,-192,-171,-149,-147,-113, -22,71,118,129,127,110,71,31,20,36,46,23,-27,-63,-53,-21,-19,-60,-92,-69, -12,25,29,30,40,41,29,30,46,39,-15,-74,0,0,0, }, { // e11 - 2 - default right filter 0,0,0,23,42,47,29,10,2,-14,-54,-92,-93,-70,-64,-77,-57,18,94,113, 87,69,67,50,25,29,58,62,24,-39,-131,-256,-325,-234,-45,58,78,223,485,496, 127,6,857,2283,2683,4928,1328,132,79,314,189,-80,-90,35,-21,-186,-195,-99,-136,-258, -189,82,257,185,53,41,84,68,38,63,77,14,-60,-71,-71,-120,-151,-84,14,29, -8,7,66,69,12,-3,54,92,52,-6,-15,-2,0,0,0, }, { // e70 - 3 0,0,0,2,-28,-37,-17,0,-9,-22,-3,35,52,39,20,7,-6,2,55,121, 129,67,8,1,9,-6,-16,16,66,96,118,130,75,-47,-92,43,223,239,151,219, 440,475,226,206,940,2100,2663,4980,865,49,-33,186,231,103,42,114,191,184,116,29, -47,-72,-21,60,96,68,31,32,63,87,76,39,7,14,55,85,67,18,-12,-3, 21,34,29,6,-27,-49,-37,-2,16,0,-21,-16,0,0,0, }, { // ecf - 4 0,0,0,48,7,-22,-29,-10,24,54,59,29,-36,-117,-185,-213,-185,-99,13,90, 83,24,-5,23,53,47,38,56,67,57,75,107,16,-242,-440,-355,-120,-33,-47,152, 501,472,-57,-292,544,1937,2277,6145,1240,153,47,200,152,36,64,134,74,-82,-208,-266, -268,-188,-42,65,74,56,89,133,114,44,-3,-1,17,29,29,-2,-76,-156,-187,-151, -85,-31,-5,7,20,32,24,-5,-20,6,48,62,0,0,0, } }; static const INT16 qsound_filter_data2[209] = { // f2e - following 95 values used for "disable output" filter 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, // f73 - following 45 values used for "mode 2" filter (overlaps with f2e) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0, -371,-196,-268,-512,-303,-315,-184,-76,276,-256,298,196,990,236,1114,-126,4377,6549,791, // fa0 - filtering disabled (for 95-taps) (use fa3 or fa4 for mode2 filters) 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-16384,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,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 INT16 adpcm_step_table[16] = { 154, 154, 128, 102, 77, 58, 58, 58, 58, 58, 58, 58, 77, 102, 128, 154 }; // DSP states enum { STATE_INIT1 = 0x288, STATE_INIT2 = 0x61a, STATE_REFRESH1 = 0x039, STATE_REFRESH2 = 0x04f, STATE_NORMAL1 = 0x314, STATE_NORMAL2 = 0x6b2, }; enum { PANTBL_LEFT = 0, PANTBL_RIGHT = 1, PANTBL_DRY = 0, PANTBL_WET = 1, }; static void init_pan_tables(struct qsound_chip *chip) { int i; for(i=0;i<33;i++) { // dry mixing levels chip->pan_tables[PANTBL_LEFT][PANTBL_DRY][i] = qsound_dry_mix_table[i]; chip->pan_tables[PANTBL_RIGHT][PANTBL_DRY][i] = qsound_dry_mix_table[32-i]; // wet mixing levels chip->pan_tables[PANTBL_LEFT][PANTBL_WET][i] = qsound_wet_mix_table[i]; chip->pan_tables[PANTBL_RIGHT][PANTBL_WET][i] = qsound_wet_mix_table[32-i]; // linear panning, only for dry component. wet component is muted. chip->pan_tables[PANTBL_LEFT][PANTBL_DRY][i+0x30] = qsound_linear_mix_table[i]; chip->pan_tables[PANTBL_RIGHT][PANTBL_DRY][i+0x30] = qsound_linear_mix_table[32-i]; } } static void init_register_map(struct qsound_chip *chip) { int i; // unused registers for(i=0;i<256;i++) chip->register_map[i] = NULL; // PCM registers for(i=0;i<16;i++) // PCM voices { chip->register_map[(i<<3)+0] = (UINT16*)&chip->voice[(i+1)%16].bank; // Bank applies to the next channel chip->register_map[(i<<3)+1] = (UINT16*)&chip->voice[i].addr; // Current sample position and start position. chip->register_map[(i<<3)+2] = (UINT16*)&chip->voice[i].rate; // 4.12 fixed point decimal. chip->register_map[(i<<3)+3] = (UINT16*)&chip->voice[i].phase; chip->register_map[(i<<3)+4] = (UINT16*)&chip->voice[i].loop_len; chip->register_map[(i<<3)+5] = (UINT16*)&chip->voice[i].end_addr; chip->register_map[(i<<3)+6] = (UINT16*)&chip->voice[i].volume; chip->register_map[(i<<3)+7] = NULL; // unused chip->register_map[i+0x80] = (UINT16*)&chip->voice_pan[i]; chip->register_map[i+0xba] = (UINT16*)&chip->voice[i].echo; } // ADPCM registers for(i=0;i<3;i++) // ADPCM voices { // ADPCM sample rate is fixed to 8khz. (one channel is updated every third sample) chip->register_map[(i<<2)+0xca] = (UINT16*)&chip->adpcm[i].start_addr; chip->register_map[(i<<2)+0xcb] = (UINT16*)&chip->adpcm[i].end_addr; chip->register_map[(i<<2)+0xcc] = (UINT16*)&chip->adpcm[i].bank; chip->register_map[(i<<2)+0xcd] = (UINT16*)&chip->adpcm[i].volume; chip->register_map[i+0xd6] = (UINT16*)&chip->adpcm[i].flag; // non-zero to start ADPCM playback chip->register_map[i+0x90] = (UINT16*)&chip->voice_pan[16+i]; } // QSound registers chip->register_map[0x93] = (UINT16*)&chip->echo.feedback; chip->register_map[0xd9] = (UINT16*)&chip->echo.end_pos; chip->register_map[0xe2] = (UINT16*)&chip->delay_update; // non-zero to update delays chip->register_map[0xe3] = (UINT16*)&chip->next_state; for(i=0;i<2;i++) // left, right { // Wet chip->register_map[(i<<1)+0xda] = (UINT16*)&chip->filter[i].table_pos; chip->register_map[(i<<1)+0xde] = (UINT16*)&chip->wet[i].delay; chip->register_map[(i<<1)+0xe4] = (UINT16*)&chip->wet[i].volume; // Dry chip->register_map[(i<<1)+0xdb] = (UINT16*)&chip->alt_filter[i].table_pos; chip->register_map[(i<<1)+0xdf] = (UINT16*)&chip->dry[i].delay; chip->register_map[(i<<1)+0xe5] = (UINT16*)&chip->dry[i].volume; } } INLINE INT16 get_sample(struct qsound_chip *chip, UINT16 bank,UINT16 address) { UINT32 rom_addr; UINT8 sample_data; if (! chip->romMask) return 0; // no ROM loaded if (! (bank & 0x8000)) return 0; // ignore attempts to read from DSP program ROM bank &= 0x7FFF; rom_addr = (bank << 16) | (address << 0); sample_data = chip->romData[rom_addr]; return (INT16)((sample_data << 8) | (sample_data << 0)); // MAME currently expands the 8 bit ROM data to 16 bits this way. } INLINE const INT16* get_filter_table(struct qsound_chip *chip, UINT16 offset) { int index; if (offset >= 0xf2e && offset < 0xfff) return &qsound_filter_data2[offset-0xf2e]; // overlapping filter data index = (offset-0xd53)/95; if(index >= 0 && index < 5) return qsound_filter_data[index]; // normal tables return NULL; // no filter found. } /********************************************************************/ // updates one DSP sample static void update_sample(struct qsound_chip *chip) { switch(chip->state) { default: case STATE_INIT1: case STATE_INIT2: state_init(chip); return; case STATE_REFRESH1: state_refresh_filter_1(chip); return; case STATE_REFRESH2: state_refresh_filter_2(chip); return; case STATE_NORMAL1: case STATE_NORMAL2: state_normal_update(chip); return; } } // Initialization routine static void state_init(struct qsound_chip *chip) { int mode = (chip->state == STATE_INIT2) ? 1 : 0; int i; // we're busy for 4 samples, including the filter refresh. if(chip->state_counter >= 2) { chip->state_counter = 0; chip->state = chip->next_state; return; } else if(chip->state_counter == 1) { chip->state_counter++; return; } memset(chip->voice, 0, sizeof(chip->voice)); memset(chip->adpcm, 0, sizeof(chip->adpcm)); memset(chip->filter, 0, sizeof(chip->filter)); memset(chip->alt_filter, 0, sizeof(chip->alt_filter)); memset(chip->wet, 0, sizeof(chip->wet)); memset(chip->dry, 0, sizeof(chip->dry)); memset(&chip->echo, 0, sizeof(chip->echo)); for(i=0;i<19;i++) { chip->voice_pan[i] = 0x120; chip->voice_output[i] = 0; } for(i=0;i<16;i++) chip->voice[i].bank = 0x8000; for(i=0;i<3;i++) chip->adpcm[i].bank = 0x8000; if(mode == 0) { // mode 1 chip->wet[0].delay = 0; chip->dry[0].delay = 46; chip->wet[1].delay = 0; chip->dry[1].delay = 48; chip->filter[0].table_pos = 0xdb2; chip->filter[1].table_pos = 0xe11; chip->echo.end_pos = 0x554 + 6; chip->next_state = STATE_REFRESH1; } else { // mode 2 chip->wet[0].delay = 1; chip->dry[0].delay = 0; chip->wet[1].delay = 0; chip->dry[1].delay = 0; chip->filter[0].table_pos = 0xf73; chip->filter[1].table_pos = 0xfa4; chip->alt_filter[0].table_pos = 0xf73; chip->alt_filter[1].table_pos = 0xfa4; chip->echo.end_pos = 0x53c + 6; chip->next_state = STATE_REFRESH2; } chip->wet[0].volume = 0x3fff; chip->dry[0].volume = 0x3fff; chip->wet[1].volume = 0x3fff; chip->dry[1].volume = 0x3fff; chip->delay_update = 1; chip->ready_flag = 0; chip->state_counter = 1; } // Updates filter parameters for mode 1 static void state_refresh_filter_1(struct qsound_chip *chip) { const INT16 *table; int ch; for(ch=0; ch<2; ch++) { chip->filter[ch].delay_pos = 0; chip->filter[ch].tap_count = 95; table = get_filter_table(chip,chip->filter[ch].table_pos); if (table != NULL) memcpy(chip->filter[ch].taps, table, 95 * sizeof(INT16)); } chip->state = chip->next_state = STATE_NORMAL1; } // Updates filter parameters for mode 2 static void state_refresh_filter_2(struct qsound_chip *chip) { const INT16 *table; int ch; for(ch=0; ch<2; ch++) { chip->filter[ch].delay_pos = 0; chip->filter[ch].tap_count = 45; table = get_filter_table(chip,chip->filter[ch].table_pos); if (table != NULL) memcpy(chip->filter[ch].taps, table, 45 * sizeof(INT16)); chip->alt_filter[ch].delay_pos = 0; chip->alt_filter[ch].tap_count = 44; table = get_filter_table(chip,chip->alt_filter[ch].table_pos); if (table != NULL) memcpy(chip->alt_filter[ch].taps, table, 44 * sizeof(INT16)); } chip->state = chip->next_state = STATE_NORMAL2; } // Updates a PCM voice. There are 16 voices, each are updated every sample // with full rate and volume control. INLINE INT16 pcm_update(struct qsound_chip *chip, int voice_no, INT32 *echo_out) { struct qsound_voice *v = &chip->voice[voice_no]; INT32 new_phase; INT16 output; if (chip->muteMask & (1<volume * get_sample(chip, v->bank, v->addr))>>14; *echo_out += (output * v->echo)<<2; // Add delta to the phase and loop back if required new_phase = v->rate + ((v->addr<<12) | (v->phase>>4)); if((new_phase>>12) >= v->end_addr) new_phase -= (v->loop_len<<12); new_phase = CLAMP(new_phase, -0x8000000, 0x7FFFFFF); v->addr = new_phase>>12; v->phase = (new_phase<<4)&0xffff; return output; } // Updates an ADPCM voice. There are 3 voices, one is updated every sample // (effectively making the ADPCM rate 1/3 of the master sample rate), and // volume is set when starting samples only. // The ADPCM algorithm is supposedly similar to Yamaha ADPCM. It also seems // like Capcom never used it, so this was not emulated in the earlier QSound // emulators. INLINE void adpcm_update(struct qsound_chip *chip, int voice_no, int nibble) { struct qsound_adpcm *v = &chip->adpcm[voice_no]; INT32 delta; INT8 step; if (chip->muteMask & (1<<(16+voice_no))) { chip->voice_output[16+voice_no] = 0; return; } if(!nibble) { // Mute voice when it reaches the end address. if(v->cur_addr == v->end_addr) v->cur_vol = 0; // Playback start flag if(v->flag) { chip->voice_output[16+voice_no] = 0; v->flag = 0; v->step_size = 10; v->cur_vol = v->volume; v->cur_addr = v->start_addr; } // get top nibble step = get_sample(chip, v->bank, v->cur_addr) >> 8; } else { // get bottom nibble step = get_sample(chip, v->bank, v->cur_addr++) >> 4; } // shift with sign extend step >>= 4; // delta = (0.5 + abs(v->step)) * v->step_size delta = ((1+abs(step<<1)) * v->step_size)>>1; if(step <= 0) delta = -delta; delta += chip->voice_output[16+voice_no]; delta = CLAMP(delta,-32768,32767); chip->voice_output[16+voice_no] = (delta * v->cur_vol)>>16; v->step_size = (adpcm_step_table[8+step] * v->step_size) >> 6; v->step_size = CLAMP(v->step_size, 1, 2000); } // The echo effect is pretty simple. A moving average filter is used on // the output from the delay line to smooth samples over time. INLINE INT16 echo(struct qsound_echo *r,INT32 input) { // get average of last 2 samples from the delay line INT32 new_sample; INT32 old_sample = r->delay_line[r->delay_pos]; INT32 last_sample = r->last_sample; r->last_sample = old_sample; old_sample = (old_sample+last_sample) >> 1; // add current sample to the delay line new_sample = input + ((old_sample * r->feedback)<<2); r->delay_line[r->delay_pos++] = new_sample>>16; if(r->delay_pos >= r->length) r->delay_pos = 0; return old_sample; } // Process a sample update static void state_normal_update(struct qsound_chip *chip) { int v, ch; INT32 echo_input = 0; INT16 echo_output; chip->ready_flag = 0x80; // recalculate echo length if(chip->state == STATE_NORMAL2) chip->echo.length = chip->echo.end_pos - 0x53c; else chip->echo.length = chip->echo.end_pos - 0x554; chip->echo.length = CLAMP(chip->echo.length, 0, 1024); // update PCM voices for(v=0; v<16; v++) chip->voice_output[v] = pcm_update(chip, v, &echo_input); // update ADPCM voices (one every third sample) adpcm_update(chip, chip->state_counter % 3, chip->state_counter / 3); echo_output = echo(&chip->echo,echo_input); // now, we do the magic stuff for(ch=0; ch<2; ch++) { // Echo is output on the unfiltered component of the left channel and // the filtered component of the right channel. INT32 wet = (ch == 1) ? echo_output<<14 : 0; INT32 dry = (ch == 0) ? echo_output<<14 : 0; INT32 output = 0; for(v=0; v<19; v++) { UINT16 pan_index = chip->voice_pan[v]-0x110; if(pan_index > 97) pan_index = 97; // Apply different volume tables on the dry and wet inputs. dry -= (chip->voice_output[v] * chip->pan_tables[ch][PANTBL_DRY][pan_index]); wet -= (chip->voice_output[v] * chip->pan_tables[ch][PANTBL_WET][pan_index]); } // Saturate accumulated voices dry = CLAMP(dry, -0x1fffffff, 0x1fffffff) << 2; wet = CLAMP(wet, -0x1fffffff, 0x1fffffff) << 2; // Apply FIR filter on 'wet' input wet = fir(&chip->filter[ch], wet >> 16); // in mode 2, we do this on the 'dry' input too if(chip->state == STATE_NORMAL2) dry = fir(&chip->alt_filter[ch], dry >> 16); // output goes through a delay line and attenuation output = (delay(&chip->wet[ch], wet) + delay(&chip->dry[ch], dry)); // DSP round function output = ((output + 0x2000) & ~0x3fff) >> 14; chip->out[ch] = CLAMP(output, -0x7fff, 0x7fff); if(chip->delay_update) { delay_update(&chip->wet[ch]); delay_update(&chip->dry[ch]); } } chip->delay_update = 0; // after 6 samples, the next state is executed. chip->state_counter++; if(chip->state_counter > 5) { chip->state_counter = 0; chip->state = chip->next_state; } } // Apply the FIR filter used as the Q1 transfer function INLINE INT32 fir(struct qsound_fir *f, INT16 input) { INT32 output = 0, tap = 0; for(; tap < (f->tap_count-1); tap++) { output -= (f->taps[tap] * f->delay_line[f->delay_pos++])<<2; if(f->delay_pos >= f->tap_count-1) f->delay_pos = 0; } output -= (f->taps[tap] * input)<<2; f->delay_line[f->delay_pos++] = input; if(f->delay_pos >= f->tap_count-1) f->delay_pos = 0; return output; } // Apply delay line and component volume INLINE INT32 delay(struct qsound_delay *d, INT32 input) { INT32 output; d->delay_line[d->write_pos++] = input>>16; if(d->write_pos >= 51) d->write_pos = 0; output = d->delay_line[d->read_pos++]*d->volume; if(d->read_pos >= 51) d->read_pos = 0; return output; } // Update the delay read position to match new delay length INLINE void delay_update(struct qsound_delay *d) { INT16 new_read_pos = (d->write_pos - d->delay) % 51; if(new_read_pos < 0) new_read_pos += 51; d->read_pos = new_read_pos; } ================================================ FILE: VGMPlay/chips/qsound_ctr.h ================================================ #pragma once void qsoundc_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_qsound_ctr(UINT8 ChipID, int clock); void device_stop_qsound_ctr(UINT8 ChipID); void device_reset_qsound_ctr(UINT8 ChipID); void qsoundc_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 qsoundc_r(UINT8 ChipID, offs_t offset); void qsoundc_write_data(UINT8 ChipID, UINT8 address, UINT16 data); void qsoundc_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void qsoundc_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void qsoundc_wait_busy(UINT8 ChipID); ================================================ FILE: VGMPlay/chips/qsound_intf.c ================================================ //#include "emu.h" #include "mamedef.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include #include "qsound_intf.h" #include "qsound_mame.h" #include "qsound_ctr.h" #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 // QSound HLE core from MAME #endif #define EC_CTR 0x00 // superctr custom HLE static UINT8 EMU_CORE = 0x00; // fix broken optimization of old VGMs causing problems with the new core static UINT8 key_on_hack = 0x00; static UINT16 start_addr_cache[2][16]; static UINT16 pitch_cache[2][16]; static UINT16 data_latch[2]; int device_start_qsound(UINT8 ChipID, int clock) { memset(start_addr_cache[ChipID], 0, sizeof(UINT16)*16); memset(pitch_cache[ChipID], 0, sizeof(UINT16)*16); switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return device_start_qsoundm(ChipID, clock); #endif case EC_CTR: if(clock < 10000000) { clock *= 15; key_on_hack = 1; } return device_start_qsound_ctr(ChipID, clock); } return 0; } void device_stop_qsound(UINT8 ChipID) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_stop_qsoundm(ChipID); return; #endif case EC_CTR: device_stop_qsound_ctr(ChipID); return; } } void device_reset_qsound(UINT8 ChipID) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: device_reset_qsoundm(ChipID); return; #endif case EC_CTR: device_reset_qsound_ctr(ChipID); // need to wait until the chip is ready before we start writing to it ... // we do this by time travel. qsoundc_wait_busy(ChipID); return; } } void qsound_w(UINT8 ChipID, offs_t offset, UINT8 data) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: qsoundm_w(ChipID, offset, data); return; #endif case EC_CTR: if(key_on_hack) { int ch; switch (offset) { // need to handle three cases, as vgm_cmp can remove writes to both phase and bank // registers, depending on version. // - start address was written before end/loop, but phase register is written // - as above, but phase is not written (we use bank as a backup then) // - voice parameters are written during a note (we can't rewrite the address then) case 0: data_latch[ChipID] = (data_latch[ChipID] & 0x00ff) | (data << 8); break; case 1: data_latch[ChipID] = (data_latch[ChipID] & 0xff00) | data; break; case 2: if(data > 0x7f) break; ch = data>>3; switch(data & 7) { case 1: // Start addr. write start_addr_cache[ChipID][ch] = data_latch[ChipID]; break; case 2: // Pitch write // (old HLE assumed writing a non-zero value after a zero value was Key On) if(pitch_cache[ChipID][ch] == 0 && data_latch[ChipID] != 0) qsoundc_write_data(ChipID, (ch << 3) + 1, start_addr_cache[ChipID][ch]); pitch_cache[ChipID][ch] = data_latch[ChipID]; break; case 3: // Phase (old HLE also assumed this was Key On) qsoundc_write_data(ChipID, (ch << 3) + 1, start_addr_cache[ChipID][ch]); default: break; } } } qsoundc_w(ChipID, offset, data); // need to wait until the chip is ready before we start writing to it ... // we do this by time travel. if(offset == 2 && data == 0xe3) qsoundc_wait_busy(ChipID); return; } } UINT8 qsound_r(UINT8 ChipID, offs_t offset) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return qsoundm_r(ChipID, offset); #endif case EC_CTR: return qsoundc_r(ChipID, offset); } return 0; } void qsound_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: qsoundm_update(ChipID, outputs, samples); return; #endif case EC_CTR: qsoundc_update(ChipID, outputs, samples); return; } } void qsound_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: qsoundm_write_rom(ChipID, ROMSize, DataStart, DataLength, ROMData); return; #endif case EC_CTR: qsoundc_write_rom(ChipID, ROMSize, DataStart, DataLength, ROMData); return; } } void qsound_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { switch(EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: qsoundm_set_mute_mask(ChipID, MuteMask); return; #endif case EC_CTR: qsoundc_set_mute_mask(ChipID, MuteMask); return; } } void qsound_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_CTR; #endif return; } ================================================ FILE: VGMPlay/chips/qsound_intf.h ================================================ #pragma once void qsound_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_qsound(UINT8 ChipID, int clock); void device_stop_qsound(UINT8 ChipID); void device_reset_qsound(UINT8 ChipID); void qsound_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 qsound_r(UINT8 ChipID, offs_t offset); void qsound_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void qsound_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void qsound_set_emu_core(UINT8 Emulator); ================================================ FILE: VGMPlay/chips/qsound_mame.c ================================================ /*************************************************************************** Capcom System QSound(tm) ======================== Driver by Paul Leaman (paul@vortexcomputing.demon.co.uk) and Miguel Angel Horna (mahorna@teleline.es) A 16 channel stereo sample player. QSpace position is simulated by panning the sound in the stereo space. Register 0 xxbb xx = unknown bb = start high address 1 ssss ssss = sample start address 2 pitch 3 unknown (always 0x8000) 4 loop offset from end address 5 end 6 master channel volume 7 not used 8 Balance (left=0x0110 centre=0x0120 right=0x0130) 9 unknown (most fixed samples use 0 for this register) Many thanks to CAB (the author of Amuse), without whom this probably would never have been finished. If anybody has some information about this hardware, please send it to me to mahorna@teleline.es or 432937@cepsz.unizar.es. http://teleline.terra.es/personal/mahorna ***************************************************************************/ //#include "emu.h" #include "mamedef.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include #include "qsound_mame.h" /* Debug defines */ #define LOG_WAVE 0 #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) /* 8 bit source ROM samples */ typedef INT8 QSOUND_SRC_SAMPLE; #define QSOUND_CLOCKDIV 166 /* Clock divider */ #define QSOUND_CHANNELS 16 typedef stream_sample_t QSOUND_SAMPLE; struct QSOUND_CHANNEL { UINT32 bank; // bank UINT32 address; // start/cur address UINT16 loop; // loop address UINT16 end; // end address UINT32 freq; // frequency UINT16 vol; // master volume // work variables UINT8 enabled; // key on / key off int lvol; // left volume int rvol; // right volume UINT32 step_ptr; // current offset counter UINT8 Muted; }; typedef struct _qsound_state qsound_state; struct _qsound_state { /* Private variables */ //sound_stream * stream; /* Audio stream */ struct QSOUND_CHANNEL channel[QSOUND_CHANNELS]; UINT16 data; /* register latch data */ QSOUND_SRC_SAMPLE *sample_rom; /* Q sound sample ROM */ UINT32 sample_rom_length; int pan_table[33]; /* Pan volume table */ //FILE *fpRawDataL; //FILE *fpRawDataR; }; #define MAX_CHIPS 0x02 static qsound_state QSoundData[MAX_CHIPS]; /*INLINE qsound_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == QSOUND); return (qsound_state *)downcast(device)->token(); }*/ /* Function prototypes */ //static STREAM_UPDATE( qsoundm_update ); static void qsound_set_command(qsound_state *chip, UINT8 address, UINT16 data); //static DEVICE_START( qsound ) int device_start_qsoundm(UINT8 ChipID, int clock) { //qsound_state *chip = get_safe_token(device); qsound_state *chip; int i; if (ChipID >= MAX_CHIPS) return 0; chip = &QSoundData[ChipID]; //chip->sample_rom = (QSOUND_SRC_SAMPLE *)*device->region(); //chip->sample_rom_length = device->region()->bytes(); chip->sample_rom = NULL; chip->sample_rom_length = 0x00; /* Create pan table */ for (i=0; i<33; i++) chip->pan_table[i]=(int)((256/sqrt(32.0)) * sqrt((double)i)); // init sound regs memset(chip->channel, 0, sizeof(chip->channel)); // LOG(("Pan table\n")); // for (i=0; i<33; i++) // LOG(("%02x ", chip->pan_table[i])); /* Allocate stream */ /*chip->stream = device->machine().sound().stream_alloc( *device, 0, 2, device->clock() / QSOUND_CLOCKDIV, chip, qsoundm_update );*/ /*if (LOG_WAVE) { chip->fpRawDataR=fopen("qsoundr.raw", "w+b"); chip->fpRawDataL=fopen("qsoundl.raw", "w+b"); }*/ /* state save */ /*for (i=0; isave_item(NAME(chip->channel[i].bank), i); device->save_item(NAME(chip->channel[i].address), i); device->save_item(NAME(chip->channel[i].pitch), i); device->save_item(NAME(chip->channel[i].loop), i); device->save_item(NAME(chip->channel[i].end), i); device->save_item(NAME(chip->channel[i].vol), i); device->save_item(NAME(chip->channel[i].pan), i); device->save_item(NAME(chip->channel[i].key), i); device->save_item(NAME(chip->channel[i].lvol), i); device->save_item(NAME(chip->channel[i].rvol), i); device->save_item(NAME(chip->channel[i].lastdt), i); device->save_item(NAME(chip->channel[i].offset), i); }*/ for (i = 0; i < QSOUND_CHANNELS; i ++) chip->channel[i].Muted = 0x00; return clock / QSOUND_CLOCKDIV; } //static DEVICE_STOP( qsound ) void device_stop_qsoundm(UINT8 ChipID) { //qsound_state *chip = get_safe_token(device); qsound_state *chip = &QSoundData[ChipID]; /*if (chip->fpRawDataR) { fclose(chip->fpRawDataR); } chip->fpRawDataR = NULL; if (chip->fpRawDataL) { fclose(chip->fpRawDataL); } chip->fpRawDataL = NULL;*/ free(chip->sample_rom); chip->sample_rom = NULL; } void device_reset_qsoundm(UINT8 ChipID) { qsound_state *chip = &QSoundData[ChipID]; int adr; // init sound regs memset(chip->channel, 0, sizeof(chip->channel)); for (adr = 0x7f; adr >= 0; adr--) qsound_set_command(chip, adr, 0); for (adr = 0x80; adr < 0x90; adr++) qsound_set_command(chip, adr, 0x120); return; } //WRITE8_DEVICE_HANDLER( qsoundm_w ) void qsoundm_w(UINT8 ChipID, offs_t offset, UINT8 data) { //qsound_state *chip = get_safe_token(device); qsound_state *chip = &QSoundData[ChipID]; switch (offset) { case 0: chip->data=(chip->data&0xff)|(data<<8); break; case 1: chip->data=(chip->data&0xff00)|data; break; case 2: qsound_set_command(chip, data, chip->data); break; default: //logerror("%s: unexpected qsound write to offset %d == %02X\n", device->machine().describe_context(), offset, data); logerror("QSound: unexpected qsound write to offset %d == %02X\n", offset, data); break; } } //READ8_DEVICE_HANDLER( qsoundm_r ) UINT8 qsoundm_r(UINT8 ChipID, offs_t offset) { /* Port ready bit (0x80 if ready) */ return 0x80; } static void qsound_set_command(qsound_state *chip, UINT8 address, UINT16 data) { int ch = 0, reg = 0; // direct sound reg if (address < 0x80) { ch = address >> 3; reg = address & 0x07; } // >= 0x80 is probably for the dsp? else if (address < 0x90) { ch = address & 0x0F; reg = 8; } else if (address >= 0xba && address < 0xca) { ch = address - 0xba; reg=9; } else { /* Unknown registers */ ch = 99; reg = 99; } switch (reg) { case 0: // bank, high bits unknown ch = (ch + 1) & 0x0f; /* strange ... */ chip->channel[ch].bank = (data & 0x7f) << 16; // Note: The most recent MAME doesn't do "& 0x7F" #ifdef _DEBUG if (data && !(data & 0x8000)) fprintf(stderr, "QSound Ch %u: Bank = %04x\n",ch,data); #endif break; case 1: // start/cur address chip->channel[ch].address = data; break; case 2: // frequency chip->channel[ch].freq = data; // This was working with the old code, but breaks the songs with the new one. // And I'm pretty sure the hardware won't do this. -Valley Bell /*if (!data) { // key off chip->channel[ch].enabled = 0; }*/ break; case 3: #ifdef _DEBUG if (chip->channel[ch].enabled && data != 0x8000) fprintf(stderr, "QSound Ch %u: KeyOn = %04x\n",ch,data); #endif // key on (does the value matter? it always writes 0x8000) //chip->channel[ch].enabled = 1; chip->channel[ch].enabled = (data & 0x8000) >> 15; chip->channel[ch].step_ptr = 0; break; case 4: // loop address chip->channel[ch].loop = data; break; case 5: // end address chip->channel[ch].end = data; break; case 6: // master volume #ifdef _DEBUG if (! chip->channel[ch].enabled && data) fprintf(stderr, "QSound update warning - please report!\n"); #endif chip->channel[ch].vol = data; break; case 7: // unused? #ifdef MAME_DEBUG popmessage("UNUSED QSOUND REG 7=%04x",data); #endif break; case 8: { // panning (left=0x0110, centre=0x0120, right=0x0130) // looks like it doesn't write other values than that int pan = (data & 0x3f) - 0x10; if (pan > 0x20) pan = 0x20; if (pan < 0) pan = 0; chip->channel[ch].rvol=chip->pan_table[pan]; chip->channel[ch].lvol=chip->pan_table[0x20 - pan]; } break; case 9: // unknown /* #ifdef MAME_DEBUG popmessage("QSOUND REG 9=%04x",data); #endif */ break; default: //logerror("%s: write_data %02x = %04x\n", machine().describe_context(), address, data); break; } //LOG(("QSOUND WRITE %02x CH%02d-R%02d =%04x\n", address, ch, reg, data)); } //static STREAM_UPDATE( qsoundm_update ) void qsoundm_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //qsound_state *chip = (qsound_state *)param; qsound_state *chip = &QSoundData[ChipID]; int i,j; UINT32 offset; UINT32 advance; INT8 sample; struct QSOUND_CHANNEL *pC=&chip->channel[0]; memset( outputs[0], 0x00, samples * sizeof(*outputs[0]) ); memset( outputs[1], 0x00, samples * sizeof(*outputs[1]) ); if (! chip->sample_rom_length) return; for (i=0; ienabled && ! pC->Muted) { QSOUND_SAMPLE *pOutL=outputs[0]; QSOUND_SAMPLE *pOutR=outputs[1]; for (j=samples-1; j>=0; j--) { advance = (pC->step_ptr >> 12); pC->step_ptr &= 0xfff; pC->step_ptr += pC->freq; if (advance) { pC->address += advance; if (pC->freq && pC->address >= pC->end) { if (pC->loop) { // Reached the end, restart the loop pC->address -= pC->loop; // Make sure we don't overflow (what does the real chip do in this case?) if (pC->address >= pC->end) pC->address = pC->end - pC->loop; pC->address &= 0xffff; } else { // Reached the end of a non-looped sample //pC->enabled = 0; pC->address --; // ensure that old ripped VGMs still work pC->step_ptr += 0x1000; break; } } } offset = (pC->bank | pC->address) % chip->sample_rom_length; sample = chip->sample_rom[offset]; *pOutL++ += ((sample * pC->lvol * pC->vol) >> 14); *pOutR++ += ((sample * pC->rvol * pC->vol) >> 14); } } } /*if (chip->fpRawDataL) fwrite(outputs[0], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataL); if (chip->fpRawDataR) fwrite(outputs[1], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataR);*/ } void qsoundm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { qsound_state* info = &QSoundData[ChipID]; if (info->sample_rom_length != ROMSize) { info->sample_rom = (QSOUND_SRC_SAMPLE*)realloc(info->sample_rom, ROMSize); info->sample_rom_length = ROMSize; memset(info->sample_rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(info->sample_rom + DataStart, ROMData, DataLength); return; } void qsoundm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { qsound_state* info = &QSoundData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < QSOUND_CHANNELS; CurChn ++) info->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( qsound ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(qsound_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( qsound ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( qsound ); break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "Q-Sound"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Capcom custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ /**************** end of file ****************/ //DEFINE_LEGACY_SOUND_DEVICE(QSOUND, qsound); ================================================ FILE: VGMPlay/chips/qsound_mame.h ================================================ /********************************************************* Capcom Q-Sound system *********************************************************/ #pragma once //#include "devlegcy.h" #define QSOUND_CLOCK 4000000 /* default 4MHz clock */ void qsoundm_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_qsoundm(UINT8 ChipID, int clock); void device_stop_qsoundm(UINT8 ChipID); void device_reset_qsoundm(UINT8 ChipID); //WRITE8_DEVICE_HANDLER( qsound_w ); //READ8_DEVICE_HANDLER( qsound_r ); void qsoundm_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 qsoundm_r(UINT8 ChipID, offs_t offset); void qsoundm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void qsoundm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(QSOUND, qsound); ================================================ FILE: VGMPlay/chips/rf5c68.c ================================================ /*********************************************************/ /* ricoh RF5C68(or clone) PCM controller */ /*********************************************************/ #include "mamedef.h" #include #include // for memset #include // for NULL //#include "sndintrf.h" //#include "streams.h" #include "rf5c68.h" #include #define NUM_CHANNELS (8) #define STEAM_STEP 0x800 typedef struct _pcm_channel pcm_channel; struct _pcm_channel { UINT8 enable; UINT8 env; UINT8 pan; UINT8 start; UINT32 addr; UINT16 step; UINT16 loopst; UINT8 Muted; }; typedef struct _mem_stream mem_stream; struct _mem_stream { UINT32 BaseAddr; UINT32 EndAddr; UINT32 CurAddr; UINT16 CurStep; const UINT8* MemPnt; }; typedef struct _rf5c68_state rf5c68_state; struct _rf5c68_state { //sound_stream * stream; pcm_channel chan[NUM_CHANNELS]; UINT8 cbank; UINT8 wbank; UINT8 enable; UINT32 datasize; UINT8* data; //void (*sample_callback)(running_device* device,int channel); mem_stream memstrm; }; static void rf5c68_mem_stream_flush(rf5c68_state *chip); #define MAX_CHIPS 0x02 static rf5c68_state RF5C68Data[MAX_CHIPS]; /*INLINE rf5c68_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_RF5C68); return (rf5c68_state *)device->token; }*/ /************************************************/ /* RF5C68 stream update */ /************************************************/ static void memstream_sample_check(rf5c68_state *chip, UINT32 addr, UINT16 Speed) { mem_stream* ms = &chip->memstrm; UINT32 SmplSpd; SmplSpd = (Speed >= 0x0800) ? (Speed >> 11) : 1; if (addr >= ms->CurAddr) { // Is the stream too fast? (e.g. about to catch up the output) if (addr - ms->CurAddr <= SmplSpd * 5) { // Yes - delay the stream ms->CurAddr -= SmplSpd * 4; if (ms->CurAddr < ms->BaseAddr) ms->CurAddr = ms->BaseAddr; } } else { // Is the stream too slow? (e.g. the output is about to catch up the stream) if (ms->CurAddr - addr <= SmplSpd * 5) { if (ms->CurAddr + SmplSpd * 4 >= ms->EndAddr) { rf5c68_mem_stream_flush(chip); } else { memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), SmplSpd * 4); ms->CurAddr += SmplSpd * 4; } } } return; } //static STREAM_UPDATE( rf5c68_update ) void rf5c68_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //rf5c68_state *chip = (rf5c68_state *)param; rf5c68_state *chip = &RF5C68Data[ChipID]; mem_stream* ms = &chip->memstrm; stream_sample_t *left = outputs[0]; stream_sample_t *right = outputs[1]; int i, j; /* start with clean buffers */ memset(left, 0, samples * sizeof(*left)); memset(right, 0, samples * sizeof(*right)); /* bail if not enabled */ if (!chip->enable) return; /* loop over channels */ for (i = 0; i < NUM_CHANNELS; i++) { pcm_channel *chan = &chip->chan[i]; /* if this channel is active, accumulate samples */ if (chan->enable && ! chan->Muted) { int lv = (chan->pan & 0x0f) * chan->env; int rv = ((chan->pan >> 4) & 0x0f) * chan->env; /* loop over the sample buffer */ for (j = 0; j < samples; j++) { int sample; /* trigger sample callback */ /*if(chip->sample_callback) { if(((chan->addr >> 11) & 0xfff) == 0xfff) chip->sample_callback(chip->device,((chan->addr >> 11)/0x2000)); }*/ memstream_sample_check(chip, (chan->addr >> 11) & 0xFFFF, chan->step); /* fetch the sample and handle looping */ sample = chip->data[(chan->addr >> 11) & 0xffff]; if (sample == 0xff) { chan->addr = chan->loopst << 11; sample = chip->data[(chan->addr >> 11) & 0xffff]; /* if we loop to a loop point, we're effectively dead */ if (sample == 0xff) break; } chan->addr += chan->step; /* add to the buffer */ if (sample & 0x80) { sample &= 0x7f; left[j] += (sample * lv) >> 5; right[j] += (sample * rv) >> 5; } else { left[j] -= (sample * lv) >> 5; right[j] -= (sample * rv) >> 5; } } } } if (samples && ms->CurAddr < ms->EndAddr) { ms->CurStep += STEAM_STEP * samples; if (ms->CurStep >= 0x0800) // 1 << 11 { i = ms->CurStep >> 11; ms->CurStep &= 0x07FF; if (ms->CurAddr + i > ms->EndAddr) i = ms->EndAddr - ms->CurAddr; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), i); ms->CurAddr += i; } } // I think, this is completely useless /* now clamp and shift the result (output is only 10 bits) */ /*for (j = 0; j < samples; j++) { stream_sample_t temp; temp = left[j]; if (temp > 32767) temp = 32767; else if (temp < -32768) temp = -32768; left[j] = temp & ~0x3f; temp = right[j]; if (temp > 32767) temp = 32767; else if (temp < -32768) temp = -32768; right[j] = temp & ~0x3f; }*/ } /************************************************/ /* RF5C68 start */ /************************************************/ //static DEVICE_START( rf5c68 ) int device_start_rf5c68(UINT8 ChipID, int clock) { //const rf5c68_interface* intf = (const rf5c68_interface*)device->baseconfig().static_config(); /* allocate memory for the chip */ //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip; int chn; if (ChipID >= MAX_CHIPS) return 0; chip = &RF5C68Data[ChipID]; chip->datasize = 0x10000; chip->data = (UINT8*)malloc(chip->datasize); /* allocate the stream */ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); /* set up callback */ /*if(intf != NULL) chip->sample_callback = intf->sample_end_callback; else chip->sample_callback = NULL;*/ for (chn = 0; chn < NUM_CHANNELS; chn ++) chip->chan[chn].Muted = 0x00; return (clock & 0x7FFFFFFF) / 384; } void device_stop_rf5c68(UINT8 ChipID) { rf5c68_state *chip = &RF5C68Data[ChipID]; free(chip->data); chip->data = NULL; return; } void device_reset_rf5c68(UINT8 ChipID) { rf5c68_state *chip = &RF5C68Data[ChipID]; int i; pcm_channel* chan; mem_stream* ms = &chip->memstrm; // Clear the PCM memory. memset(chip->data, 0x00, chip->datasize); chip->enable = 0; chip->cbank = 0; chip->wbank = 0; /* clear channel registers */ for (i = 0; i < NUM_CHANNELS; i ++) { chan = &chip->chan[i]; chan->enable = 0; chan->env = 0; chan->pan = 0; chan->start = 0; chan->addr = 0; chan->step = 0; chan->loopst = 0; } ms->BaseAddr = 0x0000; ms->CurAddr = 0x0000; ms->EndAddr = 0x0000; ms->CurStep = 0x0000; ms->MemPnt = NULL; } /************************************************/ /* RF5C68 write register */ /************************************************/ //WRITE8_DEVICE_HANDLER( rf5c68_w ) void rf5c68_w(UINT8 ChipID, offs_t offset, UINT8 data) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = &RF5C68Data[ChipID]; pcm_channel *chan = &chip->chan[chip->cbank]; int i; /* force the stream to update first */ //stream_update(chip->stream); /* switch off the address */ switch (offset) { case 0x00: /* envelope */ chan->env = data; break; case 0x01: /* pan */ chan->pan = data; break; case 0x02: /* FDL */ chan->step = (chan->step & 0xff00) | (data & 0x00ff); break; case 0x03: /* FDH */ chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00); break; case 0x04: /* LSL */ chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff); break; case 0x05: /* LSH */ chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00); break; case 0x06: /* ST */ chan->start = data; if (!chan->enable) chan->addr = chan->start << (8 + 11); break; case 0x07: /* control reg */ chip->enable = (data >> 7) & 1; if (data & 0x40) chip->cbank = data & 7; else chip->wbank = data & 15; break; case 0x08: /* channel on/off reg */ for (i = 0; i < 8; i++) { chip->chan[i].enable = (~data >> i) & 1; if (!chip->chan[i].enable) chip->chan[i].addr = chip->chan[i].start << (8 + 11); } break; } } /************************************************/ /* RF5C68 read memory */ /************************************************/ //READ8_DEVICE_HANDLER( rf5c68_mem_r ) UINT8 rf5c68_mem_r(UINT8 ChipID, offs_t offset) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = &RF5C68Data[ChipID]; return chip->data[chip->wbank * 0x1000 + offset]; } /************************************************/ /* RF5C68 write memory */ /************************************************/ //WRITE8_DEVICE_HANDLER( rf5c68_mem_w ) void rf5c68_mem_w(UINT8 ChipID, offs_t offset, UINT8 data) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = &RF5C68Data[ChipID]; rf5c68_mem_stream_flush(chip); chip->data[chip->wbank * 0x1000 | offset] = data; } static void rf5c68_mem_stream_flush(rf5c68_state *chip) { mem_stream* ms = &chip->memstrm; if (ms->CurAddr >= ms->EndAddr) return; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), ms->EndAddr - ms->CurAddr); ms->CurAddr = ms->EndAddr; return; } void rf5c68_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { rf5c68_state *chip = &RF5C68Data[ChipID]; mem_stream* ms = &chip->memstrm; UINT16 BytCnt; DataStart |= chip->wbank * 0x1000; if (DataStart >= chip->datasize) return; if (DataStart + DataLength > chip->datasize) DataLength = chip->datasize - DataStart; //memcpy(chip->data + DataStart, RAMData, DataLength); rf5c68_mem_stream_flush(chip); ms->BaseAddr = DataStart; ms->CurAddr = ms->BaseAddr; ms->EndAddr = ms->BaseAddr + DataLength; ms->CurStep = 0x0000; ms->MemPnt = RAMData; //BytCnt = (STEAM_STEP * 32) >> 11; BytCnt = 0x40; // SegaSonic Arcade: Run! Run! Run! needs such a high value if (ms->CurAddr + BytCnt > ms->EndAddr) BytCnt = ms->EndAddr - ms->CurAddr; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), BytCnt); ms->CurAddr += BytCnt; return; } void rf5c68_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { rf5c68_state *chip = &RF5C68Data[ChipID]; unsigned char CurChn; for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) chip->chan[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( rf5c68 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(rf5c68_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( rf5c68 ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: // Nothing break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "RF5C68"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Ricoh PCM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ /**************** end of file ****************/ ================================================ FILE: VGMPlay/chips/rf5c68.h ================================================ /*********************************************************/ /* ricoh RF5C68(or clone) PCM controller */ /*********************************************************/ #pragma once /******************************************/ /*WRITE8_DEVICE_HANDLER( rf5c68_w ); READ8_DEVICE_HANDLER( rf5c68_mem_r ); WRITE8_DEVICE_HANDLER( rf5c68_mem_w ); DEVICE_GET_INFO( rf5c68 ); #define SOUND_RF5C68 DEVICE_GET_INFO_NAME( rf5c68 )*/ void rf5c68_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_rf5c68(UINT8 ChipID, int clock); void device_stop_rf5c68(UINT8 ChipID); void device_reset_rf5c68(UINT8 ChipID); void rf5c68_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 rf5c68_mem_r(UINT8 ChipID, offs_t offset); void rf5c68_mem_w(UINT8 ChipID, offs_t offset, UINT8 data); void rf5c68_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void rf5c68_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/saa1099.c ================================================ /*************************************************************************** Philips SAA1099 Sound driver By Juergen Buchmueller and Manuel Abadia SAA1099 register layout: ======================== offs | 7654 3210 | description -----+-----------+--------------------------- 0x00 | ---- xxxx | Amplitude channel 0 (left) 0x00 | xxxx ---- | Amplitude channel 0 (right) 0x01 | ---- xxxx | Amplitude channel 1 (left) 0x01 | xxxx ---- | Amplitude channel 1 (right) 0x02 | ---- xxxx | Amplitude channel 2 (left) 0x02 | xxxx ---- | Amplitude channel 2 (right) 0x03 | ---- xxxx | Amplitude channel 3 (left) 0x03 | xxxx ---- | Amplitude channel 3 (right) 0x04 | ---- xxxx | Amplitude channel 4 (left) 0x04 | xxxx ---- | Amplitude channel 4 (right) 0x05 | ---- xxxx | Amplitude channel 5 (left) 0x05 | xxxx ---- | Amplitude channel 5 (right) | | 0x08 | xxxx xxxx | Frequency channel 0 0x09 | xxxx xxxx | Frequency channel 1 0x0a | xxxx xxxx | Frequency channel 2 0x0b | xxxx xxxx | Frequency channel 3 0x0c | xxxx xxxx | Frequency channel 4 0x0d | xxxx xxxx | Frequency channel 5 | | 0x10 | ---- -xxx | Channel 0 octave select 0x10 | -xxx ---- | Channel 1 octave select 0x11 | ---- -xxx | Channel 2 octave select 0x11 | -xxx ---- | Channel 3 octave select 0x12 | ---- -xxx | Channel 4 octave select 0x12 | -xxx ---- | Channel 5 octave select | | 0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on) 0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on) 0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on) 0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on) 0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on) 0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on) | | 0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on) 0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on) 0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on) 0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on) 0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on) 0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on) | | 0x16 | ---- --xx | Noise generator parameters 0 0x16 | --xx ---- | Noise generator parameters 1 | | 0x18 | --xx xxxx | Envelope generator 0 parameters 0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on) 0x19 | --xx xxxx | Envelope generator 1 parameters 0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on) | | 0x1c | ---- ---x | All channels enable (0 = off, 1 = on) 0x1c | ---- --x- | Synch & Reset generators ***************************************************************************/ //#include "emu.h" #include "mamedef.h" #include // for memset #include "saa1099.h" #define LEFT 0x00 #define RIGHT 0x01 /* this structure defines a channel */ struct saa1099_channel { int frequency; /* frequency (0x00..0xff) */ int freq_enable; /* frequency enable */ int noise_enable; /* noise enable */ int octave; /* octave (0x00..0x07) */ int amplitude[2]; /* amplitude (0x00..0x0f) */ int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ /* vars to simulate the square wave */ double counter; double freq; int level; UINT8 Muted; }; /* this structure defines a noise channel */ struct saa1099_noise { /* vars to simulate the noise generator output */ double counter; double freq; int level; /* noise polynomal shifter */ }; /* this structure defines a SAA1099 chip */ typedef struct _saa1099_state saa1099_state; struct _saa1099_state { //device_t *device; //sound_stream * stream; /* our stream */ int noise_params[2]; /* noise generators parameters */ int env_enable[2]; /* envelope generators enable */ int env_reverse_right[2]; /* envelope reversed for right channel */ int env_mode[2]; /* envelope generators mode */ int env_bits[2]; /* non zero = 3 bits resolution */ int env_clock[2]; /* envelope clock mode (non-zero external) */ int env_step[2]; /* current envelope step */ int all_ch_enable; /* all channels enable */ int sync_state; /* sync all channels */ int selected_reg; /* selected register */ struct saa1099_channel channels[6]; /* channels */ struct saa1099_noise noise[2]; /* noise generators */ double sample_rate; int master_clock; }; static const int amplitude_lookup[16] = { 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 }; static const UINT8 envelope[8][64] = { /* zero amplitude */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* maximum amplitude */ {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, /* single decay */ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* repetitive decay */ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, /* single triangular */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* repetitive triangular */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, /* single attack */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* repetitive attack */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } }; #define MAX_CHIPS 0x02 static saa1099_state SAA1099Data[MAX_CHIPS]; /*INLINE saa1099_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == SAA1099); return (saa1099_state *)downcast(device)->token(); }*/ static void saa1099_envelope(saa1099_state *saa, int ch) { if (saa->env_enable[ch]) { int step, mode, mask; mode = saa->env_mode[ch]; /* step from 0..63 and then loop in steps 32..63 */ step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); mask = 15; if (saa->env_bits[ch]) mask &= ~1; /* 3 bit resolution, mask LSB */ saa->channels[ch*3+0].envelope[ LEFT] = saa->channels[ch*3+1].envelope[ LEFT] = saa->channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; if (saa->env_reverse_right[ch] & 0x01) { saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; } else { saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; } } else { /* envelope mode off, set all envelope factors to 16 */ saa->channels[ch*3+0].envelope[ LEFT] = saa->channels[ch*3+1].envelope[ LEFT] = saa->channels[ch*3+2].envelope[ LEFT] = saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = 16; } } //static STREAM_UPDATE( saa1099_update ) void saa1099_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //saa1099_state *saa = (saa1099_state *)param; saa1099_state *saa = &SAA1099Data[ChipID]; int j, ch; int clk2div512; /* if the channels are disabled we're done */ if (!saa->all_ch_enable) { /* init output data */ memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT])); memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT])); return; } for (ch = 0; ch < 2; ch++) { switch (saa->noise_params[ch]) { case 0: saa->noise[ch].freq = saa->master_clock/ 256.0 * 2; break; case 1: saa->noise[ch].freq = saa->master_clock/ 512.0 * 2; break; case 2: saa->noise[ch].freq = saa->master_clock/1024.0 * 2; break; case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; } } // clock fix thanks to http://www.vogons.org/viewtopic.php?p=344227#p344227 //clk2div512 = 2 * saa->master_clock / 512; clk2div512 = (saa->master_clock + 128) / 256; /* fill all data needed */ for( j = 0; j < samples; j++ ) { int output_l = 0, output_r = 0; /* for each channel */ for (ch = 0; ch < 6; ch++) { if (saa->channels[ch].freq == 0.0) saa->channels[ch].freq = (double)(clk2div512 << saa->channels[ch].octave) / (511.0 - (double)saa->channels[ch].frequency); /* check the actual position in the square wave */ saa->channels[ch].counter -= saa->channels[ch].freq; while (saa->channels[ch].counter < 0) { /* calculate new frequency now after the half wave is updated */ saa->channels[ch].freq = (double)(clk2div512 << saa->channels[ch].octave) / (511.0 - (double)saa->channels[ch].frequency); saa->channels[ch].counter += saa->sample_rate; saa->channels[ch].level ^= 1; /* eventually clock the envelope counters */ if (ch == 1 && saa->env_clock[0] == 0) saa1099_envelope(saa, 0); if (ch == 4 && saa->env_clock[1] == 0) saa1099_envelope(saa, 1); } if (saa->channels[ch].Muted) continue; // placed here to ensure that envelopes are updated #if 0 // if the noise is enabled if (saa->channels[ch].noise_enable) { // if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) if (saa->noise[ch/3].level & 1) { // subtract to avoid overflows, also use only half amplitude output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; } } // if the square wave is enabled if (saa->channels[ch].freq_enable) { // if the channel level is high if (saa->channels[ch].level & 1) { output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; } } #else // Now with bipolar output. -Valley Bell if (saa->channels[ch].noise_enable) { if (saa->noise[ch/3].level & 1) { output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; } else { output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; } } if (saa->channels[ch].freq_enable) { if (saa->channels[ch].level & 1) { output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; } else { output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; } } #endif } for (ch = 0; ch < 2; ch++) { /* check the actual position in noise generator */ saa->noise[ch].counter -= saa->noise[ch].freq; while (saa->noise[ch].counter < 0) { saa->noise[ch].counter += saa->sample_rate; if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; else saa->noise[ch].level <<= 1; } } /* write sound data to the buffer */ outputs[LEFT][j] = output_l / 6; outputs[RIGHT][j] = output_r / 6; } } //static DEVICE_START( saa1099 ) int device_start_saa1099(UINT8 ChipID, int clock) { //saa1099_state *saa = get_safe_token(device); saa1099_state *saa; UINT8 CurChn; if (ChipID >= MAX_CHIPS) return 0; saa = &SAA1099Data[ChipID]; /* copy global parameters */ //saa->device = device; //saa->sample_rate = device->clock() / 256; saa->master_clock = clock; saa->sample_rate = clock / 128.0 * 8; /* for each chip allocate one stream */ //saa->stream = device->machine().sound().stream_alloc(*device, 0, 2, saa->sample_rate, saa, saa1099_update); for (CurChn = 0; CurChn < 6; CurChn ++) saa->channels[CurChn].Muted = 0x00; return (int)(saa->sample_rate + 0.5); } void device_stop_saa1099(UINT8 ChipID) { saa1099_state *saa = &SAA1099Data[ChipID]; return; } void device_reset_saa1099(UINT8 ChipID) { saa1099_state *saa = &SAA1099Data[ChipID]; struct saa1099_channel *sachn; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) { sachn = &saa->channels[CurChn]; sachn->frequency = 0; sachn->octave = 0; sachn->amplitude[0] = 0; sachn->amplitude[1] = 0; sachn->envelope[0] = 0; sachn->envelope[1] = 0; sachn->freq_enable = 0; sachn->noise_enable = 0; sachn->counter = 0; sachn->freq = 0; sachn->level = 0; } for (CurChn = 0; CurChn < 2; CurChn ++) { saa->noise[CurChn].counter = 0; saa->noise[CurChn].freq = 0; saa->noise[CurChn].level = 0; saa->noise_params[1] = 0x00; saa->env_reverse_right[CurChn] = 0x00; saa->env_mode[CurChn] = 0x00; saa->env_bits[CurChn] = 0x00; saa->env_clock[CurChn] = 0x00; saa->env_enable[CurChn] = 0x00; saa->env_step[CurChn] = 0; } saa->all_ch_enable = 0x00; saa->sync_state = 0x00; return; } //WRITE8_DEVICE_HANDLER( saa1099_control_w ) void saa1099_control_w(UINT8 ChipID, offs_t offset, UINT8 data) { //saa1099_state *saa = get_safe_token(device); saa1099_state *saa = &SAA1099Data[ChipID]; if ((data & 0xff) > 0x1c) { /* Error! */ //logerror("%s: (SAA1099 '%s') Unknown register selected\n",device->machine().describe_context(), device->tag()); logerror("SAA1099: Unknown register selected\n"); } saa->selected_reg = data & 0x1f; if (saa->selected_reg == 0x18 || saa->selected_reg == 0x19) { /* clock the envelope channels */ if (saa->env_clock[0]) saa1099_envelope(saa,0); if (saa->env_clock[1]) saa1099_envelope(saa,1); } } //WRITE8_DEVICE_HANDLER( saa1099_data_w ) void saa1099_data_w(UINT8 ChipID, offs_t offset, UINT8 data) { //saa1099_state *saa = get_safe_token(device); saa1099_state *saa = &SAA1099Data[ChipID]; int reg = saa->selected_reg; int ch; /* first update the stream to this point in time */ //saa->stream->update(); switch (reg) { /* channel i amplitude */ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: ch = reg & 7; saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; break; /* channel i frequency */ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: ch = reg & 7; saa->channels[ch].frequency = data & 0xff; break; /* channel i octave */ case 0x10: case 0x11: case 0x12: ch = (reg - 0x10) << 1; saa->channels[ch + 0].octave = data & 0x07; saa->channels[ch + 1].octave = (data >> 4) & 0x07; break; /* channel i frequency enable */ case 0x14: saa->channels[0].freq_enable = data & 0x01; saa->channels[1].freq_enable = data & 0x02; saa->channels[2].freq_enable = data & 0x04; saa->channels[3].freq_enable = data & 0x08; saa->channels[4].freq_enable = data & 0x10; saa->channels[5].freq_enable = data & 0x20; break; /* channel i noise enable */ case 0x15: saa->channels[0].noise_enable = data & 0x01; saa->channels[1].noise_enable = data & 0x02; saa->channels[2].noise_enable = data & 0x04; saa->channels[3].noise_enable = data & 0x08; saa->channels[4].noise_enable = data & 0x10; saa->channels[5].noise_enable = data & 0x20; break; /* noise generators parameters */ case 0x16: saa->noise_params[0] = data & 0x03; saa->noise_params[1] = (data >> 4) & 0x03; break; /* envelope generators parameters */ case 0x18: case 0x19: ch = reg - 0x18; saa->env_reverse_right[ch] = data & 0x01; saa->env_mode[ch] = (data >> 1) & 0x07; saa->env_bits[ch] = data & 0x10; saa->env_clock[ch] = data & 0x20; saa->env_enable[ch] = data & 0x80; /* reset the envelope */ saa->env_step[ch] = 0; break; /* channels enable & reset generators */ case 0x1c: saa->all_ch_enable = data & 0x01; saa->sync_state = data & 0x02; if (data & 0x02) { int i; /* Synch & Reset generators */ //logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n",device->machine().describe_context(), device->tag()); logerror("SAA1099: -reg 0x1c- Chip reset\n"); for (i = 0; i < 6; i++) { saa->channels[i].level = 0; saa->channels[i].counter = 0.0; } } break; default: /* Error! */ //logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n",device->machine().describe_context(), device->tag(), reg, data); logerror("SAA1099: Unknown operation (reg:%02x, data:%02x)\n",reg, data); } } void saa1099_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { saa1099_state *saa = &SAA1099Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) saa->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( saa1099 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(saa1099_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( saa1099 ); break; case DEVINFO_FCT_STOP: // Nothing // break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "SAA1099"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Philips"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(SAA1099, saa1099);*/ ================================================ FILE: VGMPlay/chips/saa1099.h ================================================ #pragma once #ifndef __SAA1099_H__ #define __SAA1099_H__ /********************************************** Philips SAA1099 Sound driver **********************************************/ //WRITE8_DEVICE_HANDLER( saa1099_control_w ); void saa1099_control_w(UINT8 ChipID, offs_t offset, UINT8 data); //WRITE8_DEVICE_HANDLER( saa1099_data_w ); void saa1099_data_w(UINT8 ChipID, offs_t offset, UINT8 data); //DECLARE_LEGACY_SOUND_DEVICE(SAA1099, saa1099); void saa1099_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_saa1099(UINT8 ChipID, int clock); void device_stop_saa1099(UINT8 ChipID); void device_reset_saa1099(UINT8 ChipID); void saa1099_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); #endif /* __SAA1099_H__ */ ================================================ FILE: VGMPlay/chips/scd_pcm.c ================================================ /***********************************************************/ /* */ /* PCM.C : PCM RF5C164 emulator */ /* */ /* This source is a part of Gens project */ /* Written by Stphane Dallongeville (gens@consolemul.com) */ /* Copyright (c) 2002 by Stphane Dallongeville */ /* */ /***********************************************************/ #include #include #include #include "mamedef.h" #include "scd_pcm.h" int PCM_Init(UINT8 ChipID, int Rate); void PCM_Set_Rate(UINT8 ChipID, int Rate); void PCM_Reset(UINT8 ChipID); void PCM_Write_Reg(UINT8 ChipID, unsigned int Reg, unsigned int Data); int PCM_Update(UINT8 ChipID, int **buf, int Length); #define PCM_STEP_SHIFT 11 extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static struct pcm_chip_ PCM_Chip[MAX_CHIPS]; /*static unsigned char VolTabIsInit = 0x00; static int PCM_Volume_Tab[256 * 256];*/ //unsigned char Ram_PCM[64 * 1024]; //int PCM_Enable; /** * PCM_Init(): Initialize the PCM chip. * @param Rate Sample rate. * @return 0 if successful. */ int PCM_Init(UINT8 ChipID, int Rate) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; int i/*, j, out*/; /*if (! VolTabIsInit) { for (i = 0; i < 0x100; i++) { for (j = 0; j < 0x100; j++) { if (i & 0x80) { out = -(i & 0x7F); out *= j; PCM_Volume_Tab[(j << 8) + i] = out; } else { out = i * j; PCM_Volume_Tab[(j << 8) + i] = out; } } } VolTabIsInit = 0x01; }*/ chip->Smpl0Patch = 0; for (i = 0; i < 8; i ++) chip->Channel[i].Muted = 0x00; chip->RAMSize = 64 * 1024; chip->RAM = (unsigned char*)malloc(chip->RAMSize); PCM_Reset(ChipID); PCM_Set_Rate(ChipID, Rate); return 0; } /** * PCM_Reset(): Reset the PCM chip. */ void PCM_Reset(UINT8 ChipID) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; int i; struct pcm_chan_* chan; // Clear the PCM memory. memset(chip->RAM, 0x00, chip->RAMSize); chip->Enable = 0; chip->Cur_Chan = 0; chip->Bank = 0; /* clear channel registers */ for (i = 0; i < 8; i++) { chan = &chip->Channel[i]; chan->Enable = 0; chan->ENV = 0; chan->PAN = 0; chan->St_Addr = 0; chan->Addr = 0; chan->Loop_Addr = 0; chan->Step = 0; chan->Step_B = 0; chan->Data = 0; } } /** * PCM_Set_Rate(): Change the PCM sample rate. * @param Rate New sample rate. */ void PCM_Set_Rate(UINT8 ChipID, int Rate) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; int i; if (Rate == 0) return; //chip->Rate = (float) (32 * 1024) / (float) Rate; chip->Rate = (float) (31.8 * 1024) / (float) Rate; for (i = 0; i < 8; i++) { chip->Channel[i].Step = (int) ((float) chip->Channel[i].Step_B * chip->Rate); } } /** * PCM_Write_Reg(): Write to a PCM register. * @param Reg Register ID. * @param Data Data to write. */ void PCM_Write_Reg(UINT8 ChipID, unsigned int Reg, unsigned int Data) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; int i; struct pcm_chan_* chan = &chip->Channel[chip->Cur_Chan]; Data &= 0xFF; switch (Reg) { case 0x00: /* evelope register */ chan->ENV = Data; chan->MUL_L = (Data * (chan->PAN & 0x0F)) >> 5; chan->MUL_R = (Data * (chan->PAN >> 4)) >> 5; break; case 0x01: /* pan register */ chan->PAN = Data; chan->MUL_L = ((Data & 0x0F) * chan->ENV) >> 5; chan->MUL_R = ((Data >> 4) * chan->ENV) >> 5; break; case 0x02: /* frequency step (LB) registers */ chan->Step_B &= 0xFF00; chan->Step_B += Data; chan->Step = (int)((float)chan->Step_B * chip->Rate); //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Step low = %.2X Step calculated = %.8X", // Data, chan->Step); break; case 0x03: /* frequency step (HB) registers */ chan->Step_B &= 0x00FF; chan->Step_B += Data << 8; chan->Step = (int)((float)chan->Step_B * chip->Rate); //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Step high = %.2X Step calculated = %.8X", // Data, chan->Step); break; case 0x04: chan->Loop_Addr &= 0xFF00; chan->Loop_Addr += Data; //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Loop low = %.2X Loop = %.8X", // Data, chan->Loop_Addr); break; case 0x05: /* loop address registers */ chan->Loop_Addr &= 0x00FF; chan->Loop_Addr += Data << 8; //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Loop high = %.2X Loop = %.8X", // Data, chan->Loop_Addr); break; case 0x06: /* start address registers */ chan->St_Addr = Data << (PCM_STEP_SHIFT + 8); //chan->Addr = chan->St_Addr; //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Start addr = %.2X New Addr = %.8X", // Data, chan->Addr); break; case 0x07: /* control register */ /* mod is H */ if (Data & 0x40) { /* select channel */ chip->Cur_Chan = Data & 0x07; } /* mod is L */ else { /* pcm ram bank select */ chip->Bank = (Data & 0x0F) << 12; } /* sounding bit */ if (Data & 0x80) chip->Enable = 0xFF; // Used as mask else chip->Enable = 0; //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "General Enable = %.2X", Data); break; case 0x08: /* sound on/off register */ Data ^= 0xFF; //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, // "Channel Enable = %.2X", Data); for (i = 0; i < 8; i++) { chan = &chip->Channel[i]; if (!chan->Enable) chan->Addr = chan->St_Addr; } for (i = 0; i < 8; i++) { chip->Channel[i].Enable = Data & (1 << i); } } } /** * PCM_Update(): Update the PCM buffer. * @param buf PCM buffer. * @param Length Buffer length. */ int PCM_Update(UINT8 ChipID, int **buf, int Length) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; int i, j; int *bufL, *bufR; //, *volL, *volR; unsigned int Addr, k; struct pcm_chan_ *CH; bufL = buf[0]; bufR = buf[1]; // clear buffers memset(bufL, 0, Length * sizeof(int)); memset(bufR, 0, Length * sizeof(int)); // if PCM disable, no sound if (!chip->Enable) return 1; #if 0 // faster for short update for (j = 0; j < Length; j++) { for (i = 0; i < 8; i++) { CH = &(chip->Channel[i]); // only loop when sounding and on if (CH->Enable && ! CH->Muted) { Addr = CH->Addr >> PCM_STEP_SHIFT; if (Addr & 0x10000) { for(k = CH->Old_Addr; k < 0x10000; k++) { if (chip->RAM[k] == 0xFF) { CH->Old_Addr = Addr = CH->Loop_Addr; CH->Addr = Addr << PCM_STEP_SHIFT; break; } } if (Addr & 0x10000) { //CH->Addr -= CH->Step; CH->Enable = 0; break; } } else { for(k = CH->Old_Addr; k <= Addr; k++) { if (chip->RAM[k] == 0xFF) { CH->Old_Addr = Addr = CH->Loop_Addr; CH->Addr = Addr << PCM_STEP_SHIFT; break; } } } // test for loop signal if (chip->RAM[Addr] == 0xFF) { Addr = CH->Loop_Addr; CH->Addr = Addr << PCM_STEP_SHIFT; } if (chip->RAM[Addr] & 0x80) { CH->Data = chip->RAM[Addr] & 0x7F; bufL[j] -= CH->Data * CH->MUL_L; bufR[j] -= CH->Data * CH->MUL_R; } else { CH->Data = chip->RAM[Addr]; bufL[j] += CH->Data * CH->MUL_L; bufR[j] += CH->Data * CH->MUL_R; } // update address register //CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; CH->Addr += CH->Step; CH->Old_Addr = Addr + 1; } } } #endif #if 1 // for long update for (i = 0; i < 8; i++) { CH = &(chip->Channel[i]); // only loop when sounding and on if (CH->Enable && ! CH->Muted) { Addr = CH->Addr >> PCM_STEP_SHIFT; //volL = &(PCM_Volume_Tab[CH->MUL_L << 8]); //volR = &(PCM_Volume_Tab[CH->MUL_R << 8]); for (j = 0; j < Length; j++) { // test for loop signal if (chip->RAM[Addr] == 0xFF) { CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; if (chip->RAM[Addr] == 0xFF) break; else j--; } else { if (chip->RAM[Addr] & 0x80) { CH->Data = chip->RAM[Addr] & 0x7F; bufL[j] -= CH->Data * CH->MUL_L; bufR[j] -= CH->Data * CH->MUL_R; } else { CH->Data = chip->RAM[Addr]; // this improves the sound of Cosmic Fantasy Stories, // although it's definately false behaviour if (! CH->Data && chip->Smpl0Patch) CH->Data = -0x7F; bufL[j] += CH->Data * CH->MUL_L; bufR[j] += CH->Data * CH->MUL_R; } // update address register k = Addr + 1; CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; Addr = CH->Addr >> PCM_STEP_SHIFT; for (; k < Addr; k++) { if (chip->RAM[k] == 0xFF) { CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; break; } } } } if (chip->RAM[Addr] == 0xFF) { CH->Addr = CH->Loop_Addr << PCM_STEP_SHIFT; } } } #endif return 0; } void rf5c164_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; PCM_Update(ChipID, outputs, samples); } int device_start_rf5c164(UINT8 ChipID, int clock) { /* allocate memory for the chip */ //rf5c164_state *chip = get_safe_token(device); struct pcm_chip_ *chip; int rate; if (ChipID >= MAX_CHIPS) return 0; chip = &PCM_Chip[ChipID]; rate = (clock & 0x7FFFFFFF) / 384; if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; PCM_Init(ChipID, rate); chip->Smpl0Patch = (clock & 0x80000000) >> 31; /* allocate the stream */ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); return rate; } void device_stop_rf5c164(UINT8 ChipID) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; free(chip->RAM); chip->RAM = NULL; return; } void device_reset_rf5c164(UINT8 ChipID) { //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; PCM_Reset(ChipID); } void rf5c164_w(UINT8 ChipID, offs_t offset, UINT8 data) { //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; PCM_Write_Reg(ChipID, offset, data); } void rf5c164_mem_w(UINT8 ChipID, offs_t offset, UINT8 data) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; chip->RAM[chip->Bank | offset] = data; } void rf5c164_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; DataStart |= chip->Bank; if (DataStart >= chip->RAMSize) return; if (DataStart + DataLength > chip->RAMSize) DataLength = chip->RAMSize - DataStart; memcpy(chip->RAM + DataStart, RAMData, DataLength); return; } void rf5c164_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { struct pcm_chip_ *chip = &PCM_Chip[ChipID]; unsigned char CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) chip->Channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/scd_pcm.h ================================================ struct pcm_chip_ { float Rate; int Smpl0Patch; int Enable; int Cur_Chan; int Bank; struct pcm_chan_ { unsigned int ENV; /* envelope register */ unsigned int PAN; /* pan register */ unsigned int MUL_L; /* envelope & pan product letf */ unsigned int MUL_R; /* envelope & pan product right */ unsigned int St_Addr; /* start address register */ unsigned int Loop_Addr; /* loop address register */ unsigned int Addr; /* current address register */ unsigned int Step; /* frequency register */ unsigned int Step_B; /* frequency register binaire */ unsigned int Enable; /* channel on/off register */ int Data; /* wave data */ unsigned int Muted; } Channel[8]; unsigned long int RAMSize; unsigned char* RAM; }; //extern struct pcm_chip_ PCM_Chip; //extern unsigned char Ram_PCM[64 * 1024]; //extern int PCM_Enable; //int PCM_Init(int Rate); //void PCM_Set_Rate(int Rate); //void PCM_Reset(void); //void PCM_Write_Reg(unsigned int Reg, unsigned int Data); //int PCM_Update(int **buf, int Length); void rf5c164_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_rf5c164(UINT8 ChipID, int clock); void device_stop_rf5c164(UINT8 ChipID); void device_reset_rf5c164(UINT8 ChipID); void rf5c164_w(UINT8 ChipID, offs_t offset, UINT8 data); void rf5c164_mem_w(UINT8 ChipID, offs_t offset, UINT8 data); void rf5c164_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void rf5c164_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/scsp.c ================================================ /* Sega/Yamaha YMF292-F (SCSP = Saturn Custom Sound Processor) emulation By ElSemi MAME/M1 conversion and cleanup by R. Belmont Additional code and bugfixes by kingshriek This chip has 32 voices. Each voice can play a sample or be part of an FM construct. Unlike traditional Yamaha FM chips, the base waveform for the FM still comes from the wavetable RAM. ChangeLog: * November 25, 2003 (ES) Fixed buggy timers and envelope overflows. (RB) Improved sample rates other than 44100, multiple chips now works properly. * December 02, 2003 (ES) Added DISDL register support, improves mix. * April 28, 2004 (ES) Corrected envelope rates, added key-rate scaling, added ringbuffer support. * January 8, 2005 (RB) Added ability to specify region offset for RAM. * January 26, 2007 (ES) Added on-board DSP capability * September 24, 2007 (RB+ES) Removed fake reverb. Rewrote timers and IRQ handling. Fixed case where voice frequency is updated while looping. Enabled DSP again. * December 16, 2007 (kingshriek) Many EG bug fixes, implemented effects mixer, implemented FM. * January 5, 2008 (kingshriek+RB) Working, good-sounding FM, removed obsolete non-USEDSP code. * April 22, 2009 ("PluginNinja") Improved slot monitor, misc cleanups * June 6, 2011 (AS) Rewrote DMA from scratch, Darius 2 relies on it. */ //#include "emu.h" #include "mamedef.h" #include // for pow() in scsplfo.c #include #include // for memset #include "scsp.h" #include "scspdsp.h" #define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) #define SHIFT 12 #define FIX(v) ((UINT32) ((float) (1<udata.data[0x0]>>0x0)&0x1000) #define KEYONB(slot) ((slot->udata.data[0x0]>>0x0)&0x0800) #define SBCTL(slot) ((slot->udata.data[0x0]>>0x9)&0x0003) #define SSCTL(slot) ((slot->udata.data[0x0]>>0x7)&0x0003) #define LPCTL(slot) ((slot->udata.data[0x0]>>0x5)&0x0003) #define PCM8B(slot) ((slot->udata.data[0x0]>>0x0)&0x0010) #define SA(slot) (((slot->udata.data[0x0]&0xF)<<16)|(slot->udata.data[0x1])) #define LSA(slot) (slot->udata.data[0x2]) #define LEA(slot) (slot->udata.data[0x3]) #define D2R(slot) ((slot->udata.data[0x4]>>0xB)&0x001F) #define D1R(slot) ((slot->udata.data[0x4]>>0x6)&0x001F) #define EGHOLD(slot) ((slot->udata.data[0x4]>>0x0)&0x0020) #define AR(slot) ((slot->udata.data[0x4]>>0x0)&0x001F) #define LPSLNK(slot) ((slot->udata.data[0x5]>>0x0)&0x4000) #define KRS(slot) ((slot->udata.data[0x5]>>0xA)&0x000F) #define DL(slot) ((slot->udata.data[0x5]>>0x5)&0x001F) #define RR(slot) ((slot->udata.data[0x5]>>0x0)&0x001F) #define STWINH(slot) ((slot->udata.data[0x6]>>0x0)&0x0200) #define SDIR(slot) ((slot->udata.data[0x6]>>0x0)&0x0100) #define TL(slot) ((slot->udata.data[0x6]>>0x0)&0x00FF) #define MDL(slot) ((slot->udata.data[0x7]>>0xC)&0x000F) #define MDXSL(slot) ((slot->udata.data[0x7]>>0x6)&0x003F) #define MDYSL(slot) ((slot->udata.data[0x7]>>0x0)&0x003F) #define OCT(slot) ((slot->udata.data[0x8]>>0xB)&0x000F) #define FNS(slot) ((slot->udata.data[0x8]>>0x0)&0x03FF) #define LFORE(slot) ((slot->udata.data[0x9]>>0x0)&0x8000) #define LFOF(slot) ((slot->udata.data[0x9]>>0xA)&0x001F) #define PLFOWS(slot) ((slot->udata.data[0x9]>>0x8)&0x0003) #define PLFOS(slot) ((slot->udata.data[0x9]>>0x5)&0x0007) #define ALFOWS(slot) ((slot->udata.data[0x9]>>0x3)&0x0003) #define ALFOS(slot) ((slot->udata.data[0x9]>>0x0)&0x0007) #define ISEL(slot) ((slot->udata.data[0xA]>>0x3)&0x000F) #define IMXL(slot) ((slot->udata.data[0xA]>>0x0)&0x0007) #define DISDL(slot) ((slot->udata.data[0xB]>>0xD)&0x0007) #define DIPAN(slot) ((slot->udata.data[0xB]>>0x8)&0x001F) #define EFSDL(slot) ((slot->udata.data[0xB]>>0x5)&0x0007) #define EFPAN(slot) ((slot->udata.data[0xB]>>0x0)&0x001F) //Envelope times in ms static const double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0, 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0, 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4, 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0}; static const double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0, 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0, 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0, 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1}; typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE; struct _EG { int volume; // _STATE state; int step; //step vals int AR; //Attack int D1R; //Decay1 int D2R; //Decay2 int RR; //Release int DL; //Decay level UINT8 EGHOLD; UINT8 LPLINK; }; struct _SLOT { union { UINT16 data[0x10]; //only 0x1a bytes used UINT8 datab[0x20]; } udata; UINT8 Backwards; //the wave is playing backwards UINT8 active; //this slot is currently playing UINT8 Muted; UINT8 *base; //samples base address UINT32 cur_addr; //current play address (24.8) UINT32 nxt_addr; //next play address UINT32 step; //pitch step (24.8) struct _EG EG; //Envelope struct _LFO PLFO; //Phase LFO struct _LFO ALFO; //Amplitude LFO int slot; signed short Prev; //Previous sample (for interpolation) }; #define MEM4B(scsp) ((scsp->udata.data[0]>>0x0)&0x0200) #define DAC18B(scsp) ((scsp->udata.data[0]>>0x0)&0x0100) #define MVOL(scsp) ((scsp->udata.data[0]>>0x0)&0x000F) #define RBL(scsp) ((scsp->udata.data[1]>>0x7)&0x0003) #define RBP(scsp) ((scsp->udata.data[1]>>0x0)&0x003F) #define MOFULL(scsp) ((scsp->udata.data[2]>>0x0)&0x1000) #define MOEMPTY(scsp) ((scsp->udata.data[2]>>0x0)&0x0800) #define MIOVF(scsp) ((scsp->udata.data[2]>>0x0)&0x0400) #define MIFULL(scsp) ((scsp->udata.data[2]>>0x0)&0x0200) #define MIEMPTY(scsp) ((scsp->udata.data[2]>>0x0)&0x0100) #define SCILV0(scsp) ((scsp->udata.data[0x24/2]>>0x0)&0xff) #define SCILV1(scsp) ((scsp->udata.data[0x26/2]>>0x0)&0xff) #define SCILV2(scsp) ((scsp->udata.data[0x28/2]>>0x0)&0xff) #define SCIEX0 0 #define SCIEX1 1 #define SCIEX2 2 #define SCIMID 3 #define SCIDMA 4 #define SCIIRQ 5 #define SCITMA 6 #define SCITMB 7 #define USEDSP typedef struct _scsp_state scsp_state; struct _scsp_state { union { UINT16 data[0x30/2]; UINT8 datab[0x30]; } udata; struct _SLOT Slots[32]; signed short RINGBUF[128]; unsigned char BUFPTR; #if FM_DELAY signed short DELAYBUF[FM_DELAY]; unsigned char DELAYPTR; #endif unsigned char *SCSPRAM; UINT32 SCSPRAM_LENGTH; //char Master; //void (*Int68kCB)(device_t *device, int irq); //sound_stream * stream; int clock; int rate; //INT32 *buffertmpl,*buffertmpr; /*UINT32 IrqTimA; UINT32 IrqTimBC; UINT32 IrqMidi;*/ UINT8 MidiOutW,MidiOutR; UINT8 MidiStack[32]; UINT8 MidiW,MidiR; INT32 EG_TABLE[0x400]; int LPANTABLE[0x10000]; int RPANTABLE[0x10000]; int TimPris[3]; int TimCnt[3]; // timers //emu_timer *timerA, *timerB, *timerC; // DMA stuff struct { UINT32 dmea; UINT16 drga; UINT16 dtlg; UINT8 dgate; UINT8 ddir; } dma; UINT16 mcieb; UINT16 mcipd; int ARTABLE[64], DRTABLE[64]; struct _SCSPDSP DSP; //devcb_resolved_write_line main_irq; //device_t *device; }; //static void SCSP_exec_dma(address_space *space, scsp_state *scsp); /*state DMA transfer function*/ /* TODO */ //#define dma_transfer_end ((scsp_regs[0x24/2] & 0x10)>>4)|(((scsp_regs[0x26/2] & 0x10)>>4)<<1)|(((scsp_regs[0x28/2] & 0x10)>>4)<<2) static const float SDLT[8]={-1000000.0f,-36.0f,-30.0f,-24.0f,-18.0f,-12.0f,-6.0f,0.0f}; //static stream_sample_t *bufferl; //static stream_sample_t *bufferr; //static int length; static signed short *RBUFDST; //this points to where the sample will be stored in the RingBuf #define MAX_CHIPS 0x02 static scsp_state SCSPData[MAX_CHIPS]; static UINT8 BypassDSP = 0x01; /*INLINE scsp_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == SCSP); return (scsp_state *)downcast(device)->token(); }*/ /*static unsigned char DecodeSCI(scsp_state *scsp,unsigned char irq) { unsigned char SCI=0; unsigned char v; v=(SCILV0((scsp))&(1<udata.data[0x20/2]; UINT32 en=scsp->udata.data[0x1e/2]; if(scsp->MidiW!=scsp->MidiR) { scsp->udata.data[0x20/2] |= 8; pend |= 8; } if(!pend) return; if(pend&0x40) if(en&0x40) { scsp->Int68kCB(scsp->device, scsp->IrqTimA); return; } if(pend&0x80) if(en&0x80) { scsp->Int68kCB(scsp->device, scsp->IrqTimBC); return; } if(pend&0x100) if(en&0x100) { scsp->Int68kCB(scsp->device, scsp->IrqTimBC); return; } if(pend&8) if (en&8) { scsp->Int68kCB(scsp->device, scsp->IrqMidi); scsp->udata.data[0x20/2] &= ~8; return; } scsp->Int68kCB(scsp->device, 0); } static void ResetInterrupts(scsp_state *scsp) { UINT32 reset = scsp->udata.data[0x22/2]; if (reset & 0x40) { scsp->Int68kCB(scsp->device, -scsp->IrqTimA); } if (reset & 0x180) { scsp->Int68kCB(scsp->device, -scsp->IrqTimBC); } if (reset & 0x8) { scsp->Int68kCB(scsp->device, -scsp->IrqMidi); } CheckPendingIRQ(scsp); } static TIMER_CALLBACK( timerA_cb ) { scsp_state *scsp = (scsp_state *)ptr; scsp->TimCnt[0] = 0xFFFF; scsp->udata.data[0x20/2]|=0x40; scsp->udata.data[0x18/2]&=0xff00; scsp->udata.data[0x18/2]|=scsp->TimCnt[0]>>8; CheckPendingIRQ(scsp); } static TIMER_CALLBACK( timerB_cb ) { scsp_state *scsp = (scsp_state *)ptr; scsp->TimCnt[1] = 0xFFFF; scsp->udata.data[0x20/2]|=0x80; scsp->udata.data[0x1a/2]&=0xff00; scsp->udata.data[0x1a/2]|=scsp->TimCnt[1]>>8; CheckPendingIRQ(scsp); } static TIMER_CALLBACK( timerC_cb ) { scsp_state *scsp = (scsp_state *)ptr; scsp->TimCnt[2] = 0xFFFF; scsp->udata.data[0x20/2]|=0x100; scsp->udata.data[0x1c/2]&=0xff00; scsp->udata.data[0x1c/2]|=scsp->TimCnt[2]>>8; CheckPendingIRQ(scsp); }*/ static int Get_AR(scsp_state *scsp,int base,int R) { int Rate=base+(R<<1); if(Rate>63) Rate=63; if(Rate<0) Rate=0; return scsp->ARTABLE[Rate]; } static int Get_DR(scsp_state *scsp,int base,int R) { int Rate=base+(R<<1); if(Rate>63) Rate=63; if(Rate<0) Rate=0; return scsp->DRTABLE[Rate]; } static int Get_RR(scsp_state *scsp,int base,int R) { int Rate=base+(R<<1); if(Rate>63) Rate=63; if(Rate<0) Rate=0; return scsp->DRTABLE[Rate]; } static void Compute_EG(scsp_state *scsp,struct _SLOT *slot) { int octave=(OCT(slot)^8)-8; int rate; if(KRS(slot)!=0xf) rate=octave+2*KRS(slot)+((FNS(slot)>>9)&1); else rate=0; //rate=((FNS(slot)>>9)&1); slot->EG.volume=0x17F<EG.AR=Get_AR(scsp,rate,AR(slot)); slot->EG.D1R=Get_DR(scsp,rate,D1R(slot)); slot->EG.D2R=Get_DR(scsp,rate,D2R(slot)); slot->EG.RR=Get_RR(scsp,rate,RR(slot)); slot->EG.DL=0x1f-DL(slot); slot->EG.EGHOLD=EGHOLD(slot); } static void SCSP_StopSlot(struct _SLOT *slot,int keyoff); static int EG_Update(struct _SLOT *slot) { switch(slot->EG.state) { case ATTACK: slot->EG.volume+=slot->EG.AR; if(slot->EG.volume>=(0x3ff<EG.state=DECAY1; if(slot->EG.D1R>=(1024<EG.state=DECAY2; } slot->EG.volume=0x3ff<EG.EGHOLD) return 0x3ff<<(SHIFT-10); break; case DECAY1: slot->EG.volume-=slot->EG.D1R; if(slot->EG.volume<=0) slot->EG.volume=0; if(slot->EG.volume>>(EG_SHIFT+5)<=slot->EG.DL) slot->EG.state=DECAY2; break; case DECAY2: if(D2R(slot)==0) return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10); slot->EG.volume-=slot->EG.D2R; if(slot->EG.volume<=0) slot->EG.volume=0; break; case RELEASE: slot->EG.volume-=slot->EG.RR; if(slot->EG.volume<=0) { slot->EG.volume=0; SCSP_StopSlot(slot,0); //slot->EG.volume=0x17F<EG.state=ATTACK; } break; default: return 1<EG.volume>>EG_SHIFT)<<(SHIFT-10); } static UINT32 SCSP_Step(struct _SLOT *slot) { int octave=(OCT(slot)^8)-8+SHIFT-10; UINT32 Fn=FNS(slot)+(1 << 10); if (octave >= 0) { Fn<<=octave; } else { Fn>>=-octave; } return Fn; } static void Compute_LFO(struct _SLOT *slot) { if(PLFOS(slot)!=0) LFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0); if(ALFOS(slot)!=0) LFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1); } static void SCSP_StartSlot(scsp_state *scsp, struct _SLOT *slot) { UINT32 start_offset; slot->active=1; start_offset = PCM8B(slot) ? SA(slot) : SA(slot) & 0x7FFFE; slot->base=scsp->SCSPRAM + start_offset; slot->cur_addr=0; slot->nxt_addr=1<step=SCSP_Step(slot); Compute_EG(scsp,slot); slot->EG.state=ATTACK; slot->EG.volume=0x17F<Prev=0; slot->Backwards=0; Compute_LFO(slot); // printf("StartSlot[%p]: SA %x PCM8B %x LPCTL %x ALFOS %x STWINH %x TL %x EFSDL %x\n", slot, SA(slot), PCM8B(slot), LPCTL(slot), ALFOS(slot), STWINH(slot), TL(slot), EFSDL(slot)); } static void SCSP_StopSlot(struct _SLOT *slot,int keyoff) { if(keyoff /*&& slot->EG.state!=RELEASE*/) { slot->EG.state=RELEASE; } else { slot->active=0; } slot->udata.data[0]&=~0x800; } #define log_base_2(n) (log((double)(n))/log(2.0)) //static void SCSP_Init(device_t *device, scsp_state *scsp, const scsp_interface *intf) static void SCSP_Init(scsp_state *scsp, int clock) { int i; memset(scsp,0,sizeof(*scsp)); SCSPDSP_Init(&scsp->DSP); //scsp->device = device; scsp->clock = clock; scsp->rate = clock / 512; //scsp->IrqTimA = scsp->IrqTimBC = scsp->IrqMidi = 0; scsp->MidiR=scsp->MidiW=0; scsp->MidiOutR=scsp->MidiOutW=0; // get SCSP RAM /*if (strcmp(device->tag(), "scsp") == 0 || strcmp(device->tag(), "scsp1") == 0) { scsp->Master=1; } else { scsp->Master=0; }*/ /*scsp->SCSPRAM = *device->region(); if (scsp->SCSPRAM) { scsp->SCSPRAM_LENGTH = device->region()->bytes(); scsp->DSP.SCSPRAM = (UINT16 *)scsp->SCSPRAM; scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH/2; scsp->SCSPRAM += intf->roffset; }*/ scsp->SCSPRAM_LENGTH = 0x80000; // 512 KB scsp->SCSPRAM = (unsigned char*)malloc(scsp->SCSPRAM_LENGTH); scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; /*scsp->timerA = device->machine().scheduler().timer_alloc(FUNC(timerA_cb), scsp); scsp->timerB = device->machine().scheduler().timer_alloc(FUNC(timerB_cb), scsp); scsp->timerC = device->machine().scheduler().timer_alloc(FUNC(timerC_cb), scsp);*/ for(i=0;i<0x400;++i) { float envDB=((float)(3*(i-0x3ff)))/32.0f; float scale=(float)(1<EG_TABLE[i]=(INT32)(pow(10.0,envDB/20.0)*scale); } for(i=0;i<0x10000;++i) { int iTL =(i>>0x0)&0xff; int iPAN=(i>>0x8)&0x1f; int iSDL=(i>>0xD)&0x07; float TL=1.0f; float SegaDB=0.0f; float fSDL=1.0f; float PAN=1.0f; float LPAN,RPAN; if(iTL&0x01) SegaDB-=0.4f; if(iTL&0x02) SegaDB-=0.8f; if(iTL&0x04) SegaDB-=1.5f; if(iTL&0x08) SegaDB-=3.0f; if(iTL&0x10) SegaDB-=6.0f; if(iTL&0x20) SegaDB-=12.0f; if(iTL&0x40) SegaDB-=24.0f; if(iTL&0x80) SegaDB-=48.0f; TL=pow(10.0,SegaDB/20.0); SegaDB=0; if(iPAN&0x1) SegaDB-=3.0f; if(iPAN&0x2) SegaDB-=6.0f; if(iPAN&0x4) SegaDB-=12.0f; if(iPAN&0x8) SegaDB-=24.0f; if((iPAN&0xf)==0xf) PAN=0.0; else PAN=pow(10.0,SegaDB/20.0); if(iPAN<0x10) { LPAN=PAN; RPAN=1.0; } else { RPAN=PAN; LPAN=1.0; } if(iSDL) fSDL=pow(10.0,(SDLT[iSDL])/20.0); else fSDL=0.0; scsp->LPANTABLE[i]=FIX((4.0*LPAN*TL*fSDL)); scsp->RPANTABLE[i]=FIX((4.0*RPAN*TL*fSDL)); } scsp->ARTABLE[0]=scsp->DRTABLE[0]=0; //Infinite time scsp->ARTABLE[1]=scsp->DRTABLE[1]=0; //Infinite time for(i=2;i<64;++i) { double t,step,scale; t=ARTimes[i]; //In ms if(t!=0.0) { step=(1023*1000.0)/((float) 44100.0f*t); scale=(double) (1<ARTABLE[i]=(int) (step*scale); } else scsp->ARTABLE[i]=1024<DRTABLE[i]=(int) (step*scale); } // make sure all the slots are off for(i=0;i<32;++i) { scsp->Slots[i].slot=i; scsp->Slots[i].active=0; scsp->Slots[i].base=NULL; scsp->Slots[i].EG.state=RELEASE; } //LFO_Init(device->machine()); LFO_Init(); //scsp->buffertmpl=auto_alloc_array_clear(device->machine(), signed int, 44100); //scsp->buffertmpr=auto_alloc_array_clear(device->machine(), signed int, 44100); // no "pend" scsp->udata.data[0x20/2] = 0; scsp->TimCnt[0] = 0xffff; scsp->TimCnt[1] = 0xffff; scsp->TimCnt[2] = 0xffff; } INLINE void SCSP_UpdateSlotReg(scsp_state *scsp,int s,int r) { struct _SLOT *slot=scsp->Slots+s; int sl; switch(r&0x3f) { case 0: case 1: if(KEYONEX(slot)) { for(sl=0;sl<32;++sl) { struct _SLOT *s2=scsp->Slots+sl; { if(KEYONB(s2) && s2->EG.state==RELEASE/*&& !s2->active*/) { SCSP_StartSlot(scsp, s2); } if(!KEYONB(s2) /*&& s2->active*/) { SCSP_StopSlot(s2,1); } } } slot->udata.data[0]&=~0x1000; } break; case 0x10: case 0x11: slot->step=SCSP_Step(slot); break; case 0xA: case 0xB: slot->EG.RR=Get_RR(scsp,0,RR(slot)); slot->EG.DL=0x1f-DL(slot); break; case 0x12: case 0x13: Compute_LFO(slot); break; } } INLINE void SCSP_UpdateReg(scsp_state *scsp, /*address_space &space,*/ int reg) { switch(reg&0x3f) { case 0x0: // TODO: Make this work in VGMPlay //scsp->stream->set_output_gain(0,MVOL(scsp) / 15.0); //scsp->stream->set_output_gain(1,MVOL(scsp) / 15.0); break; case 0x2: case 0x3: { unsigned int v=RBL(scsp); scsp->DSP.RBP=RBP(scsp); if(v==0) scsp->DSP.RBL=8*1024; else if(v==1) scsp->DSP.RBL=16*1024; else if(v==2) scsp->DSP.RBL=32*1024; else if(v==3) scsp->DSP.RBL=64*1024; } break; case 0x6: case 0x7: //scsp_midi_in(space->machine().device("scsp"), 0, scsp->udata.data[0x6/2]&0xff, 0); break; case 8: case 9: /* Only MSLC could be written. */ scsp->udata.data[0x8/2] &= 0x7800; break; case 0x12: case 0x13: //scsp->dma.dmea = (scsp->udata.data[0x12/2] & 0xfffe) | (scsp->dma.dmea & 0xf0000); break; case 0x14: case 0x15: //scsp->dma.dmea = ((scsp->udata.data[0x14/2] & 0xf000) << 4) | (scsp->dma.dmea & 0xfffe); //scsp->dma.drga = (scsp->udata.data[0x14/2] & 0x0ffe); break; case 0x16: case 0x17: //scsp->dma.dtlg = (scsp->udata.data[0x16/2] & 0x0ffe); //scsp->dma.ddir = (scsp->udata.data[0x16/2] & 0x2000) >> 13; //scsp->dma.dgate = (scsp->udata.data[0x16/2] & 0x4000) >> 14; //if(scsp->udata.data[0x16/2] & 0x1000) // dexe // SCSP_exec_dma(space, scsp); break; case 0x18: case 0x19: /*if(scsp->Master) { UINT32 time; scsp->TimPris[0]=1<<((scsp->udata.data[0x18/2]>>8)&0x7); scsp->TimCnt[0]=(scsp->udata.data[0x18/2]&0xff)<<8; if ((scsp->udata.data[0x18/2]&0xff) != 255) { time = (44100 / scsp->TimPris[0]) / (255-(scsp->udata.data[0x18/2]&0xff)); if (time) { scsp->timerA->adjust(attotime::from_hz(time)); } } }*/ break; case 0x1a: case 0x1b: /*if(scsp->Master) { UINT32 time; scsp->TimPris[1]=1<<((scsp->udata.data[0x1A/2]>>8)&0x7); scsp->TimCnt[1]=(scsp->udata.data[0x1A/2]&0xff)<<8; if ((scsp->udata.data[0x1A/2]&0xff) != 255) { time = (44100 / scsp->TimPris[1]) / (255-(scsp->udata.data[0x1A/2]&0xff)); if (time) { scsp->timerB->adjust(attotime::from_hz(time)); } } }*/ break; case 0x1C: case 0x1D: /*if(scsp->Master) { UINT32 time; scsp->TimPris[2]=1<<((scsp->udata.data[0x1C/2]>>8)&0x7); scsp->TimCnt[2]=(scsp->udata.data[0x1C/2]&0xff)<<8; if ((scsp->udata.data[0x1C/2]&0xff) != 255) { time = (44100 / scsp->TimPris[2]) / (255-(scsp->udata.data[0x1C/2]&0xff)); if (time) { scsp->timerC->adjust(attotime::from_hz(time)); } } }*/ break; case 0x1e: // SCIEB case 0x1f: /*if(scsp->Master) { CheckPendingIRQ(scsp); if(scsp->udata.data[0x1e/2] & 0x610) popmessage("SCSP SCIEB enabled %04x, contact MAMEdev",scsp->udata.data[0x1e/2]); }*/ break; case 0x20: // SCIPD case 0x21: /*if(scsp->Master) { if(scsp->udata.data[0x1e/2] & scsp->udata.data[0x20/2] & 0x20) popmessage("SCSP SCIPD write %04x, contact MAMEdev",scsp->udata.data[0x20/2]); }*/ break; case 0x22: //SCIRE case 0x23: /*if(scsp->Master) { scsp->udata.data[0x20/2]&=~scsp->udata.data[0x22/2]; ResetInterrupts(scsp); // behavior from real hardware: if you SCIRE a timer that's expired, // it'll immediately pop up again in SCIPD. ask Sakura Taisen on the Saturn... if (scsp->TimCnt[0] == 0xffff) { scsp->udata.data[0x20/2] |= 0x40; } if (scsp->TimCnt[1] == 0xffff) { scsp->udata.data[0x20/2] |= 0x80; } if (scsp->TimCnt[2] == 0xffff) { scsp->udata.data[0x20/2] |= 0x100; } }*/ break; case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: /*if(scsp->Master) { scsp->IrqTimA=DecodeSCI(scsp,SCITMA); scsp->IrqTimBC=DecodeSCI(scsp,SCITMB); scsp->IrqMidi=DecodeSCI(scsp,SCIMID); }*/ break; case 0x2a: case 0x2b: scsp->mcieb = scsp->udata.data[0x2a/2]; /*MainCheckPendingIRQ(scsp, 0); if(scsp->mcieb & ~0x60) popmessage("SCSP MCIEB enabled %04x, contact MAMEdev",scsp->mcieb);*/ break; case 0x2c: case 0x2d: //if(scsp->udata.data[0x2c/2] & 0x20) // MainCheckPendingIRQ(scsp, 0x20); break; case 0x2e: case 0x2f: scsp->mcipd &= ~scsp->udata.data[0x2e/2]; //MainCheckPendingIRQ(scsp, 0); break; } } static void SCSP_UpdateSlotRegR(scsp_state *scsp, int slot,int reg) { } static void SCSP_UpdateRegR(scsp_state *scsp, int reg) { switch(reg&0x3f) { case 4: case 5: { unsigned short v=scsp->udata.data[0x5/2]; v&=0xff00; v|=scsp->MidiStack[scsp->MidiR]; /*scsp->Int68kCB(scsp->device, -scsp->IrqMidi); // cancel the IRQ logerror("Read %x from SCSP MIDI\n", v);*/ if(scsp->MidiR!=scsp->MidiW) { ++scsp->MidiR; scsp->MidiR&=31; } scsp->udata.data[0x5/2]=v; } break; case 8: case 9: { // MSLC | CA |SGC|EG // f e d c b a 9 8 7 6 5 4 3 2 1 0 unsigned char MSLC=(scsp->udata.data[0x8/2]>>11)&0x1f; struct _SLOT *slot=scsp->Slots + MSLC; unsigned int SGC = (slot->EG.state) & 3; unsigned int CA = (slot->cur_addr>>(SHIFT+12)) & 0xf; unsigned int EG = (0x1f - (slot->EG.volume>>(EG_SHIFT+5))) & 0x1f; /* note: according to the manual MSLC is write only, CA, SGC and EG read only. */ scsp->udata.data[0x8/2] = /*(MSLC << 11) |*/ (CA << 7) | (SGC << 5) | EG; } break; case 0x18: case 0x19: break; case 0x1a: case 0x1b: break; case 0x1c: case 0x1d: break; case 0x2a: case 0x2b: scsp->udata.data[0x2a/2] = scsp->mcieb; break; case 0x2c: case 0x2d: scsp->udata.data[0x2c/2] = scsp->mcipd; break; } } INLINE void SCSP_w16(scsp_state *scsp,unsigned int addr,unsigned short val) { addr&=0xffff; if(addr<0x400) { int slot=addr/0x20; addr&=0x1f; *((unsigned short *) (scsp->Slots[slot].udata.datab+(addr))) = val; SCSP_UpdateSlotReg(scsp,slot,addr&0x1f); } else if(addr<0x600) { if (addr < 0x430) { *((unsigned short *) (scsp->udata.datab+((addr&0x3f)))) = val; SCSP_UpdateReg(scsp, addr&0x3f); } } else if(addr<0x700) scsp->RINGBUF[(addr-0x600)/2]=val; else { //DSP if(addr<0x780) //COEF *((unsigned short *) (scsp->DSP.COEF+(addr-0x700)/2))=val; else if(addr<0x7c0) *((unsigned short *) (scsp->DSP.MADRS+(addr-0x780)/2))=val; else if(addr<0x800) // MADRS is mirrored twice *((unsigned short *) (scsp->DSP.MADRS+(addr-0x7c0)/2))=val; else if(addr<0xC00) { *((unsigned short *) (scsp->DSP.MPRO+(addr-0x800)/2))=val; if(addr==0xBF0) { SCSPDSP_Start(&scsp->DSP); } } } } INLINE unsigned short SCSP_r16(scsp_state *scsp, unsigned int addr) { unsigned short v=0; addr&=0xffff; if(addr<0x400) { int slot=addr/0x20; addr&=0x1f; SCSP_UpdateSlotRegR(scsp, slot,addr&0x1f); v=*((unsigned short *) (scsp->Slots[slot].udata.datab+(addr))); } else if(addr<0x600) { if (addr < 0x430) { SCSP_UpdateRegR(scsp, addr&0x3f); v= *((unsigned short *) (scsp->udata.datab+((addr&0x3f)))); } } else if(addr<0x700) v=scsp->RINGBUF[(addr-0x600)/2]; #if 1 // disabled by default until I get the DSP to work correctly // can be enabled using separate option else { //DSP if(addr<0x780) //COEF v= *((unsigned short *) (scsp->DSP.COEF+(addr-0x700)/2)); else if(addr<0x7c0) v= *((unsigned short *) (scsp->DSP.MADRS+(addr-0x780)/2)); else if(addr<0x800) v= *((unsigned short *) (scsp->DSP.MADRS+(addr-0x7c0)/2)); else if(addr<0xC00) v= *((unsigned short *) (scsp->DSP.MPRO+(addr-0x800)/2)); else if(addr<0xE00) { if(addr & 2) v= scsp->DSP.TEMP[(addr >> 2) & 0x7f] & 0xffff; else v= scsp->DSP.TEMP[(addr >> 2) & 0x7f] >> 16; } else if(addr<0xE80) { if(addr & 2) v= scsp->DSP.MEMS[(addr >> 2) & 0x1f] & 0xffff; else v= scsp->DSP.MEMS[(addr >> 2) & 0x1f] >> 16; } else if(addr<0xEC0) { if(addr & 2) v= scsp->DSP.MIXS[(addr >> 2) & 0xf] & 0xffff; else v= scsp->DSP.MIXS[(addr >> 2) & 0xf] >> 16; } else if(addr<0xEE0) v= *((unsigned short *) (scsp->DSP.EFREG+(addr-0xec0)/2)); else { /* Kyuutenkai reads from 0xee0/0xee2, it's tied with EXTS register(s) also used for CD-Rom Player equalizer. This port is actually an external parallel port, directly connected from the CD Block device, hence code is a bit of an hack. */ logerror("SCSP: Reading from EXTS register %08x\n",addr); //if(addr == 0xee0) // v = space.machine().device("cdda")->get_channel_volume(0); //if(addr == 0xee2) // v = space.machine().device("cdda")->get_channel_volume(1); v = 0xFFFF; } } #endif return v; } #define REVSIGN(v) ((~v)+1) INLINE INT32 SCSP_UpdateSlot(scsp_state *scsp, struct _SLOT *slot) { INT32 sample; int step=slot->step; UINT32 addr1,addr2,addr_select; // current and next sample addresses UINT32 *addr[2] = {&addr1, &addr2}; // used for linear interpolation UINT32 *slot_addr[2] = {&(slot->cur_addr), &(slot->nxt_addr)}; // if(SSCTL(slot)!=0) //no FM or noise yet return 0; if(PLFOS(slot)!=0) { step=step*PLFO_Step(&(slot->PLFO)); step>>=SHIFT; } if(PCM8B(slot)) { addr1=slot->cur_addr>>SHIFT; addr2=slot->nxt_addr>>SHIFT; } else { addr1=(slot->cur_addr>>(SHIFT-1))&0x7fffe; addr2=(slot->nxt_addr>>(SHIFT-1))&0x7fffe; } if(MDL(slot)!=0 || MDXSL(slot)!=0 || MDYSL(slot)!=0) { INT32 smp=(scsp->RINGBUF[(scsp->BUFPTR+MDXSL(slot))&63]+scsp->RINGBUF[(scsp->BUFPTR+MDYSL(slot))&63])/2; smp<<=0xA; // associate cycle with 1024 smp>>=0x1A-MDL(slot); // ex. for MDL=0xF, sample range corresponds to +/- 64 pi (32=2^5 cycles) so shift by 11 (16-5 == 0x1A-0xF) if(!PCM8B(slot)) smp<<=1; addr1+=smp; addr2+=smp; } #if 0 // --- old code --- // Since the SCSP is for Big Endian platforms (and this is optimized for Little Endian), // this code expects the data in byte order 1 0 3 2 5 4 .... if(PCM8B(slot)) //8 bit signed { INT8 *p1=(signed char *) (scsp->SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr1))&0x7FFFF)); INT8 *p2=(signed char *) (scsp->SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr2))&0x7FFFF)); //sample=(p[0])<<8; INT32 s; INT32 fpart=slot->cur_addr&((1<>SHIFT); } else //16 bit signed (endianness?) { #ifdef VGM_BIG_ENDIAN #warning "SCSP sound emulation uses Endian-unsafe 16-Bit reads!" #endif INT16 *p1=(signed short *) (scsp->SCSPRAM+((SA(slot)+addr1)&0x7FFFE)); INT16 *p2=(signed short *) (scsp->SCSPRAM+((SA(slot)+addr2)&0x7FFFE)); INT32 s; INT32 fpart=slot->cur_addr&((1<>SHIFT); } #else // --- new code --- #ifdef VGM_BIG_ENDIAN #define READ_BE16(ptr) (*(INT16*)ptr) #else #define READ_BE16(ptr) (((ptr)[0] << 8) | (ptr)[1]) #endif // I prefer the byte order 0 1 2 3 4 5 ... // also, I won't use pointers here, since they only used [0] on them anyway. if(PCM8B(slot)) //8 bit signed { INT16 p1=(INT8)scsp->SCSPRAM[(SA(slot)+addr1)&0x7FFFF]<<8; INT16 p2=(INT8)scsp->SCSPRAM[(SA(slot)+addr2)&0x7FFFF]<<8; INT32 fpart=slot->cur_addr&((1<>SHIFT); } else //16 bit signed { UINT8 *pp1 = &scsp->SCSPRAM[(SA(slot)+addr1)&0x7FFFE]; UINT8 *pp2 = &scsp->SCSPRAM[(SA(slot)+addr2)&0x7FFFE]; INT16 p1 = (INT16)READ_BE16(pp1); INT16 p2 = (INT16)READ_BE16(pp2); INT32 fpart=slot->cur_addr&((1<>SHIFT); } #endif if(SBCTL(slot)&0x1) sample ^= 0x7FFF; if(SBCTL(slot)&0x2) sample = (INT16)(sample^0x8000); if(slot->Backwards) slot->cur_addr-=step; else slot->cur_addr+=step; slot->nxt_addr=slot->cur_addr+(1<cur_addr>>SHIFT; addr2=slot->nxt_addr>>SHIFT; if(addr1>=LSA(slot) && !(slot->Backwards)) { if(LPSLNK(slot) && slot->EG.state==ATTACK) slot->EG.state = DECAY1; } for (addr_select=0;addr_select<2;addr_select++) { INT32 rem_addr; switch(LPCTL(slot)) { case 0: //no loop if(*addr[addr_select]>=LSA(slot) && *addr[addr_select]>=LEA(slot)) { //slot->active=0; SCSP_StopSlot(slot,0); } break; case 1: //normal loop if(*addr[addr_select]>=LEA(slot)) { rem_addr = *slot_addr[addr_select] - (LEA(slot)<=LSA(slot)) && !(slot->Backwards)) { rem_addr = *slot_addr[addr_select] - (LSA(slot)<Backwards=1; } else if((*addr[addr_select]Backwards) { rem_addr = (LSA(slot)<=LEA(slot)) //reached end, reverse till start { rem_addr = *slot_addr[addr_select] - (LEA(slot)<Backwards=1; } else if((*addr[addr_select]Backwards)//reached start or negative { rem_addr = (LSA(slot)<Backwards=0; } break; } } if(!SDIR(slot)) { if(ALFOS(slot)!=0) { sample=sample*ALFO_Step(&(slot->ALFO)); sample>>=SHIFT; } if(slot->EG.state==ATTACK) sample=(sample*EG_Update(slot))>>SHIFT; else sample=(sample*scsp->EG_TABLE[EG_Update(slot)>>(SHIFT-10)])>>SHIFT; } if(!STWINH(slot)) { if(!SDIR(slot)) { unsigned short Enc=((TL(slot))<<0x0)|(0x7<<0xd); *RBUFDST=(sample*scsp->LPANTABLE[Enc])>>(SHIFT+1); } else { unsigned short Enc=(0<<0x0)|(0x7<<0xd); *RBUFDST=(sample*scsp->LPANTABLE[Enc])>>(SHIFT+1); } } return sample; } INLINE void SCSP_DoMasterSamples(scsp_state *scsp, stream_sample_t **outputs, int nsamples) { stream_sample_t *bufr,*bufl; int sl, s, i; //bufr=bufferr; //bufl=bufferl; bufl = outputs[0]; bufr = outputs[1]; for(s=0;sDELAYBUF+scsp->DELAYPTR; #else RBUFDST=scsp->RINGBUF+scsp->BUFPTR; #endif if(scsp->Slots[sl].active && ! scsp->Slots[sl].Muted) { struct _SLOT *slot=scsp->Slots+sl; unsigned short Enc; signed int sample; sample=SCSP_UpdateSlot(scsp, slot); if (! BypassDSP) { Enc=((TL(slot))<<0x0)|((IMXL(slot))<<0xd); SCSPDSP_SetSample(&scsp->DSP,(sample*scsp->LPANTABLE[Enc])>>(SHIFT-2),ISEL(slot),IMXL(slot)); } Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((DISDL(slot))<<0xd); { smpl+=(sample*scsp->LPANTABLE[Enc])>>SHIFT; smpr+=(sample*scsp->RPANTABLE[Enc])>>SHIFT; } } #if FM_DELAY scsp->RINGBUF[(scsp->BUFPTR+64-(FM_DELAY-1))&63] = scsp->DELAYBUF[(scsp->DELAYPTR+FM_DELAY-(FM_DELAY-1))%FM_DELAY]; #endif ++scsp->BUFPTR; scsp->BUFPTR&=63; #if FM_DELAY ++scsp->DELAYPTR; if(scsp->DELAYPTR>FM_DELAY-1) scsp->DELAYPTR=0; #endif } if (! BypassDSP) { SCSPDSP_Step(&scsp->DSP); for(i=0;i<16;++i) { struct _SLOT *slot=scsp->Slots+i; if(EFSDL(slot)) { unsigned short Enc=((EFPAN(slot))<<0x8)|((EFSDL(slot))<<0xd); smpl+=(scsp->DSP.EFREG[i]*scsp->LPANTABLE[Enc])>>SHIFT; smpr+=(scsp->DSP.EFREG[i]*scsp->RPANTABLE[Enc])>>SHIFT; } } } //*bufl++ = ICLIP16(smpl>>2); //*bufr++ = ICLIP16(smpr>>2); *bufl++ = smpl; *bufr++ = smpr; } } /* TODO: this needs to be timer-ized */ /*static void SCSP_exec_dma(address_space *space, scsp_state *scsp) { static UINT16 tmp_dma[3]; int i; logerror("SCSP: DMA transfer START\n" "DMEA: %04x DRGA: %04x DTLG: %04x\n" "DGATE: %d DDIR: %d\n",scsp->dma.dmea,scsp->dma.drga,scsp->dma.dtlg,scsp->dma.dgate ? 1 : 0,scsp->dma.ddir ? 1 : 0); // Copy the dma values in a temp storage for resuming later // (DMA *can't* overwrite its parameters) if(!(dma.ddir)) { for(i=0;i<3;i++) tmp_dma[i] = scsp->udata.data[(0x12+(i*2))/2]; } // note: we don't use space.read_word / write_word because it can happen that SH-2 enables the DMA instead of m68k. // TODO: don't know if params auto-updates, I guess not ... if(dma.ddir) { if(dma.dgate) { popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev"); for(i=0;i < scsp->dma.dtlg;i+=2) { scsp->SCSPRAM[scsp->dma.dmea] = 0; scsp->SCSPRAM[scsp->dma.dmea+1] = 0; scsp->dma.dmea+=2; } } else { for(i=0;i < scsp->dma.dtlg;i+=2) { UINT16 tmp; tmp = SCSP_r16(scsp, space, scsp->dma.drga); scsp->SCSPRAM[scsp->dma.dmea] = tmp & 0xff; scsp->SCSPRAM[scsp->dma.dmea+1] = tmp>>8; scsp->dma.dmea+=2; scsp->dma.drga+=2; } } } else { if(dma.dgate) { popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev"); for(i=0;i < scsp->dma.dtlg;i+=2) { SCSP_w16(scsp, space, scsp->dma.drga, 0); scsp->dma.drga+=2; } } else { for(i=0;i < scsp->dma.dtlg;i+=2) { UINT16 tmp; tmp = scsp->SCSPRAM[scsp->dma.dmea]; tmp|= scsp->SCSPRAM[scsp->dma.dmea+1]<<8; SCSP_w16(scsp, space, scsp->dma.drga, tmp); scsp->dma.dmea+=2; scsp->dma.drga+=2; } } } //Resume the values if(!(dma.ddir)) { for(i=0;i<3;i++) scsp->udata.data[(0x12+(i*2))/2] = tmp_dma[i]; } // Job done scsp->udata.data[0x16/2] &= ~0x1000; // request a dma end irq (TODO: make it inside the interface) if(scsp->udata.data[0x1e/2] & 0x10) { popmessage("SCSP DMA IRQ triggered, contact MAMEdev"); device_set_input_line(space->machine().device("audiocpu"),DecodeSCI(scsp,SCIDMA),HOLD_LINE); } }*/ #ifdef UNUSED_FUNCTION int SCSP_IRQCB(void *param) { CheckPendingIRQ(param); return -1; } #endif //static STREAM_UPDATE( SCSP_Update ) void SCSP_Update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //scsp_state *scsp = (scsp_state *)param; scsp_state *scsp = &SCSPData[ChipID]; //bufferl = outputs[0]; //bufferr = outputs[1]; //length = samples; SCSP_DoMasterSamples(scsp, outputs, samples); } //static DEVICE_START( scsp ) int device_start_scsp(UINT8 ChipID, int clock) { /*const scsp_interface *intf; scsp_state *scsp = get_safe_token(device); intf = (const scsp_interface *)device->static_config();*/ scsp_state *scsp; if (ChipID >= MAX_CHIPS) return 0; scsp = &SCSPData[ChipID]; if (clock < 1000000) // if < 1 MHz, then it's the sample rate, not the clock clock *= 512; // (for backwards compatibility with old VGM logs) // init the emulation //SCSP_Init(device, scsp, intf); SCSP_Init(scsp, clock); // set up the IRQ callbacks /*{ scsp->Int68kCB = intf->irq_callback; scsp->stream = device->machine().sound().stream_alloc(*device, 0, 2, 44100, scsp, SCSP_Update); } scsp->main_irq.resolve(intf->main_irq, *device);*/ return scsp->rate; // 44100 } void device_stop_scsp(UINT8 ChipID) { scsp_state *scsp = &SCSPData[ChipID]; free(scsp->SCSPRAM); scsp->SCSPRAM = NULL; return; } void device_reset_scsp(UINT8 ChipID) { scsp_state *scsp = &SCSPData[ChipID]; int i; // make sure all the slots are off for(i=0;i<32;++i) { scsp->Slots[i].slot=i; scsp->Slots[i].active=0; scsp->Slots[i].base=NULL; scsp->Slots[i].EG.state=RELEASE; } SCSPDSP_Init(&scsp->DSP); scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; return; } /*void scsp_set_ram_base(device_t *device, void *base) { scsp_state *scsp = get_safe_token(device); if (scsp) { scsp->SCSPRAM = (unsigned char *)base; scsp->DSP.SCSPRAM = (UINT16 *)base; scsp->SCSPRAM_LENGTH = 0x80000; scsp->DSP.SCSPRAM_LENGTH = 0x80000/2; } }*/ //READ16_DEVICE_HANDLER( scsp_r ) UINT16 scsp_r(UINT8 ChipID, offs_t offset) { //scsp_state *scsp = get_safe_token(device); scsp_state *scsp = &SCSPData[ChipID]; //scsp->stream->update(); return SCSP_r16(scsp, offset*2); } //WRITE16_DEVICE_HANDLER( scsp_w ) void scsp_w(UINT8 ChipID, offs_t offset, UINT8 data) { //scsp_state *scsp = get_safe_token(device); scsp_state *scsp = &SCSPData[ChipID]; UINT16 tmp; //scsp->stream->update(); tmp = SCSP_r16(scsp, offset & 0xFFFE); //COMBINE_DATA(&tmp); if (offset & 1) tmp = (tmp & 0xFF00) | (data << 0); else tmp = (tmp & 0x00FF) | (data << 8); SCSP_w16(scsp,offset & 0xFFFE, tmp); } /*WRITE16_DEVICE_HANDLER( scsp_midi_in ) { scsp_state *scsp = get_safe_token(device); // printf("scsp_midi_in: %02x\n", data); scsp->MidiStack[scsp->MidiW++]=data; scsp->MidiW &= 31; //CheckPendingIRQ(scsp); } READ16_DEVICE_HANDLER( scsp_midi_out_r ) { scsp_state *scsp = get_safe_token(device); unsigned char val; val=scsp->MidiStack[scsp->MidiR++]; scsp->MidiR&=31; return val; }*/ /*void scsp_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { scsp_state *scsp = &SCSPData[ChipID]; if (scsp->SCSPRAM_LENGTH != ROMSize) { scsp->SCSPRAM = (unsigned char*)realloc(scsp->SCSPRAM, ROMSize); scsp->SCSPRAM_LENGTH = ROMSize; scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; memset(scsp->SCSPRAM, 0x00, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(scsp->SCSPRAM + DataStart, ROMData, DataLength); return; }*/ void scsp_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { scsp_state *scsp = &SCSPData[ChipID]; if (DataStart >= scsp->SCSPRAM_LENGTH) return; if (DataStart + DataLength > scsp->SCSPRAM_LENGTH) DataLength = scsp->SCSPRAM_LENGTH - DataStart; memcpy(scsp->SCSPRAM + DataStart, RAMData, DataLength); return; } void scsp_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { scsp_state *scsp = &SCSPData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 32; CurChn ++) scsp->Slots[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } void scsp_set_options(UINT8 Flags) { BypassDSP = (Flags & 0x01) >> 0; return; } UINT8 scsp_get_channels(UINT32* ChannelMask) { scsp_state *scsp = &SCSPData[0]; UINT8 CurChn; UINT8 UsedChns; UINT32 ChnMask; ChnMask = 0x00000000; UsedChns = 0x00; for (CurChn = 0; CurChn < 32; CurChn ++) { if (scsp->Slots[CurChn].active) { ChnMask |= (1 << CurChn); UsedChns ++; } } if (ChannelMask != NULL) *ChannelMask = ChnMask; return UsedChns; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( scsp ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(scsp_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( scsp ); break; case DEVINFO_FCT_STOP: // Nothing // break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "SCSP"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega/Yamaha custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "2.1.1"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ //DEFINE_LEGACY_SOUND_DEVICE(SCSP, scsp); ================================================ FILE: VGMPlay/chips/scsp.h ================================================ /* SCSP (YMF292-F) header */ #pragma once #ifndef __SCSP_H__ #define __SCSP_H__ //#include "devlegcy.h" /*typedef struct _scsp_interface scsp_interface; struct _scsp_interface { int roffset; // offset in the region void (*irq_callback)(device_t *device, int state); // irq callback devcb_write_line main_irq; }; void scsp_set_ram_base(device_t *device, void *base);*/ void SCSP_Update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_scsp(UINT8 ChipID, int clock); void device_stop_scsp(UINT8 ChipID); void device_reset_scsp(UINT8 ChipID); // SCSP register access /*READ16_DEVICE_HANDLER( scsp_r ); WRITE16_DEVICE_HANDLER( scsp_w );*/ void scsp_w(UINT8 ChipID, offs_t offset, UINT8 data); //UINT8 scsp_r(UINT8 ChipID, offs_t offset); // MIDI I/O access (used for comms on Model 2/3) /*WRITE16_DEVICE_HANDLER( scsp_midi_in ); READ16_DEVICE_HANDLER( scsp_midi_out_r );*/ //void scsp_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, // const UINT8* ROMData); void scsp_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void scsp_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void scsp_set_options(UINT8 Flags); /*extern UINT32* stv_scu; DECLARE_LEGACY_SOUND_DEVICE(SCSP, scsp);*/ #endif /* __SCSP_H__ */ ================================================ FILE: VGMPlay/chips/scspdsp.c ================================================ //#include "emu.h" #include // for memset #include "mamedef.h" #include "scsp.h" #include "scspdsp.h" static UINT16 PACK(INT32 val) { UINT32 temp; int sign,exponent,k; sign = (val >> 23) & 0x1; temp = (val ^ (val << 1)) & 0xFFFFFF; exponent = 0; for (k=0; k<12; k++) { if (temp & 0x800000) break; temp <<= 1; exponent += 1; } if (exponent < 12) val = (val << exponent) & 0x3FFFFF; else val <<= 11; val >>= 11; val &= 0x7FF; val |= sign << 15; val |= exponent << 11; return (UINT16)val; } static INT32 UNPACK(UINT16 val) { int sign,exponent,mantissa; INT32 uval; sign = (val >> 15) & 0x1; exponent = (val >> 11) & 0xF; mantissa = val & 0x7FF; uval = mantissa << 11; if (exponent > 11) { exponent = 11; uval |= sign << 22; } else uval |= (sign ^ 1) << 22; uval |= sign << 23; uval <<= 8; uval >>= 8; uval >>= exponent; return uval; } void SCSPDSP_Init(struct _SCSPDSP *DSP) { memset(DSP,0,sizeof(struct _SCSPDSP)); DSP->RBL=0x8000; DSP->Stopped=1; } void SCSPDSP_Step(struct _SCSPDSP *DSP) { INT32 ACC=0; //26 bit INT32 SHIFTED=0; //24 bit INT32 X=0; //24 bit INT32 Y=0; //13 bit INT32 B=0; //26 bit INT32 INPUTS=0; //24 bit INT32 MEMVAL=0; INT32 FRC_REG=0; //13 bit INT32 Y_REG=0; //24 bit UINT32 ADDR=0; UINT32 ADRS_REG=0; //13 bit int step; if(DSP->Stopped) return; memset(DSP->EFREG,0,2*16); #if 0 int dump=0; FILE *f=NULL; if(dump) f=fopen("dsp.txt","wt"); #endif for(step=0;stepLastStep;++step) { UINT16 *IPtr=DSP->MPRO+step*4; // if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0) // break; UINT32 TRA=(IPtr[0]>>8)&0x7F; UINT32 TWT=(IPtr[0]>>7)&0x01; UINT32 TWA=(IPtr[0]>>0)&0x7F; UINT32 XSEL=(IPtr[1]>>15)&0x01; UINT32 YSEL=(IPtr[1]>>13)&0x03; UINT32 IRA=(IPtr[1]>>6)&0x3F; UINT32 IWT=(IPtr[1]>>5)&0x01; UINT32 IWA=(IPtr[1]>>0)&0x1F; UINT32 TABLE=(IPtr[2]>>15)&0x01; UINT32 MWT=(IPtr[2]>>14)&0x01; UINT32 MRD=(IPtr[2]>>13)&0x01; UINT32 EWT=(IPtr[2]>>12)&0x01; UINT32 EWA=(IPtr[2]>>8)&0x0F; UINT32 ADRL=(IPtr[2]>>7)&0x01; UINT32 FRCL=(IPtr[2]>>6)&0x01; UINT32 SHIFT=(IPtr[2]>>4)&0x03; UINT32 YRL=(IPtr[2]>>3)&0x01; UINT32 NEGB=(IPtr[2]>>2)&0x01; UINT32 ZERO=(IPtr[2]>>1)&0x01; UINT32 BSEL=(IPtr[2]>>0)&0x01; UINT32 NOFL=(IPtr[3]>>15)&1; //???? UINT32 COEF=(IPtr[3]>>9)&0x3f; UINT32 MASA=(IPtr[3]>>2)&0x1f; //??? UINT32 ADREB=(IPtr[3]>>1)&0x1; UINT32 NXADR=(IPtr[3]>>0)&0x1; INT64 v; //operations are done at 24 bit precision #if 0 if(MASA) int a=1; if(NOFL) int a=1; // int dump=0; if(f) { #define DUMP(v) fprintf(f," " #v ": %04X",v); fprintf(f,"%d: ",step); DUMP(ACC); DUMP(SHIFTED); DUMP(X); DUMP(Y); DUMP(B); DUMP(INPUTS); DUMP(MEMVAL); DUMP(FRC_REG); DUMP(Y_REG); DUMP(ADDR); DUMP(ADRS_REG); fprintf(f,"\n"); } #endif //INPUTS RW // colmns97 hits this // assert(IRA<0x32); if(IRA<=0x1f) INPUTS=DSP->MEMS[IRA]; else if(IRA<=0x2F) INPUTS=DSP->MIXS[IRA-0x20]<<4; //MIXS is 20 bit else if(IRA<=0x31) INPUTS=0; else return; INPUTS<<=8; INPUTS>>=8; //if(INPUTS&0x00800000) // INPUTS|=0xFF000000; if(IWT) { DSP->MEMS[IWA]=MEMVAL; //MEMVAL was selected in previous MRD if(IRA==IWA) INPUTS=MEMVAL; } //Operand sel //B if(!ZERO) { if(BSEL) B=ACC; else { B=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; B<<=8; B>>=8; //if(B&0x00800000) // B|=0xFF000000; //Sign extend } if(NEGB) B=0-B; } else B=0; //X if(XSEL) X=INPUTS; else { X=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; X<<=8; X>>=8; //if(X&0x00800000) // X|=0xFF000000; } //Y if(YSEL==0) Y=FRC_REG; else if(YSEL==1) Y=DSP->COEF[COEF]>>3; //COEF is 16 bits else if(YSEL==2) Y=(Y_REG>>11)&0x1FFF; else if(YSEL==3) Y=(Y_REG>>4)&0x0FFF; if(YRL) Y_REG=INPUTS; //Shifter if(SHIFT==0) { SHIFTED=ACC; if(SHIFTED>0x007FFFFF) SHIFTED=0x007FFFFF; if(SHIFTED<(-0x00800000)) SHIFTED=-0x00800000; } else if(SHIFT==1) { SHIFTED=ACC*2; if(SHIFTED>0x007FFFFF) SHIFTED=0x007FFFFF; if(SHIFTED<(-0x00800000)) SHIFTED=-0x00800000; } else if(SHIFT==2) { SHIFTED=ACC*2; SHIFTED<<=8; SHIFTED>>=8; //SHIFTED&=0x00FFFFFF; //if(SHIFTED&0x00800000) // SHIFTED|=0xFF000000; } else if(SHIFT==3) { SHIFTED=ACC; SHIFTED<<=8; SHIFTED>>=8; //SHIFTED&=0x00FFFFFF; //if(SHIFTED&0x00800000) // SHIFTED|=0xFF000000; } //ACCUM Y<<=19; Y>>=19; //if(Y&0x1000) // Y|=0xFFFFF000; v=(((INT64) X*(INT64) Y)>>12); ACC=(int) v+B; if(TWT) DSP->TEMP[(TWA+DSP->DEC)&0x7F]=SHIFTED; if(FRCL) { if(SHIFT==3) FRC_REG=SHIFTED&0x0FFF; else FRC_REG=(SHIFTED>>11)&0x1FFF; } if(MRD || MWT) //if(0) { ADDR=DSP->MADRS[MASA]; if(!TABLE) ADDR+=DSP->DEC; if(ADREB) ADDR+=ADRS_REG&0x0FFF; if(NXADR) ADDR++; if(!TABLE) ADDR&=DSP->RBL-1; else ADDR&=0xFFFF; //ADDR<<=1; //ADDR+=DSP->RBP<<13; //MEMVAL=DSP->SCSPRAM[ADDR>>1]; ADDR+=DSP->RBP<<12; if (ADDR > 0x7ffff) ADDR = 0; if(MRD && (step&1)) //memory only allowed on odd? DoA inserts NOPs on even { if(NOFL) MEMVAL=DSP->SCSPRAM[ADDR]<<8; else MEMVAL=UNPACK(DSP->SCSPRAM[ADDR]); } if(MWT && (step&1)) { if(NOFL) DSP->SCSPRAM[ADDR]=SHIFTED>>8; else DSP->SCSPRAM[ADDR]=PACK(SHIFTED); } } if(ADRL) { if(SHIFT==3) ADRS_REG=(SHIFTED>>12)&0xFFF; else ADRS_REG=(INPUTS>>16); } if(EWT) DSP->EFREG[EWA]+=SHIFTED>>8; } --DSP->DEC; memset(DSP->MIXS,0,4*16); // if(f) // fclose(f); } void SCSPDSP_SetSample(struct _SCSPDSP *DSP,INT32 sample,int SEL,int MXL) { //DSP->MIXS[SEL]+=sample<<(MXL+1)/*7*/; DSP->MIXS[SEL]+=sample; // if(MXL) // int a=1; } void SCSPDSP_Start(struct _SCSPDSP *DSP) { int i; DSP->Stopped=0; for(i=127;i>=0;--i) { UINT16 *IPtr=DSP->MPRO+i*4; if(IPtr[0]!=0 || IPtr[1]!=0 || IPtr[2]!=0 || IPtr[3]!=0) break; } DSP->LastStep=i+1; } ================================================ FILE: VGMPlay/chips/scspdsp.h ================================================ #pragma once #ifndef __SCSPDSP_H__ #define __SCSPDSP_H__ //the DSP Context struct _SCSPDSP { //Config UINT16 *SCSPRAM; UINT32 SCSPRAM_LENGTH; UINT32 RBP; //Ring buf pointer UINT32 RBL; //Delay ram (Ring buffer) size in words //context INT16 COEF[64]; //16 bit signed UINT16 MADRS[32]; //offsets (in words), 16 bit UINT16 MPRO[128*4]; //128 steps 64 bit INT32 TEMP[128]; //TEMP regs,24 bit signed INT32 MEMS[32]; //MEMS regs,24 bit signed UINT32 DEC; //input INT32 MIXS[16]; //MIXS, 24 bit signed INT16 EXTS[2]; //External inputs (CDDA) 16 bit signed //output INT16 EFREG[16]; //EFREG, 16 bit signed int Stopped; int LastStep; }; void SCSPDSP_Init(struct _SCSPDSP *DSP); void SCSPDSP_SetSample(struct _SCSPDSP *DSP, INT32 sample, INT32 SEL, INT32 MXL); void SCSPDSP_Step(struct _SCSPDSP *DSP); void SCSPDSP_Start(struct _SCSPDSP *DSP); #endif /* __SCSPDSP_H__ */ ================================================ FILE: VGMPlay/chips/scsplfo.c ================================================ /* SCSP LFO handling Part of the SCSP (YMF292-F) emulator package. (not compiled directly, #included from scsp.c) By ElSemi MAME/M1 conversion and cleanup by R. Belmont */ #define LFO_SHIFT 8 struct _LFO { unsigned short phase; UINT32 phase_step; int *table; int *scale; }; #define LFIX(v) ((unsigned int) ((float) (1<phase+=LFO->phase_step; #if LFO_SHIFT!=8 LFO->phase&=(1<<(LFO_SHIFT+8))-1; #endif p=LFO->table[LFO->phase>>LFO_SHIFT]; p=LFO->scale[p+128]; return p<<(SHIFT-LFO_SHIFT); } INLINE signed int ALFO_Step(struct _LFO *LFO) { int p; LFO->phase+=LFO->phase_step; #if LFO_SHIFT!=8 LFO->phase&=(1<<(LFO_SHIFT+8))-1; #endif p=LFO->table[LFO->phase>>LFO_SHIFT]; p=LFO->scale[p]; return p<<(SHIFT-LFO_SHIFT); } static void LFO_ComputeStep(struct _LFO *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO) { float step=(float) LFOFreq[LFOF]*256.0/(float)44100; LFO->phase_step=(unsigned int) ((float) (1<table=ALFO_SAW; break; case 1: LFO->table=ALFO_SQR; break; case 2: LFO->table=ALFO_TRI; break; case 3: LFO->table=ALFO_NOI; break; } LFO->scale=ASCALES[LFOS]; } else { switch(LFOWS) { case 0: LFO->table=PLFO_SAW; break; case 1: LFO->table=PLFO_SQR; break; case 2: LFO->table=PLFO_TRI; break; case 3: LFO->table=PLFO_NOI; break; } LFO->scale=PSCALES[LFOS]; } } ================================================ FILE: VGMPlay/chips/segapcm.c ================================================ /*********************************************************/ /* SEGA 16ch 8bit PCM */ /*********************************************************/ #include "mamedef.h" #include #include // for memset #include //#include "sndintrf.h" //#include "streams.h" #include "segapcm.h" typedef struct _segapcm_state segapcm_state; struct _segapcm_state { UINT8 *ram; UINT8 low[16]; UINT32 ROMSize; UINT8 *rom; #ifdef _DEBUG UINT8 *romusage; #endif int bankshift; int bankmask; int rgnmask; sega_pcm_interface intf; UINT8 Muted[16]; //sound_stream * stream; }; #define MAX_CHIPS 0x02 static segapcm_state SPCMData[MAX_CHIPS]; #ifndef _DEBUG //UINT8 SegaPCM_NewCore = 0x00; #else //UINT8 SegaPCM_NewCore = 0x01; static void sega_pcm_fwrite_romusage(UINT8 ChipID); #endif /*INLINE segapcm_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_SEGAPCM); return (segapcm_state *)device->token; }*/ //static STREAM_UPDATE( SEGAPCM_update ) void SEGAPCM_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //segapcm_state *spcm = (segapcm_state *)param; segapcm_state *spcm = &SPCMData[ChipID]; int rgnmask = spcm->rgnmask; int ch; /* clear the buffers */ memset(outputs[0], 0, samples*sizeof(stream_sample_t)); memset(outputs[1], 0, samples*sizeof(stream_sample_t)); // reg function // ------------------------------------------------ // 0x00 ? // 0x01 ? // 0x02 volume left // 0x03 volume right // 0x04 loop address (08-15) // 0x05 loop address (16-23) // 0x06 end address // 0x07 address delta // 0x80 ? // 0x81 ? // 0x82 ? // 0x83 ? // 0x84 current address (08-15), 00-07 is internal? // 0x85 current address (16-23) // 0x86 bit 0: channel disable? // bit 1: loop disable // other bits: bank // 0x87 ? /* loop over channels */ for (ch = 0; ch < 16; ch++) { #if 0 //if (! SegaPCM_NewCore) //{ /* only process active channels */ if (!(spcm->ram[0x86+8*ch] & 1) && ! spcm->Muted[ch]) { UINT8 *base = spcm->ram+8*ch; UINT8 flags = base[0x86]; const UINT8 *rom = spcm->rom + ((flags & spcm->bankmask) << spcm->bankshift); #ifdef _DEBUG UINT8 *romusage = spcm->romusage + ((flags & spcm->bankmask) << spcm->bankshift); #endif UINT32 addr = (base[5] << 16) | (base[4] << 8) | spcm->low[ch]; UINT16 loop = (base[0x85] << 8) | base[0x84]; UINT8 end = base[6] + 1; UINT8 delta = base[7]; UINT8 voll = base[2] & 0x7F; UINT8 volr = base[3] & 0x7F; int i; /* loop over samples on this channel */ for (i = 0; i < samples; i++) { INT8 v = 0; /* handle looping if we've hit the end */ if ((addr >> 16) == end) { if (!(flags & 2)) addr = loop << 8; else { flags |= 1; break; } } /* fetch the sample */ v = rom[(addr >> 8) & rgnmask] - 0x80; #ifdef _DEBUG if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (voll || volr)) fprintf(stderr, "Access to empty ROM section! (0x%06lX)\n", ((flags & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); romusage[(addr >> 8) & rgnmask] |= 0x01; #endif /* apply panning and advance */ outputs[0][i] += v * voll; outputs[1][i] += v * volr; addr += delta; } /* store back the updated address and info */ base[0x86] = flags; base[4] = addr >> 8; base[5] = addr >> 16; spcm->low[ch] = flags & 1 ? 0 : addr; } //} //else //{ #else UINT8 *regs = spcm->ram+8*ch; /* only process active channels */ if (!(regs[0x86] & 1) && ! spcm->Muted[ch]) { const UINT8 *rom = spcm->rom + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); #ifdef _DEBUG UINT8 *romusage = spcm->romusage + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); #endif UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | spcm->low[ch]; UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8); UINT8 end = regs[6] + 1; int i; /* loop over samples on this channel */ for (i = 0; i < samples; i++) { INT8 v = 0; /* handle looping if we've hit the end */ if ((addr >> 16) == end) { if (regs[0x86] & 2) { regs[0x86] |= 1; break; } else addr = loop; } /* fetch the sample */ v = rom[(addr >> 8) & rgnmask] - 0x80; #ifdef _DEBUG if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (regs[2] || regs[3])) fprintf(stderr, "Access to empty ROM section! (0x%06lX)\n", ((regs[0x86] & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); romusage[(addr >> 8) & rgnmask] |= 0x01; #endif /* apply panning and advance */ // fixed Bitmask for volume multiplication, thanks to ctr -Valley Bell outputs[0][i] += v * (regs[2] & 0x7F); outputs[1][i] += v * (regs[3] & 0x7F); addr = (addr + regs[7]) & 0xffffff; } /* store back the updated address */ regs[0x84] = addr >> 8; regs[0x85] = addr >> 16; spcm->low[ch] = regs[0x86] & 1 ? 0 : addr; } //} #endif } } //static DEVICE_START( segapcm ) int device_start_segapcm(UINT8 ChipID, int clock, int intf_bank) { const UINT32 STD_ROM_SIZE = 0x80000; //const sega_pcm_interface *intf = (const sega_pcm_interface *)device->static_config; sega_pcm_interface *intf; int mask, rom_mask, len; //segapcm_state *spcm = get_safe_token(device); segapcm_state *spcm; if (ChipID >= MAX_CHIPS) return 0; spcm = &SPCMData[ChipID]; intf = &spcm->intf; intf->bank = intf_bank; //spcm->rom = (const UINT8 *)device->region; //spcm->ram = auto_alloc_array(device->machine, UINT8, 0x800); spcm->ROMSize = STD_ROM_SIZE; spcm->rom = malloc(STD_ROM_SIZE); #ifdef _DEBUG spcm->romusage = malloc(STD_ROM_SIZE); #endif spcm->ram = (UINT8*)malloc(0x800); #ifndef _DEBUG //memset(spcm->rom, 0xFF, STD_ROM_SIZE); // filling 0xFF would actually be more true to the hardware, // (unused ROMs have all FFs) // but 0x80 is the effective 'zero' byte memset(spcm->rom, 0x80, STD_ROM_SIZE); #else // filling with FF makes it easier to find bugs in a .wav-log memset(spcm->rom, 0xFF, STD_ROM_SIZE); memset(spcm->romusage, 0x02, STD_ROM_SIZE); #endif //memset(spcm->ram, 0xff, 0x800); // RAM Clear is done at device_reset spcm->bankshift = (UINT8)(intf->bank); mask = intf->bank >> 16; if(!mask) mask = BANK_MASK7>>16; len = STD_ROM_SIZE; spcm->rgnmask = len - 1; for(rom_mask = 1; rom_mask < len; rom_mask *= 2); rom_mask--; spcm->bankmask = mask & (rom_mask >> spcm->bankshift); //spcm->stream = stream_create(device, 0, 2, device->clock / 128, spcm, SEGAPCM_update); //state_save_register_device_item_array(device, 0, spcm->low); //state_save_register_device_item_pointer(device, 0, spcm->ram, 0x800); for (mask = 0; mask < 16; mask ++) spcm->Muted[mask] = 0x00; return clock / 128; } //static DEVICE_STOP( segapcm ) void device_stop_segapcm(UINT8 ChipID) { //segapcm_state *spcm = get_safe_token(device); segapcm_state *spcm = &SPCMData[ChipID]; free(spcm->rom); spcm->rom = NULL; #ifdef _DEBUG //sega_pcm_fwrite_romusage(ChipID); free(spcm->romusage); #endif free(spcm->ram); return; } //static DEVICE_RESET( segapcm ) void device_reset_segapcm(UINT8 ChipID) { //segapcm_state *spcm = get_safe_token(device); segapcm_state *spcm = &SPCMData[ChipID]; memset(spcm->ram, 0xFF, 0x800); return; } //WRITE8_DEVICE_HANDLER( sega_pcm_w ) void sega_pcm_w(UINT8 ChipID, offs_t offset, UINT8 data) { //segapcm_state *spcm = get_safe_token(device); segapcm_state *spcm = &SPCMData[ChipID]; //stream_update(spcm->stream); spcm->ram[offset & 0x07ff] = data; } //READ8_DEVICE_HANDLER( sega_pcm_r ) UINT8 sega_pcm_r(UINT8 ChipID, offs_t offset) { //segapcm_state *spcm = get_safe_token(device); segapcm_state *spcm = &SPCMData[ChipID]; //stream_update(spcm->stream); return spcm->ram[offset & 0x07ff]; } void sega_pcm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { segapcm_state *spcm = &SPCMData[ChipID]; if (spcm->ROMSize != ROMSize) { unsigned long int mask, rom_mask; spcm->rom = (UINT8*)realloc(spcm->rom, ROMSize); #ifdef _DEBUG spcm->romusage = (UINT8*)realloc(spcm->romusage, ROMSize); #endif spcm->ROMSize = ROMSize; memset(spcm->rom, 0x80, ROMSize); #ifdef _DEBUG memset(spcm->romusage, 0x02, ROMSize); #endif // recalculate bankmask mask = spcm->intf.bank >> 16; if (! mask) mask = BANK_MASK7 >> 16; //spcm->rgnmask = ROMSize - 1; for (rom_mask = 1; rom_mask < ROMSize; rom_mask *= 2); rom_mask --; spcm->rgnmask = rom_mask; // fix for ROMs with e.g 0x60000 bytes (stupid M1) spcm->bankmask = mask & (rom_mask >> spcm->bankshift); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(spcm->rom + DataStart, ROMData, DataLength); #ifdef _DEBUG memset(spcm->romusage + DataStart, 0x00, DataLength); #endif return; } #ifdef _DEBUG static void sega_pcm_fwrite_romusage(UINT8 ChipID) { segapcm_state *spcm = &SPCMData[ChipID]; FILE* hFile; hFile = fopen("SPCM_ROMUsage.bin", "wb"); if (hFile == NULL) return; fwrite(spcm->romusage, 0x01, spcm->ROMSize, hFile); fclose(hFile); return; } #endif void segapcm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { segapcm_state *spcm = &SPCMData[ChipID]; unsigned char CurChn; for (CurChn = 0; CurChn < 16; CurChn ++) spcm->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( segapcm ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(segapcm_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( segapcm ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: // Nothing break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "Sega PCM"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/segapcm.h ================================================ /*********************************************************/ /* SEGA 8bit PCM */ /*********************************************************/ #pragma once #define BANK_256 (11) #define BANK_512 (12) #define BANK_12M (13) #define BANK_MASK7 (0x70<<16) #define BANK_MASKF (0xf0<<16) #define BANK_MASKF8 (0xf8<<16) typedef struct _sega_pcm_interface sega_pcm_interface; struct _sega_pcm_interface { int bank; }; /*WRITE8_DEVICE_HANDLER( sega_pcm_w ); READ8_DEVICE_HANDLER( sega_pcm_r ); DEVICE_GET_INFO( segapcm ); #define SOUND_SEGAPCM DEVICE_GET_INFO_NAME( segapcm )*/ void SEGAPCM_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_segapcm(UINT8 ChipID, int clock, int intf_bank); void device_stop_segapcm(UINT8 ChipID); void device_reset_segapcm(UINT8 ChipID); void sega_pcm_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 sega_pcm_r(UINT8 ChipID, offs_t offset); void sega_pcm_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void segapcm_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/sn76489.c ================================================ /* SN76489 emulation by Maxim in 2001 and 2002 converted from my original Delphi implementation I'm a C newbie so I'm sure there are loads of stupid things in here which I'll come back to some day and redo Includes: - Super-high quality tone channel "oversampling" by calculating fractional positions on transitions - Noise output pattern reverse engineered from actual SMS output - Volume levels taken from actual SMS output 07/08/04 Charles MacDonald Modified for use with SMS Plus: - Added support for multiple PSG chips. - Added reset/config/update routines. - Added context management routines. - Removed SN76489_GetValues(). - Removed some unused variables. */ #include // malloc/free #include // for FLT_MIN #include // for memcpy #include "mamedef.h" #include "sn76489.h" #include "panning.h" #define NoiseInitialState 0x8000 /* Initial state of shift register */ #define PSG_CUTOFF 0x6 /* Value below which PSG does not output */ static const int PSGVolumeValues[16] = { /* // These values are taken from a real SMS2's output {892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, // I can't remember why 892... :P some scaling I did at some point // these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) 1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0*/ // The MAME core uses 0x2000 as maximum volume (0x1000 for bipolar output) 4096, 3254, 2584, 2053, 1631, 1295, 1029, 817, 649, 516, 410, 325, 258, 205, 163, 0 }; /*static SN76489_Context SN76489[MAX_SN76489];*/ static SN76489_Context* LastChipInit = NULL; //static unsigned short int FNumLimit; SN76489_Context* SN76489_Init( int PSGClockValue, int SamplingRate) { int i; SN76489_Context* chip = (SN76489_Context*)malloc(sizeof(SN76489_Context)); if(chip) { chip->dClock=(float)(PSGClockValue & 0x7FFFFFF)/16/SamplingRate; SN76489_SetMute(chip, MUTE_ALLON); SN76489_Config(chip, /*MUTE_ALLON,*/ FB_SEGAVDP, SRW_SEGAVDP, 1); for( i = 0; i <= 3; i++ ) centre_panning(chip->panning[i]); //SN76489_Reset(chip); if ((PSGClockValue & 0x80000000) && LastChipInit != NULL) { // Activate special NeoGeoPocket Mode LastChipInit->NgpFlags = 0x80 | 0x00; chip->NgpFlags = 0x80 | 0x01; chip->NgpChip2 = LastChipInit; LastChipInit->NgpChip2 = chip; LastChipInit = NULL; } else { chip->NgpFlags = 0x00; chip->NgpChip2 = NULL; LastChipInit = chip; } } return chip; } void SN76489_Reset(SN76489_Context* chip) { int i; chip->PSGStereo = 0xFF; for( i = 0; i <= 3; i++ ) { /* Initialise PSG state */ chip->Registers[2*i] = 1; /* tone freq=1 */ chip->Registers[2*i+1] = 0xf; /* vol=off */ chip->NoiseFreq = 0x10; /* Set counters to 0 */ chip->ToneFreqVals[i] = 0; /* Set flip-flops to 1 */ chip->ToneFreqPos[i] = 1; /* Set intermediate positions to do-not-use value */ chip->IntermediatePos[i] = FLT_MIN; /* Set panning to centre */ //centre_panning( chip->panning[i] ); } chip->LatchedRegister = 0; /* Initialise noise generator */ chip->NoiseShiftRegister = NoiseInitialState; /* Zero clock */ chip->Clock = 0; } void SN76489_Shutdown(SN76489_Context* chip) { free(chip); } void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sr_width, int boost_noise) { //chip->Mute = mute; chip->WhiteNoiseFeedback = feedback; chip->SRWidth = sr_width; } /* void SN76489_SetContext(int which, uint8 *data) { memcpy( &SN76489[which], data, sizeof(SN76489_Context) ); } void SN76489_GetContext(int which, uint8 *data) { memcpy( data, &SN76489[which], sizeof(SN76489_Context) ); } uint8 *SN76489_GetContextPtr(int which) { return (uint8 *)&SN76489[which]; } int SN76489_GetContextSize(void) { return sizeof(SN76489_Context); } */ void SN76489_Write(SN76489_Context* chip, int data) { if ( data & 0x80 ) { /* Latch/data byte %1 cc t dddd */ chip->LatchedRegister = ( data >> 4 ) & 0x07; chip->Registers[chip->LatchedRegister] = ( chip->Registers[chip->LatchedRegister] & 0x3f0 ) /* zero low 4 bits */ | ( data & 0xf ); /* and replace with data */ } else { /* Data byte %0 - dddddd */ if ( !( chip->LatchedRegister % 2 ) && ( chip->LatchedRegister < 5 ) ) /* Tone register */ chip->Registers[chip->LatchedRegister] = ( chip->Registers[chip->LatchedRegister] & 0x00f) /* zero high 6 bits */ | ( ( data & 0x3f ) << 4 ); /* and replace with data */ else /* Other register */ chip->Registers[chip->LatchedRegister]=data&0x0f; /* Replace with data */ } switch (chip->LatchedRegister) { case 0: case 2: case 4: /* Tone channels */ if ( chip->Registers[chip->LatchedRegister] == 0 ) chip->Registers[chip->LatchedRegister] = 1; /* Zero frequency changed to 1 to avoid div/0 */ break; case 6: /* Noise */ chip->NoiseShiftRegister = NoiseInitialState; /* reset shift register */ chip->NoiseFreq = 0x10 << ( chip->Registers[6] & 0x3 ); /* set noise signal generator frequency */ break; } } void SN76489_GGStereoWrite(SN76489_Context* chip, int data) { chip->PSGStereo=data; } //void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length) void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length) { int i, j; int NGPMode; SN76489_Context* chip2; SN76489_Context* chip_t; SN76489_Context* chip_n; NGPMode = (chip->NgpFlags >> 7) & 0x01; if (! NGPMode) { chip2 = NULL; chip_t = chip_n = chip; } else { chip2 = (SN76489_Context*)chip->NgpChip2; if (! (chip->NgpFlags & 0x01)) { chip_t = chip; chip_n = chip2; } else { chip_t = chip2; chip_n = chip; } } for( j = 0; j < length; j++ ) { /* Tone channels */ for ( i = 0; i <= 2; ++i ) if ( (chip_t->Mute >> i) & 1 ) { if ( chip_t->IntermediatePos[i] != FLT_MIN ) /* Intermediate position (antialiasing) */ chip->Channels[i] = (short)( PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->IntermediatePos[i] ); else /* Flat (no antialiasing needed) */ chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->ToneFreqPos[i]; } else /* Muted channel */ chip->Channels[i] = 0; /* Noise channel */ if ( (chip_t->Mute >> 3) & 1 ) { //chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip_n->NoiseShiftRegister & 0x1 ) * 2; /* double noise volume */ // Now the noise is bipolar, too. -Valley Bell chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * (( chip_n->NoiseShiftRegister & 0x1 ) * 2 - 1); // due to the way the white noise works here, it seems twice as loud as it should be if (chip->Registers[6] & 0x4 ) chip->Channels[3] >>= 1; } else chip->Channels[i] = 0; // Build stereo result into buffer buffer[0][j] = 0; buffer[1][j] = 0; if (! chip->NgpFlags) { // For all 4 channels for ( i = 0; i <= 3; ++i ) { if ( ( ( chip->PSGStereo >> i ) & 0x11 ) == 0x11 ) { // no GG stereo for this channel if ( chip->panning[i][0] == 1.0f ) { buffer[0][j] += chip->Channels[i]; // left buffer[1][j] += chip->Channels[i]; // right } else { buffer[0][j] += (INT32)( chip->panning[i][0] * chip->Channels[i] ); // left buffer[1][j] += (INT32)( chip->panning[i][1] * chip->Channels[i] ); // right } } else { // GG stereo overrides panning buffer[0][j] += ( chip->PSGStereo >> (i+4) & 0x1 ) * chip->Channels[i]; // left buffer[1][j] += ( chip->PSGStereo >> i & 0x1 ) * chip->Channels[i]; // right } } } else { if (! (chip->NgpFlags & 0x01)) { // For all 3 tone channels for (i = 0; i < 3; i ++) { buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip ->Channels[i]; // left buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip2->Channels[i]; // right } } else { // noise channel i = 3; buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip2->Channels[i]; // left buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip ->Channels[i]; // right } } /* Increment clock by 1 sample length */ chip->Clock += chip->dClock; chip->NumClocksForSample = (int)chip->Clock; /* truncate */ chip->Clock -= chip->NumClocksForSample; /* remove integer part */ /* Decrement tone channel counters */ for ( i = 0; i <= 2; ++i ) chip->ToneFreqVals[i] -= chip->NumClocksForSample; /* Noise channel: match to tone2 or decrement its counter */ if ( chip->NoiseFreq == 0x80 ) chip->ToneFreqVals[3] = chip->ToneFreqVals[2]; else chip->ToneFreqVals[3] -= chip->NumClocksForSample; /* Tone channels: */ for ( i = 0; i <= 2; ++i ) { if ( chip->ToneFreqVals[i] <= 0 ) { /* If the counter gets below 0... */ if (chip->Registers[i*2]>=PSG_CUTOFF) { /* For tone-generating values, calculate how much of the sample is + and how much is - */ /* This is optimised into an even more confusing state than it was in the first place... */ chip->IntermediatePos[i] = ( chip->NumClocksForSample - chip->Clock + 2 * chip->ToneFreqVals[i] ) * chip->ToneFreqPos[i] / ( chip->NumClocksForSample + chip->Clock ); /* Flip the flip-flop */ chip->ToneFreqPos[i] = -chip->ToneFreqPos[i]; } else { /* stuck value */ chip->ToneFreqPos[i] = 1; chip->IntermediatePos[i] = FLT_MIN; } chip->ToneFreqVals[i] += chip->Registers[i*2] * ( chip->NumClocksForSample / chip->Registers[i*2] + 1 ); } else /* signal no antialiasing needed */ chip->IntermediatePos[i] = FLT_MIN; } /* Noise channel */ if ( chip->ToneFreqVals[3] <= 0 ) { /* If the counter gets below 0... */ /* Flip the flip-flop */ chip->ToneFreqPos[3] = -chip->ToneFreqPos[3]; if (chip->NoiseFreq != 0x80) /* If not matching tone2, decrement counter */ chip->ToneFreqVals[3] += chip->NoiseFreq * ( chip->NumClocksForSample / chip->NoiseFreq + 1 ); if (chip->ToneFreqPos[3] == 1) { /* On the positive edge of the square wave (only once per cycle) */ int Feedback; if ( chip->Registers[6] & 0x4 ) { /* White noise */ /* Calculate parity of fed-back bits for feedback */ switch (chip->WhiteNoiseFeedback) { /* Do some optimised calculations for common (known) feedback values */ case 0x0003: /* SC-3000, BBC %00000011 */ case 0x0009: /* SMS, GG, MD %00001001 */ /* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */ /* since that's (one or more bits set) && (not all bits set) */ Feedback = ( ( chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) && ( (chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) ^ chip->WhiteNoiseFeedback ) ); break; default: /* Default handler for all other feedback values */ /* XOR fold bits into the final bit */ Feedback = chip->NoiseShiftRegister & chip->WhiteNoiseFeedback; Feedback ^= Feedback >> 8; Feedback ^= Feedback >> 4; Feedback ^= Feedback >> 2; Feedback ^= Feedback >> 1; Feedback &= 1; break; } } else /* Periodic noise */ Feedback=chip->NoiseShiftRegister&1; chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (chip->SRWidth-1)); } } } } /*void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r) { INT16 tl,tr; INT16 *buff[2] = { &tl, &tr }; SN76489_Update( chip, buff, 1 ); *l = tl; *r = tr; }*/ /*int SN76489_GetMute(SN76489_Context* chip) { return chip->Mute; }*/ void SN76489_SetMute(SN76489_Context* chip, int val) { chip->Mute=val; } void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3) { calc_panning( chip->panning[0], ch0 ); calc_panning( chip->panning[1], ch1 ); calc_panning( chip->panning[2], ch2 ); calc_panning( chip->panning[3], ch3 ); } ================================================ FILE: VGMPlay/chips/sn76489.h ================================================ #ifndef _SN76489_H_ #define _SN76489_H_ // all these defines are defined in mamedef.h, but GCC's #ifdef doesn't seem to know typedefs /*#ifndef INT32 #define INT32 signed long #endif #ifndef UINT16 #define UINT16 unsigned short #endif #ifndef INT16 #define INT16 signed short #endif #ifndef INT8 #define INT8 signed char #endif*/ #ifndef uint8 #define uint8 signed char #endif /*#define MAX_SN76489 4*/ /* More testing is needed to find and confirm feedback patterns for SN76489 variants and compatible chips. */ enum feedback_patterns { FB_BBCMICRO = 0x8005, /* Texas Instruments TMS SN76489N (original) from BBC Micro computer */ FB_SC3000 = 0x0006, /* Texas Instruments TMS SN76489AN (rev. A) from SC-3000H computer */ FB_SEGAVDP = 0x0009, /* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */ }; enum sr_widths { SRW_SC3000BBCMICRO = 15, SRW_SEGAVDP = 16 }; enum volume_modes { VOL_TRUNC = 0, /* Volume levels 13-15 are identical */ VOL_FULL = 1, /* Volume levels 13-15 are unique */ }; enum mute_values { MUTE_ALLOFF = 0, /* All channels muted */ MUTE_TONE1 = 1, /* Tone 1 mute control */ MUTE_TONE2 = 2, /* Tone 2 mute control */ MUTE_TONE3 = 4, /* Tone 3 mute control */ MUTE_NOISE = 8, /* Noise mute control */ MUTE_ALLON = 15, /* All channels enabled */ }; typedef struct { int Mute; // per-channel muting int BoostNoise; // double noise volume when non-zero /* Variables */ float Clock; float dClock; int PSGStereo; int NumClocksForSample; int WhiteNoiseFeedback; int SRWidth; /* PSG registers: */ int Registers[8]; /* Tone, vol x4 */ int LatchedRegister; int NoiseShiftRegister; int NoiseFreq; /* Noise channel signal generator frequency */ /* Output calculation variables */ int ToneFreqVals[4]; /* Frequency register values (counters) */ int ToneFreqPos[4]; /* Frequency channel flip-flops */ int Channels[4]; /* Value of each channel, before stereo is applied */ float IntermediatePos[4]; /* intermediate values used at boundaries between + and - (does not need double accuracy)*/ float panning[4][2]; /* fake stereo */ int NgpFlags; /* bit 7 - NGP Mode on/off, bit 0 - is 2nd NGP chip */ void* NgpChip2; } SN76489_Context; /* Function prototypes */ SN76489_Context* SN76489_Init(int PSGClockValue, int SamplingRate); void SN76489_Reset(SN76489_Context* chip); void SN76489_Shutdown(SN76489_Context* chip); void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sw_width, int boost_noise); /* void SN76489_SetContext(SN76489_Context* chip, uint8 *data); void SN76489_GetContext(SN76489_Context* chip, uint8 *data); uint8 *SN76489_GetContextPtr(int chip); int SN76489_GetContextSize(void);*/ void SN76489_Write(SN76489_Context* chip, int data); void SN76489_GGStereoWrite(SN76489_Context* chip, int data); //void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length); void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length); /* Non-standard getters and setters */ //int SN76489_GetMute(SN76489_Context* chip); void SN76489_SetMute(SN76489_Context* chip, int val); void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3); /* and a non-standard data getter */ //void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r); #endif /* _SN76489_H_ */ ================================================ FILE: VGMPlay/chips/sn76496.c ================================================ /*************************************************************************** sn76496.c by Nicola Salmoria with contributions by others Routines to emulate the: Texas Instruments SN76489, SN76489A, SN76494/SN76496 ( Also known as, or at least compatible with, the TMS9919 and SN94624.) and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis This chip is known as the Programmable Sound Generator, or PSG, and is a 4 channel sound generator, with three squarewave channels and a noise/arbitrary duty cycle channel. Noise emulation for all verified chips should be accurate: ** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E, XOR function. It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. Its output is inverted. ** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input. ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F, XOR function. It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. Its output is not inverted. ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input. ** SN76496 is identical in operation to the SN76489A, but the audio input is documented. All the TI-made PSG chips have an audio input line which is mixed with the 4 channels of output. (It is undocumented and may not function properly on the sn76489, 76489a and 76494; the sn76489a input is mentioned in datasheets for the tms5200) All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is written to the frequency register. ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps on bits C and F, output on F It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle. (whether it uses an XOR or XNOR needs to be verified, assumed XOR) (whether output is inverted or not needs to be verified, assumed to be inverted) ** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an extra register for mapping which channels go to which speaker. The register, connected to a z80 port, means: for bits 7 6 5 4 3 2 1 0 L3 L2 L1 L0 R3 R2 R1 R0 Noise is an XOR function, and audio output is negated before being output. All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written to the frequency register. ** NCR7496 (as used on the Tandy 1000) is similar to the SN76489 but with a different noise LFSR patttern: taps on bits A and E, output on E It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. (all this chip's info needs to be verified) 28/03/2005 : Sebastien Chevalier Update th SN76496Write func, according to SN76489 doc found on SMSPower. - On write with 0x80 set to 0, when LastRegister is other then TONE, the function is similar than update with 0x80 set to 1 23/04/2007 : Lord Nightmare Major update, implement all three different noise generation algorithms and a set_variant call to discern among them. 28/04/2009 : Lord Nightmare Add READY line readback; cleaned up struct a bit. Cleaned up comments. Add more TODOs. Fixed some unsaved savestate related stuff. 04/11/2009 : Lord Nightmare Changed the way that the invert works (it now selects between XOR and XNOR for the taps), and added R->OldNoise to simulate the extra 0 that is always output before the noise LFSR contents are after an LFSR reset. This fixes SN76489/A to match chips. Added SN94624. 14/11/2009 : Lord Nightmare Removed STEP mess, vastly simplifying the code. Made output bipolar rather than always above the 0 line, but disabled that code due to pending issues. 16/11/2009 : Lord Nightmare Fix screeching in regulus: When summing together four equal channels, the size of the max amplitude per channel should be 1/4 of the max range, not 1/3. Added NCR7496. 18/11/2009 : Lord Nightmare Modify Init functions to support negating the audio output. The gamegear psg does this. Change gamegear and sega psgs to use XOR rather than XNOR based on testing. Got rid of R->OldNoise and fixed taps accordingly. Added stereo support for game gear. 15/01/2010 : Lord Nightmare Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods. Note that properly emulating the noise cycle bit timing accurately may require extensive rewriting. 24/01/2010: Lord Nightmare Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively. Thanks to PlgDavid for providing samples which helped immensely here. Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than the others, as in real life. 15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid) Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here. 23/02/2011: Lord Nightmare & Enik Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the frequency register, while the others have 0x400 as before. Should fix a bug or two on sega games, particularly Vigilante on Sega Master System. Verified on SMS hardware. TODO: * Implement the TMS9919 - any difference to sn94624? * Implement the T6W28; has registers in a weird order, needs writes to be 'sanitized' first. Also is stereo, similar to game gear. * Test the NCR7496; Smspower says the whitenoise taps are A and E, but this needs verification on real hardware. * Factor out common code so that the SAA1099 can share some code. * Convert to modern device ***************************************************************************/ /* Note: I patched the core to speed the emulation up (factor 8!!) My Pentium2 233MHz was too slow for two SN76496 chips in release mode! Now a 2xSN76496 vgm takes about 45 % CPU. */ #include "mamedef.h" #ifdef _DEBUG #include #endif //#include "emu.h" //#include "streams.h" #include #include // for memset #include // for NULL #include "sn76496.h" //#define MAX_OUTPUT 0x7fff #define MAX_OUTPUT 0x8000 #define NOISEMODE (R->Register[6]&4)?1:0 typedef struct _sn76496_state sn76496_state; struct _sn76496_state { //sound_stream * Channel; INT32 VolTable[16]; /* volume table (for 4-bit to db conversion)*/ INT32 Register[8]; /* registers */ INT32 LastRegister; /* last register written */ INT32 Volume[4]; /* db volume of voice 0-2 and noise */ UINT32 RNG; /* noise generator LFSR*/ INT32 ClockDivider; /* clock divider */ INT32 CurrentClock; INT32 FeedbackMask; /* mask for feedback */ INT32 WhitenoiseTap1; /* mask for white noise tap 1 (higher one, usually bit 14) */ INT32 WhitenoiseTap2; /* mask for white noise tap 2 (lower one, usually bit 13)*/ INT32 Negate; /* output negate flag */ INT32 Stereo; /* whether we're dealing with stereo or not */ INT32 StereoMask; /* the stereo output mask */ INT32 Period[4]; /* Length of 1/2 of waveform */ INT32 Count[4]; /* Position within the waveform */ INT32 Output[4]; /* 1-bit output of each channel, pre-volume */ INT32 CyclestoREADY;/* number of cycles until the READY line goes active */ INT32 Freq0IsMax; /* flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0 */ UINT32 MuteMsk[4]; UINT8 NgpFlags; /* bit 7 - NGP Mode on/off, bit 0 - is 2nd NGP chip */ sn76496_state* NgpChip2; /* Pointer to other Chip */ }; static sn76496_state* LastChipInit = NULL; static unsigned short int FNumLimit; /*INLINE sn76496_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == SN76496 || device->type() == SN76489 || device->type() == SN76489A || device->type() == SN76494 || device->type() == SN94624 || device->type() == NCR7496 || device->type() == GAMEGEAR || device->type() == SMSIII); return (sn76496_state *)downcast(device)->token(); }*/ //READ_LINE_DEVICE_HANDLER( sn76496_ready_r ) UINT8 sn76496_ready_r(void *chip, offs_t offset) { //sn76496_state *R = get_safe_token(device); sn76496_state *R = (sn76496_state*)chip; //stream_update(R->Channel); return (R->CyclestoREADY? 0 : 1); } //WRITE8_DEVICE_HANDLER( sn76496_stereo_w ) void sn76496_stereo_w(void *chip, offs_t offset, UINT8 data) { //sn76496_state *R = get_safe_token(device); sn76496_state *R = (sn76496_state*)chip; //stream_update(R->Channel); if (R->Stereo) R->StereoMask = data; #ifdef _DEBUG else logerror("Call to stereo write with mono chip!\n"); #endif } //WRITE8_DEVICE_HANDLER( sn76496_w ) void sn76496_write_reg(void *chip, offs_t offset, UINT8 data) { //sn76496_state *R = get_safe_token(device); sn76496_state *R = (sn76496_state*)chip; int n, r, c; /* update the output buffer before changing the registers */ //stream_update(R->Channel); /* set number of cycles until READY is active; this is always one 'sample', i.e. it equals the clock divider exactly; until the clock divider is fully supported, we delay until one sample has played. The fact that this below is '2' and not '1' is because of a ?race condition? in the mess crvision driver, where after any sample is played at all, no matter what, the cycles_to_ready ends up never being not ready, unless this value is greater than 1. Once the full clock divider stuff is written, this should no longer be an issue. */ R->CyclestoREADY = 2; if (data & 0x80) { r = (data & 0x70) >> 4; R->LastRegister = r; R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); } else { r = R->LastRegister; } c = r/2; switch (r) { case 0: /* tone 0 : frequency */ case 2: /* tone 1 : frequency */ case 4: /* tone 2 : frequency */ if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); if ((R->Register[r] != 0) || (R->Freq0IsMax == 0)) R->Period[c] = R->Register[r]; else R->Period[c] = 0x400; if (r == 4) { /* update noise shift frequency */ if ((R->Register[6] & 0x03) == 0x03) R->Period[3] = 2 * R->Period[2]; } break; case 1: /* tone 0 : volume */ case 3: /* tone 1 : volume */ case 5: /* tone 2 : volume */ case 7: /* noise : volume */ R->Volume[c] = R->VolTable[data & 0x0f]; if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); // // "Every volume write resets the waveform to High level.", TmEE, 2012-11-24 on SMSPower // R->Output[c] = 1; // R->Count[c] = R->Period[c]; // disabled for now - sounds awful break; case 6: /* noise : frequency, mode */ { #ifdef _DEBUG //if ((data & 0x80) == 0) logerror("sn76489: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", R->Register[6], data); #endif if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); n = R->Register[6]; /* N/512,N/1024,N/2048,Tone #3 output */ R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3))); R->RNG = R->FeedbackMask; } break; } } //static STREAM_UPDATE( SN76496Update ) void SN76496Update(void *chip, stream_sample_t **outputs, int samples) { int i; //sn76496_state *R = (sn76496_state *)param; sn76496_state *R = (sn76496_state*)chip; sn76496_state *R2; stream_sample_t *lbuffer = outputs[0]; //stream_sample_t *rbuffer = (R->Stereo)?outputs[1]:NULL; stream_sample_t *rbuffer = outputs[1]; INT32 out = 0; INT32 out2 = 0; INT32 vol[4]; UINT8 NGPMode; INT32 ggst[2]; NGPMode = (R->NgpFlags >> 7) & 0x01; R2 = R->NgpChip2; if (! NGPMode) { // Speed Hack out = 0; for (i = 0; i < 3; i ++) { if (R->Period[i] || R->Volume[i]) { out = 1; break; } } if (R->Volume[3]) out = 1; if (! out) { memset(lbuffer, 0x00, sizeof(stream_sample_t) * samples); memset(rbuffer, 0x00, sizeof(stream_sample_t) * samples); return; } } ggst[0] = 0x01; ggst[1] = 0x01; while (samples > 0) { /* Speed Patch */ /*// clock chip once if (R->CurrentClock > 0) // not ready for new divided clock { R->CurrentClock--; } else // ready for new divided clock, make a new sample { R->CurrentClock = R->ClockDivider-1;*/ /* decrement Cycles to READY by one */ if (R->CyclestoREADY >0) R->CyclestoREADY--; // handle channels 0,1,2 for (i = 0;i < 3;i++) { R->Count[i]--; if (R->Count[i] <= 0) { R->Output[i] ^= 1; R->Count[i] = R->Period[i]; } } // handle channel 3 R->Count[3]--; if (R->Count[3] <= 0) { // if noisemode is 1, both taps are enabled // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0 if (((R->RNG & R->WhitenoiseTap1)?1:0) ^ ((((R->RNG & R->WhitenoiseTap2)?1:0))*(NOISEMODE))) { R->RNG >>= 1; R->RNG |= R->FeedbackMask; } else { R->RNG >>= 1; } R->Output[3] = R->RNG & 1; R->Count[3] = R->Period[3]; } //} /*if (R->Stereo) { out = (((R->StereoMask&0x10)&&R->Output[0])?R->Volume[0]:0) + (((R->StereoMask&0x20)&&R->Output[1])?R->Volume[1]:0) + (((R->StereoMask&0x40)&&R->Output[2])?R->Volume[2]:0) + (((R->StereoMask&0x80)&&R->Output[3])?R->Volume[3]:0); out2 = (((R->StereoMask&0x1)&&R->Output[0])?R->Volume[0]:0) + (((R->StereoMask&0x2)&&R->Output[1])?R->Volume[1]:0) + (((R->StereoMask&0x4)&&R->Output[2])?R->Volume[2]:0) + (((R->StereoMask&0x8)&&R->Output[3])?R->Volume[3]:0); } else { out = (R->Output[0]?R->Volume[0]:0) +(R->Output[1]?R->Volume[1]:0) +(R->Output[2]?R->Volume[2]:0) +(R->Output[3]?R->Volume[3]:0); }*/ // --- CUSTOM CODE START -- out = out2 = 0; if (! R->NgpFlags) { for (i = 0; i < 4; i ++) { // --- Preparation Start --- // Bipolar output vol[i] = R->Output[i] ? +1 : -1; // Disable high frequencies (> SampleRate / 2) for tone channels // Freq. 0/1 isn't disabled becaus it would also disable PCM if (i != 3) { if (R->Period[i] <= FNumLimit && R->Period[i] > 1) vol[i] = 0; } vol[i] &= R->MuteMsk[i]; // --- Preparation End --- if (R->Stereo) { ggst[0] = (R->StereoMask & (0x10 << i)) ? 0x01 : 0x00; ggst[1] = (R->StereoMask & (0x01 << i)) ? 0x01 : 0x00; } if (R->Period[i] > 1 || i == 3) { out += vol[i] * R->Volume[i] * ggst[0]; out2 += vol[i] * R->Volume[i] * ggst[1]; } else if (R->MuteMsk[i]) { // Make Bipolar Output with PCM possible //out += (2 * R->Volume[i] - R->VolTable[5]) * ggst[0]; //out2 += (2 * R->Volume[i] - R->VolTable[5]) * ggst[1]; out += R->Volume[i] * ggst[0]; out2 += R->Volume[i] * ggst[1]; } } } else { if (! (R->NgpFlags & 0x01)) { // Tone Channel 1-3 if (R->Stereo) { ggst[0] = (R->StereoMask & (0x10 << i)) ? 0x01 : 0x00; ggst[1] = (R->StereoMask & (0x01 << i)) ? 0x01 : 0x00; } for (i = 0; i < 3; i ++) { // --- Preparation Start --- // Bipolar output vol[i] = R->Output[i] ? +1 : -1; // Disable high frequencies (> SampleRate / 2) for tone channels // Freq. 0 isn't disabled becaus it would also disable PCM if (R->Period[i] <= FNumLimit && R->Period[i]) vol[i] = 0; vol[i] &= R->MuteMsk[i]; // --- Preparation End --- //out += vol[i] * R->Volume[i]; //out2 += vol[i] * R2->Volume[i]; if (R->Period[i]) { out += vol[i] * R->Volume[i] * ggst[0]; out2 += vol[i] * R2->Volume[i] * ggst[1]; } else if (R->MuteMsk[i]) { // Make Bipolar Output with PCM possible out += R->Volume[i] * ggst[0]; out2 += R2->Volume[i] * ggst[1]; } } } else { // --- Preparation Start --- // Bipolar output vol[i] = R->Output[i] ? +1 : -1; //vol[i] &= R->MuteMsk[i]; vol[i] &= R2->MuteMsk[i]; // use MuteMask from chip 0 // --- Preparation End --- // Noise Channel if (R->Stereo) { ggst[0] = (R->StereoMask & 0x80) ? 0x01 : 0x00; ggst[1] = (R->StereoMask & 0x08) ? 0x01 : 0x00; } else { ggst[0] = 0x01; ggst[1] = 0x01; } //out += vol[3] * R2->Volume[3]; //out2 += vol[3] * R->Volume[3]; out += vol[3] * R2->Volume[3] * ggst[0]; out2 += vol[3] * R->Volume[3] * ggst[1]; } } // --- CUSTOM CODE END -- if(R->Negate) { out = -out; out2 = -out2; } *(lbuffer++) = out >> 1; // Output is Bipolar //if (R->Stereo) *(rbuffer++) = out2; *(rbuffer++) = out2 >> 1; samples--; } } static void SN76496_set_gain(sn76496_state *R,int gain) { int i; double out; gain &= 0xff; /* increase max output basing on gain (0.2 dB per step) */ out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range while (gain-- > 0) out *= 1.023292992; /* = (10 ^ (0.2/20)) */ /* build volume table (2dB per step) */ for (i = 0;i < 15;i++) { /* limit volume to avoid clipping */ if (out > MAX_OUTPUT / 4) R->VolTable[i] = MAX_OUTPUT / 4; //else R->VolTable[i] = out; else R->VolTable[i] = (INT32)(out + 0.5); // I like rounding out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ } R->VolTable[15] = 0; } //static int SN76496_init(running_device *device, sn76496_state *R, int stereo) static int SN76496_init(int clock, sn76496_state *R, int stereo) { int sample_rate = clock/2; int i; //R->Channel = stream_create(device,0,(stereo?2:1),sample_rate,R,SN76496Update); for (i = 0;i < 4;i++) R->Volume[i] = 0; R->LastRegister = 0; for (i = 0;i < 8;i+=2) { R->Register[i] = 0; R->Register[i + 1] = 0x0f; /* volume = 0 */ } for (i = 0;i < 4;i++) { R->Output[i] = R->Period[i] = R->Count[i] = 0; R->MuteMsk[i] = ~0x00; } /* Default is SN76489A */ R->ClockDivider = 8; R->FeedbackMask = 0x10000; /* mask for feedback */ R->WhitenoiseTap1 = 0x04; /* mask for white noise tap 1*/ R->WhitenoiseTap2 = 0x08; /* mask for white noise tap 2*/ R->Negate = 0; /* channels are not negated */ R->Stereo = stereo; /* depends on init */ R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/ R->StereoMask = 0xFF; /* all channels enabled */ R->Freq0IsMax = 1; /* frequency set to 0 results in freq = 0x400 rather than 0 */ R->RNG = R->FeedbackMask; R->Output[3] = R->RNG & 1; R->NgpFlags = 0x00; R->NgpChip2 = NULL; //return 0; return sample_rate; } //static void generic_start(running_device *device, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider, int freq0) static int generic_start(sn76496_state *chip, int clock, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider, int freq0) { int sample_rate; //sn76496_state *chip = get_safe_token(device); //sn76496_state *chip; sn76496_state *chip2; //if (SN76496_init(device,chip,stereo) != 0) // fatalerror("Error creating SN76496 chip"); sample_rate = SN76496_init(clock & 0x7FFFFFFF, chip, stereo); if ((clock & 0x80000000) && LastChipInit != NULL) { // Activate special NeoGeoPocket Mode chip2 = LastChipInit; chip2->NgpFlags = 0x80 | 0x00; chip->NgpFlags = 0x80 | 0x01; chip->NgpChip2 = chip2; chip2->NgpChip2 = chip; LastChipInit = NULL; } else { LastChipInit = chip; } SN76496_set_gain(chip, 0); chip->FeedbackMask = feedbackmask; chip->WhitenoiseTap1 = noisetap1; chip->WhitenoiseTap2 = noisetap2; chip->Negate = negate; chip->Stereo = stereo; if (clockdivider) chip->ClockDivider = clockdivider; chip->CurrentClock = clockdivider-1; chip->Freq0IsMax = freq0; /* Speed Patch*/ sample_rate /= chip->ClockDivider; /*state_save_register_device_item_array(device, 0, chip->VolTable); state_save_register_device_item_array(device, 0, chip->Register); state_save_register_device_item(device, 0, chip->LastRegister); state_save_register_device_item_array(device, 0, chip->Volume); state_save_register_device_item(device, 0, chip->RNG); state_save_register_device_item(device, 0, chip->ClockDivider); state_save_register_device_item(device, 0, chip->CurrentClock); state_save_register_device_item(device, 0, chip->FeedbackMask); state_save_register_device_item(device, 0, chip->WhitenoiseTap1); state_save_register_device_item(device, 0, chip->WhitenoiseTap2); state_save_register_device_item(device, 0, chip->Negate); state_save_register_device_item(device, 0, chip->Stereo); state_save_register_device_item(device, 0, chip->StereoMask); state_save_register_device_item_array(device, 0, chip->Period); state_save_register_device_item_array(device, 0, chip->Count); state_save_register_device_item_array(device, 0, chip->Output); state_save_register_device_item(device, 0, chip->CyclestoREADY);*/ return sample_rate; } unsigned long int sn76496_start(void **chip, int clock, int shiftregwidth, int noisetaps, int negate, int stereo, int clockdivider, int freq0) { sn76496_state* sn_chip; int ntap[2]; int curbit; int curtap; sn_chip = (sn76496_state*)malloc(sizeof(sn76496_state)); if (sn_chip == NULL) return 0; memset(sn_chip, 0x00, sizeof(sn76496_state)); *chip = sn_chip; // extract single noise tap bits curtap = 0; for (curbit = 0; curbit < 16; curbit ++) { if (noisetaps & (1 << curbit)) { ntap[curtap] = (1 << curbit); curtap ++; if (curtap >= 2) break; } } while(curtap < 2) { ntap[curtap] = ntap[0]; curtap ++; } return generic_start(sn_chip, clock, 1 << (shiftregwidth - 1), ntap[0], ntap[1], negate, ! stereo, clockdivider ? 1 : 8, freq0); } void sn76496_shutdown(void *chip) { sn76496_state *R = (sn76496_state*)chip; free(R); return; } void sn76496_reset(void *chip) { sn76496_state *R; UINT8 i; R = (sn76496_state*)chip; for (i = 0;i < 4;i++) R->Volume[i] = 0; R->LastRegister = 0; for (i = 0;i < 8;i+=2) { R->Register[i] = 0; R->Register[i + 1] = 0x0f; /* volume = 0 */ } for (i = 0;i < 4;i++) { R->Output[i] = R->Period[i] = R->Count[i] = 0; } R->CyclestoREADY = 1; R->StereoMask = 0xFF; /* all channels enabled */ R->RNG = R->FeedbackMask; R->Output[3] = R->RNG & 1; return; } void sn76496_freq_limiter(int clock, int clockdiv, int sample_rate) { FNumLimit = (unsigned short int)((clock / (clockdiv ? 2.0 : 16.0)) / sample_rate); return; } void sn76496_set_mutemask(void *chip, UINT32 MuteMask) { sn76496_state *R = (sn76496_state*)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) R->MuteMsk[CurChn] = (MuteMask & (1 << CurChn)) ? 0 : ~0; return; } // function parameters: device, feedback destination tap, feedback source taps, // normal(false)/invert(true), mono(false)/stereo(true), clock divider factor /*static DEVICE_START( sn76489 ) { generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE); // SN76489 not verified yet. todo: verify; } static DEVICE_START( sn76489a ) { generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid) } static DEVICE_START( sn76494 ) { generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 1, TRUE); // SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider) } static DEVICE_START( sn76496 ) { generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf) } static DEVICE_START( sn94624 ) { generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 1, TRUE); // SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid } static DEVICE_START( ncr7496 ) { generic_start(device, 0x8000, 0x02, 0x20, FALSE, FALSE, 8, TRUE); // NCR7496 not verified; info from smspower wiki } static DEVICE_START( gamegear ) { generic_start(device, 0x8000, 0x01, 0x08, TRUE, TRUE, 8, FALSE); // Verified by Justin Kerk } static DEVICE_START( smsiii ) { generic_start(device, 0x8000, 0x01, 0x08, TRUE, FALSE, 8, FALSE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear }*/ /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( sn76496 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(sn76496_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76496 ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: // Nothing break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "SN76496"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "TI PSG"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.1"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEVICE_GET_INFO( sn76489 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76489 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "SN76489"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( sn76489a ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76489a ); break; case DEVINFO_STR_NAME: strcpy(info->s, "SN76489A"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( sn76494 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76494 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "SN76494"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( sn94624 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn94624 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "SN94624"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( ncr7496 ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ncr7496 ); break; case DEVINFO_STR_NAME: strcpy(info->s, "NCR7496"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( gamegear ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( gamegear ); break; case DEVINFO_STR_NAME: strcpy(info->s, "Game Gear PSG"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } } DEVICE_GET_INFO( smsiii ) { switch (state) { case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( smsiii ); break; case DEVINFO_STR_NAME: strcpy(info->s, "SMSIII PSG"); break; default: DEVICE_GET_INFO_CALL(sn76496); break; } }*/ /*DEFINE_LEGACY_SOUND_DEVICE(SN76496, sn76496); DEFINE_LEGACY_SOUND_DEVICE(SN76489, sn76489); DEFINE_LEGACY_SOUND_DEVICE(SN76489A, sn76489a); DEFINE_LEGACY_SOUND_DEVICE(SN76494, sn76494); DEFINE_LEGACY_SOUND_DEVICE(SN94624, sn94624); DEFINE_LEGACY_SOUND_DEVICE(NCR7496, ncr7496); DEFINE_LEGACY_SOUND_DEVICE(GAMEGEAR, gamegear); DEFINE_LEGACY_SOUND_DEVICE(SMSIII, smsiii);*/ ================================================ FILE: VGMPlay/chips/sn76496.h ================================================ #pragma once /*READ8_DEVICE_HANDLER( sn76496_ready_r ); WRITE8_DEVICE_HANDLER( sn76496_w ); WRITE8_DEVICE_HANDLER( sn76496_stereo_w ); DEVICE_GET_INFO( sn76496 ); DEVICE_GET_INFO( sn76489 ); DEVICE_GET_INFO( sn76489a ); DEVICE_GET_INFO( sn76494 ); DEVICE_GET_INFO( sn94624 ); DEVICE_GET_INFO( ncr7496 ); DEVICE_GET_INFO( gamegear ); DEVICE_GET_INFO( smsiii ); #define SOUND_SN76496 DEVICE_GET_INFO_NAME( sn76496 ) #define SOUND_SN76489 DEVICE_GET_INFO_NAME( sn76489 ) #define SOUND_SN76489A DEVICE_GET_INFO_NAME( sn76489a ) #define SOUND_SN76494 DEVICE_GET_INFO_NAME( sn76494 ) #define SOUND_SN94624 DEVICE_GET_INFO_NAME( sn94624 ) #define SOUND_NCR7496 DEVICE_GET_INFO_NAME( ncr7496 ) #define SOUND_GAMEGEAR DEVICE_GET_INFO_NAME( gamegear ) #define SOUND_SMSIII DEVICE_GET_INFO_NAME( smsiii )*/ UINT8 sn76496_ready_r(void *chip, offs_t offset); void sn76496_write_reg(void *chip, offs_t offset, UINT8 data); void sn76496_stereo_w(void *chip, offs_t offset, UINT8 data); void SN76496Update(void *chip, stream_sample_t **outputs, int samples); unsigned long int sn76496_start(void **chip, int clock, int shiftregwidth, int noisetaps, int negate, int stereo, int clockdivider, int freq0); void sn76496_shutdown(void *chip); void sn76496_reset(void *chip); void sn76496_freq_limiter(int clock, int clockdiv, int sample_rate); void sn76496_set_mutemask(void *chip, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/sn76496_opl.c ================================================ #include // for NULL #include "mamedef.h" #include "math.h" void OPL_RegMapper(UINT16 Reg, UINT8 Data); typedef struct _sn76496_state_opl sn76496_state_opl; struct _sn76496_state_opl { int clock; //sound_stream * Channel; //INT32 VolTable[16]; /* volume table (for 4-bit to db conversion)*/ INT32 Register[8]; /* registers */ INT32 LastRegister; /* last register written */ //INT32 Volume[4]; /* db volume of voice 0-2 and noise */ UINT8 Volume[4]; /* native volume of voice 0-2 and noise */ //UINT32 RNG; /* noise generator LFSR*/ //INT32 FeedbackMask; /* mask for feedback */ //INT32 WhitenoiseTaps; /* mask for white noise taps */ //INT32 FeedbackInvert; /* feedback invert flag (xor vs xnor) */ //INT32 Negate; /* output negate flag */ INT32 Stereo; /* whether we're dealing with stereo or not */ UINT8 StereoMask; /* the stereo output mask */ INT32 Period[4]; /* Length of 1/2 of waveform */ //INT32 Count[4]; /* Position within the waveform */ //INT32 Output[4]; /* 1-bit output of each channel, pre-volume */ //INT32 CyclestoREADY;/* number of cycles until the READY line goes active */ unsigned char NgpFlags; // bit 7 - NGP Mode on/off, bit 0 - is 2nd NGP chip sn76496_state_opl* NgpChip2; // Pointer to other Chip UINT8 LastVol[4]; UINT8 RegC0_M[4]; UINT8 RegC0_L[4]; }; #define NOISEMODE (R->Register[6]&4)?1:0 const unsigned char REG_LIST[0x0B] = {0x20, 0x40, 0x60, 0x80, 0xE0, 0x23, 0x43, 0x63, 0x83, 0xE3, 0xC0}; // writing Reg 83 Data #6 makes a smooth fading, if you break playing // actually the release time could be everything - it wouldn't change anything const unsigned char SQUARE_FM_INS_OPL[0x0B] = {0x02, 0x18, 0xFF, 0x00, 0x02, 0x01, 0x00, 0xF0, 0xF6, 0x00, 0x00}; // OPL2/OPL3 const unsigned char SQUARE_FM_INS_OPL3[0x0B] = {0x01, 0x3F, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xF0, 0xF6, 0x06, 0x01}; // OPL3 only // OPL3 has a SquareWave Waveform (OPL2 has only SineWaves) const unsigned char NOISE_FM_INS[0x0A] = {0x0F, 0x00, 0xF0, 0xFF, 0x00, 0x01, 0x00, 0xF0, 0xF6, 0x00}; const unsigned char NOISE_FM_REG_C0[0x02] = {0x0B, 0x0E}; // periodic, white // {0x01, 0x0E}; /* Volume Table (4-bit SN76496 -> 6-bit OPL Conversion) SN76496 OPL Idx Vol Idx Vol 0 100.0% 00 100.0% 01 91.7% 02 84.1% 1 79.4% 03 77.1% 04 70.7% 2 63.1% 05 64.8% 06 59.5% 07 54.5% 3 50.1% 08 50.0% 09 45.9% 0A 42.0% 4 39.8% 0B 38.6% 0C 35.4% 5 31.6% 0D 32.4% 0E 29.7% 0F 27.3% 6 25.1% 10 25.0% 11 22.9% 12 21.0% 7 20.0% 13 19.3% 14 17.7% 8 15.8% 15 16.2% 16 14.9% 17 13.6% 9 12.6% 18 12.5% 19 11.5% 1A 10.5% A 10.0% 1B 9.6% 1C 8.8% B 7.9% 1D 8.1% 1E 7.4% 1F 6.8% C 6.3% 20 6.3% 21 5.7% 22 5.3% D 5.0% 23 4.8% 24 4.4% E 4.0% 25 4.1% 26 3.7% 27 3.4% 28 3.1% 29 2.9% 2A 2.6% 2B 2.4% 2C 2.2% 2D 2.0% 2E 1.9% 2F 1.7% 30 1.6% 31 1.4% 32 1.3% 33 1.2% 34 1.1% 35 1.0% 36 0.9% 37 0.9% 38 0.8% 39 0.7% 3A 0.7% 3B 0.6% 3C 0.6% 3D 0.5% 3E 0.5% F 0.0% 3F 0.4% */ const unsigned char VOL_OPL[0x10] = {0x00, 0x03, 0x05, 0x08, 0x0B, 0x0D, 0x10, 0x13, 0x15, 0x18, 0x1B, 0x1D, 0x20, 0x23, 0x25, 0x3F}; extern unsigned char OPL_MODE; #define MAX_CHIPS 0x02 static sn76496_state_opl SN76496Data[MAX_CHIPS]; static unsigned char LastChipInit; void sn76496_stereo_opl(UINT8 ChipID, offs_t offset, UINT8 data) { sn76496_state_opl *R = &SN76496Data[ChipID]; unsigned char i; unsigned char st_data; if (! R->Stereo) return; R->StereoMask = data; for (i = 0; i < 4; i ++) { st_data = 0x00; st_data |= (R->StereoMask & (0x10 << i)) ? 0x10 : 0x00; // Left Channel st_data |= (R->StereoMask & (0x01 << i)) ? 0x20 : 0x00; // Right Channel R->RegC0_M[i] = st_data; OPL_RegMapper(REG_LIST[0x0A] | i, R->RegC0_M[i] | R->RegC0_L[i]); } return; } void sn76496_refresh_t6w28_opl(UINT8 ChipID) { sn76496_state_opl *R = &SN76496Data[ChipID]; sn76496_state_opl *R2 = R->NgpChip2; unsigned char CurChn; unsigned char Channel; for (CurChn = 0x00; CurChn < 0x08; CurChn ++) { Channel = CurChn & 0x03; if (! (CurChn & 0x04)) { // Tone Chip: Left Channel R->RegC0_M[Channel] = 0x10; OPL_RegMapper(REG_LIST[0x0A] | CurChn, R->RegC0_M[Channel] | R->RegC0_L[Channel]); } else { // Noise Chip: Right Channel R2->RegC0_M[Channel] = 0x20; OPL_RegMapper(REG_LIST[0x0A] | CurChn, R2->RegC0_M[Channel] | R2->RegC0_L[Channel]); } } return; } static void SendVolume(sn76496_state_opl* R, UINT8 Channel) { unsigned char ChnFnl; unsigned char ChnOp; unsigned char OPLVol; if (Channel >= 0x04) Channel &= 0x03; if (R->Volume[Channel] == R->LastVol[Channel]) return; else R->LastVol[Channel] = R->Volume[Channel]; ChnFnl = ((R->NgpFlags & 0x01) << 2) | Channel; ChnOp = (ChnFnl / 0x03) * 0x08 + (ChnFnl % 0x03); OPLVol = VOL_OPL[R->Volume[Channel]]; if (Channel == 0x03) { if (R->RegC0_L[Channel] & 0x01) OPL_RegMapper(0x40 | (ChnOp + 0x00), 0x00 | OPLVol); } OPL_RegMapper(0x40 | (ChnOp + 0x03), 0x00 | OPLVol); return; } static void SendFrequency(sn76496_state_opl* R, UINT8 Channel) { const double OPL_CHIP_RATE = 3579545.0 / 72.0; double FreqVal; signed short int FNum; signed char BlockVal; sn76496_state_opl* R2; unsigned char ChnB; Channel &= 0x03; if (R->NgpFlags & 0x80) { if ((R->NgpFlags & 0x01) ^ (Channel == 0x03)) return; R2 = R->NgpChip2; ChnB = Channel | 0x04; } if (R->Period[Channel]) FreqVal = (R->clock / 32.0) / R->Period[Channel]; else FreqVal = 0.0; if (Channel == 0x03) { R->RegC0_L[Channel] = NOISE_FM_REG_C0[(R->Register[6] >> 2) & 0x01]; OPL_RegMapper(REG_LIST[0x0A] | Channel, R->RegC0_M[Channel] | R->RegC0_L[Channel]); if (R->NgpFlags & 0x80) { OPL_RegMapper(REG_LIST[0x0A] | ChnB, R2->RegC0_M[Channel] | R->RegC0_L[Channel]); FNum = (ChnB / 0x03) * 0x08 + (ChnB % 0x03); } if (! (R->RegC0_L[Channel] & 0x01)) { //FreqVal = 220.0; FreqVal /= 8.0; // too high frequencies sound weird // reset Modulator-Volume OPL_RegMapper(0x40 | 0x08, NOISE_FM_INS[0x01]); if (R->NgpFlags & 0x80) OPL_RegMapper(0x40 | FNum, NOISE_FM_INS[0x01]); } else { // set Modulator-Volume (because of Additive Synthesis) OPL_RegMapper(0x40 | 0x08, 0x00 | VOL_OPL[R->Volume[Channel]]); if (R->NgpFlags & 0x80) OPL_RegMapper(0x40 | FNum, 0x00 | VOL_OPL[R2->Volume[Channel]]); } } else if (FreqVal > OPL_CHIP_RATE) { FreqVal = 0.0; } BlockVal = (unsigned char)(0x05 + (log(FreqVal) - log(440.0)) / log(2.0)); if (BlockVal < 0x00) { BlockVal = 0x00; FNum = 0x000; } else if (BlockVal > 0x07) { BlockVal = 0x07; FNum = 0x3FF; } else { FNum = (unsigned short int)(FreqVal * (1 << (20 - BlockVal)) / OPL_CHIP_RATE + 0.5); if (FNum < 0x000) FNum = 0x000; else if (FNum > 0x3FF) FNum = 0x3FF; } if (Channel == 0x03) // stop Noise Note before restarting OPL_RegMapper(0xB0 | Channel, 0x00); OPL_RegMapper(0xA0 | Channel, FNum & 0x00FF); OPL_RegMapper(0xB0 | Channel, 0x20 | (BlockVal << 2) |( (FNum & 0x0300) >> 8)); if (R->NgpFlags & 0x80) { // Send 2nd Channel Set if (Channel == 0x03) OPL_RegMapper(0xB0 | ChnB, 0x00); OPL_RegMapper(0xA0 | ChnB, FNum & 0x00FF); OPL_RegMapper(0xB0 | ChnB, 0x20 | (BlockVal << 2) |( (FNum & 0x0300) >> 8)); } return; } void sn76496_write_opl(UINT8 ChipID, offs_t offset, UINT8 data) { sn76496_state_opl *R = &SN76496Data[ChipID]; unsigned char n, r, c; if (data & 0x80) { r = (data & 0x70) >> 4; R->LastRegister = r; R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); } else { r = R->LastRegister; } c = r/2; switch (r) { case 0: /* tone 0 : frequency */ case 2: /* tone 1 : frequency */ case 4: /* tone 2 : frequency */ if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); R->Period[c] = R->Register[r]; if (data & 0x80) break; // only send after receiving a Data Command SendFrequency(R, c); if (r == 4) { /* update noise shift frequency */ if ((R->Register[6] & 0x03) == 0x03) { R->Period[3] = 2 * R->Period[2]; SendFrequency(R, 3); } } break; case 1: /* tone 0 : volume */ case 3: /* tone 1 : volume */ case 5: /* tone 2 : volume */ case 7: /* noise : volume */ //R->Volume[c] = R->VolTable[data & 0x0f]; R->Volume[c] = data & 0x0F; SendVolume(R, c); if ((data & 0x80) == 0) { R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); // Register is set, but frequency is NOT refreshed } break; case 6: /* noise : frequency, mode */ if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); n = R->Register[6]; /* N/512,N/1024,N/2048,Tone #3 output */ R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3))); SendFrequency(R, 3); /* Reset noise shifter */ //R->RNG = R->FeedbackMask; //R->Output[3] = R->RNG & 1; break; } } static void SN76496_init(int clock, sn76496_state_opl *R, int stereo) { const unsigned char* SQUARE_FM_INS; unsigned char i; unsigned char reg; R->NgpFlags = 0x00; R->NgpChip2 = NULL; R->clock = clock; //for (i = 0;i < 4;i++) // R->Volume[i] = 0; R->LastRegister = 0; for (i = 0;i < 8;i+=2) { R->Register[i] = 0; R->Register[i + 1] = 0x0f; /* volume = 0 */ } if (OPL_MODE == 0x03) SQUARE_FM_INS = SQUARE_FM_INS_OPL3; else SQUARE_FM_INS = SQUARE_FM_INS_OPL; // Init Instruments for (i = 0;i < 3;i++) { for (reg = 0x00; reg < 0x0A; reg ++) { OPL_RegMapper(REG_LIST[reg] + i, SQUARE_FM_INS[reg]); } R->RegC0_L[i] = SQUARE_FM_INS[0x0A]; R->RegC0_M[i] = 0x30; OPL_RegMapper(REG_LIST[0x0A] + i, R->RegC0_M[i] | R->RegC0_L[i]); } for (reg = 0x00; reg < 0x0A; reg ++) { OPL_RegMapper(REG_LIST[reg] + 0x08, NOISE_FM_INS[reg]); } R->RegC0_L[3] = NOISE_FM_REG_C0[0x00]; R->RegC0_M[3] = 0x30; OPL_RegMapper(REG_LIST[0x0A] + 0x03, R->RegC0_M[i] | R->RegC0_L[i]); for (i = 0;i < 4;i++) { R->LastVol[i] = 0xFF; R->Volume[i] = R->Register[i * 2 + 1]; R->Period[i] = 0; SendVolume(R, i); OPL_RegMapper(0xB0 | i, 0x00); SendFrequency(R, i); } /* Default is SN76489 non-A */ //R->FeedbackMask = 0x4000; /* mask for feedback */ //R->WhitenoiseTaps = 0x03; /* mask for white noise taps */ //R->FeedbackInvert = 0; /* feedback invert flag */ //R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/ //R->Negate = 0; /* channels are not negated */ R->Stereo = stereo; /* depends on init */ R->StereoMask = 0xFF; /* all channels enabled */ //R->RNG = R->FeedbackMask; //R->Output[3] = R->RNG & 1; return; } void start_sn76496_opl(UINT8 ChipID, int clock, int stereo) { sn76496_state_opl *chip; sn76496_state_opl *chip2; if (ChipID >= MAX_CHIPS) return; chip = &SN76496Data[ChipID]; //if (SN76496_init(device,chip,stereo) != 0) // fatalerror("Error creating SN76496 chip"); SN76496_init(clock & 0x7FFFFFFF, chip, stereo); if (clock & 0x80000000) { // Activate special NeoGeoPocket Mode chip2 = &SN76496Data[LastChipInit]; chip2->NgpFlags = 0x80 | 0x00; chip->NgpFlags = 0x80 | 0x01; chip->NgpChip2 = chip2; chip2->NgpChip2 = chip; } //chip->FeedbackMask = feedbackmask; //chip->WhitenoiseTaps = noisetaps; //chip->FeedbackInvert = feedbackinvert; //chip->Negate = negate; chip->Stereo = ! stereo; sn76496_stereo_opl(ChipID, 0x00, chip->StereoMask); return; } ================================================ FILE: VGMPlay/chips/sn764intf.c ================================================ /**************************************************************** MAME / MESS functions ****************************************************************/ #include // for NULL #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #include "sn76496.h" #include "sn76489.h" #include "sn764intf.h" #define EC_MAME 0x00 // SN76496 core from MAME #ifdef ENABLE_ALL_CORES #define EC_MAXIM 0x01 // SN76489 core by Maxim (from in_vgm) #endif /* for stream system */ typedef struct _sn764xx_state sn764xx_state; struct _sn764xx_state { void *chip; }; static UINT8 EMU_CORE = 0x00; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static sn764xx_state SN764xxData[MAX_CHIPS]; void sn764xx_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: SN76496Update(info->chip, outputs, samples); break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: SN76489_Update((SN76489_Context*)info->chip, outputs, samples); break; #endif } } int device_start_sn764xx(UINT8 ChipID, int clock, int shiftregwidth, int noisetaps, int negate, int stereo, int clockdivider, int freq0) { sn764xx_state *info; int rate = 0; if (ChipID >= MAX_CHIPS) return 0; info = &SN764xxData[ChipID]; /* emulator create */ switch(EMU_CORE) { case EC_MAME: rate = sn76496_start(&info->chip, clock, shiftregwidth, noisetaps, negate, stereo, clockdivider, freq0); sn76496_freq_limiter(clock & 0x3FFFFFFF, clockdivider, SampleRate); break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: rate = SampleRate; info->chip = SN76489_Init(clock, rate); if (info->chip == NULL) return 0; SN76489_Config((SN76489_Context*)info->chip, noisetaps, shiftregwidth, 0); break; #endif } return rate; } void device_stop_sn764xx(UINT8 ChipID) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: sn76496_shutdown(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: SN76489_Shutdown((SN76489_Context*)info->chip); break; #endif } } void device_reset_sn764xx(UINT8 ChipID) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: sn76496_reset(info->chip); break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: SN76489_Reset((SN76489_Context*)info->chip); break; #endif } } void sn764xx_w(UINT8 ChipID, offs_t offset, UINT8 data) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: switch(offset) { case 0x00: sn76496_write_reg(info->chip, offset & 1, data); break; case 0x01: sn76496_stereo_w(info->chip, offset, data); break; } break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: switch(offset) { case 0x00: SN76489_Write((SN76489_Context*)info->chip, data); break; case 0x01: SN76489_GGStereoWrite((SN76489_Context*)info->chip, data); break; } break; #endif } } void sn764xx_set_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else EMU_CORE = EC_MAME; #endif return; } void sn764xx_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: sn76496_set_mutemask(info->chip, MuteMask); break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: SN76489_SetMute(info->chip, ~MuteMask & 0x0F); break; #endif } return; } void sn764xx_set_panning(UINT8 ChipID, INT16* PanVals) { sn764xx_state *info = &SN764xxData[ChipID]; switch(EMU_CORE) { case EC_MAME: break; #ifdef ENABLE_ALL_CORES case EC_MAXIM: SN76489_SetPanning(info->chip, PanVals[0x00], PanVals[0x01], PanVals[0x02], PanVals[0x03]); break; #endif } return; } ================================================ FILE: VGMPlay/chips/sn764intf.h ================================================ #pragma once /*WRITE8_DEVICE_HANDLER( ym2413_w ); WRITE8_DEVICE_HANDLER( ym2413_register_port_w ); WRITE8_DEVICE_HANDLER( ym2413_data_port_w ); DEVICE_GET_INFO( ym2413 ); #define SOUND_YM2413 DEVICE_GET_INFO_NAME( ym2413 )*/ void sn764xx_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_sn764xx(UINT8 ChipID, int clock, int shiftregwidth, int noisetaps, int negate, int stereo, int clockdivider, int freq0); void device_stop_sn764xx(UINT8 ChipID); void device_reset_sn764xx(UINT8 ChipID); void sn764xx_w(UINT8 ChipID, offs_t offset, UINT8 data); void sn764xx_set_emu_core(UINT8 Emulator); void sn764xx_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); void sn764xx_set_panning(UINT8 ChipID, INT16* PanVals); ================================================ FILE: VGMPlay/chips/upd7759.c ================================================ /************************************************************ NEC UPD7759 ADPCM Speech Processor by: Juergen Buchmueller, Mike Balfour, Howie Cohen, Olivier Galibert, and Aaron Giles ************************************************************* Description: The UPD7759 is a speech processing LSI that utilizes ADPCM to produce speech or other sampled sounds. It can directly address up to 1Mbit (128k) of external data ROM, or the host CPU can control the speech data transfer. The UPD7759 is usually hooked up to a 640 kHz clock and has one 8-bit input port, a start pin, a busy pin, and a clock output. The chip is composed of 3 parts: - a clock divider - a rom-reading engine - an adpcm engine - a 4-to-9 bit adpcm converter The clock divider takes the base 640KHz clock and divides it first by a fixed divisor of 4 and then by a value between 9 and 32. The result gives a clock between 5KHz and 17.78KHz. It's probably possible, but not recommended and certainly out-of-spec, to push the chip harder by reducing the divider. The rom-reading engine reads one byte every two divided clock cycles. The factor two comes from the fact that a byte has two nibbles, i.e. two samples. The apdcm engine takes bytes and interprets them as commands: 00000000 sample end 00dddddd silence 01ffffff send the 256 following nibbles to the converter 10ffffff nnnnnnnn send the n+1 following nibbles to the converter 11---rrr --ffffff nnnnnnnn send the n+1 following nibbles to the converter, and repeat r+1 times "ffffff" is sent to the clock divider to be the base clock for the adpcm converter, i.e., it's the sampling rate. If the number of nibbles to send is odd the last nibble is ignored. The commands are always 8-bit aligned. "dddddd" is the duration of the silence. The base speed is unknown, 1ms sounds reasonably. It does not seem linked to the adpcm clock speed because there often is a silence before any 01 or 10 command. The adpcm converter converts nibbles into 9-bit DAC values. It has an internal state of 4 bits that's used in conjunction with the nibble to lookup which of the 256 possible steps is used. Then the state is changed according to the nibble value. Essentially, the higher the state, the bigger the steps are, and using big steps increase the state. Conversely, using small steps reduces the state. This allows the engine to be a little more adaptative than a classical ADPCM algorithm. The UPD7759 can run in two modes, master (also known as standalone) and slave. The mode is selected through the "md" pin. No known game changes modes on the fly, and it's unsure if that's even possible to do. Master mode: The output of the rom reader is directly connected to the adpcm converter. The controlling cpu only sends a sample number and the 7759 plays it. The sample rom has a header at the beginning of the form nn 5a a5 69 55 where nn is the number of the last sample. This is then followed by a vector of 2-bytes msb-first values, one per sample. Multiplying them by two gives the sample start offset in the rom. A 0x00 marks the end of each sample. It seems that the UPD7759 reads at least part of the rom header at startup. Games doing rom banking are careful to reset the chip after each change. Slave mode: The rom reader is completely disconnected. The input port is connected directly to the adpcm engine. The first write to the input port activates the engine (the value itself is ignored). The engine activates the clock output and waits for commands. The clock speed is unknown, but its probably a divider of 640KHz. We use 40KHz here because 80KHz crashes altbeast. The chip probably has an internal fifo to the converter and suspends the clock when the fifo is full. The first command is always 0xFF. A second 0xFF marks the end of the sample and the engine stops. OTOH, there is a 0x00 at the end too. Go figure. *************************************************************/ //#include "emu.h" //#include "streams.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include "mamedef.h" #include "upd7759.h" #define DEBUG_STATES (0) //#define DEBUG_METHOD mame_printf_debug #define DEBUG_METHOD logerror /************************************************************ Constants *************************************************************/ /* step value fractional bits */ #define FRAC_BITS 20 #define FRAC_ONE (1 << FRAC_BITS) #define FRAC_MASK (FRAC_ONE - 1) /* chip states */ enum { STATE_IDLE, STATE_DROP_DRQ, STATE_START, STATE_FIRST_REQ, STATE_LAST_SAMPLE, STATE_DUMMY1, STATE_ADDR_MSB, STATE_ADDR_LSB, STATE_DUMMY2, STATE_BLOCK_HEADER, STATE_NIBBLE_COUNT, STATE_NIBBLE_MSN, STATE_NIBBLE_LSN }; /************************************************************ Type definitions *************************************************************/ typedef struct _upd7759_state upd7759_state; struct _upd7759_state { //running_device *device; //sound_stream *channel; /* stream channel for playback */ /* internal clock to output sample rate mapping */ UINT32 pos; /* current output sample position */ UINT32 step; /* step value per output sample */ //attotime clock_period; /* clock period */ //emu_timer *timer; /* timer */ /* I/O lines */ UINT8 fifo_in; /* last data written to the sound chip */ UINT8 reset; /* current state of the RESET line */ UINT8 start; /* current state of the START line */ UINT8 drq; /* current state of the DRQ line */ //void (*drqcallback)(running_device *device, int param); /* drq callback */ //void (*drqcallback)(int param); /* drq callback */ /* internal state machine */ INT8 state; /* current overall chip state */ INT32 clocks_left; /* number of clocks left in this state */ UINT16 nibbles_left; /* number of ADPCM nibbles left to process */ UINT8 repeat_count; /* number of repeats remaining in current repeat block */ INT8 post_drq_state; /* state we will be in after the DRQ line is dropped */ INT32 post_drq_clocks; /* clocks that will be left after the DRQ line is dropped */ UINT8 req_sample; /* requested sample number */ UINT8 last_sample; /* last sample number available */ UINT8 block_header; /* header byte */ UINT8 sample_rate; /* number of UPD clocks per ADPCM nibble */ UINT8 first_valid_header; /* did we get our first valid header yet? */ UINT32 offset; /* current ROM offset */ UINT32 repeat_offset; /* current ROM repeat offset */ /* ADPCM processing */ INT8 adpcm_state; /* ADPCM state index */ UINT8 adpcm_data; /* current byte of ADPCM data */ INT16 sample; /* current sample value */ /* ROM access */ UINT32 romsize; UINT8 * rom; /* pointer to ROM data or NULL for slave mode */ UINT8 * rombase; /* pointer to ROM data or NULL for slave mode */ UINT32 romoffset; /* ROM offset to make save/restore easier */ UINT8 ChipMode; // 0 - Master, 1 - Slave // Valley Bell: Added a FIFO buffer based on Sega Pico. UINT8 data_buf[0x40]; UINT8 dbuf_pos_read; UINT8 dbuf_pos_write; }; /************************************************************ Local variables *************************************************************/ static const int upd7759_step[16][16] = { { 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10 }, { 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13 }, { 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15 }, { 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19 }, { 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23 }, { 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29 }, { 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33 }, { 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43 }, { 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53 }, { 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64 }, { 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76 }, { 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96 }, { 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117 }, { 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143 }, { 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175 }, { 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 }, }; static const int upd7759_state_table[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 }; /*INLINE upd7759_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == UPD7759); return (upd7759_state *)downcast(device)->token(); }*/ #define MAX_CHIPS 0x02 static upd7759_state UPD7759Data[MAX_CHIPS]; /************************************************************ ADPCM sample updater *************************************************************/ INLINE void update_adpcm(upd7759_state *chip, int data) { /* update the sample and the state */ chip->sample += upd7759_step[chip->adpcm_state][data]; chip->adpcm_state += upd7759_state_table[data]; /* clamp the state to 0..15 */ if (chip->adpcm_state < 0) chip->adpcm_state = 0; else if (chip->adpcm_state > 15) chip->adpcm_state = 15; } /************************************************************ Master chip state machine *************************************************************/ static void get_fifo_data(upd7759_state *chip) { if (chip->dbuf_pos_read == chip->dbuf_pos_write) { logerror("Warning: UPD7759 reading empty FIFO!\n"); return; } chip->fifo_in = chip->data_buf[chip->dbuf_pos_read]; chip->dbuf_pos_read ++; chip->dbuf_pos_read &= 0x3F; return; } static void advance_state(upd7759_state *chip) { switch (chip->state) { /* Idle state: we stick around here while there's nothing to do */ case STATE_IDLE: chip->clocks_left = 4; break; /* drop DRQ state: update to the intended state */ case STATE_DROP_DRQ: chip->drq = 0; if (chip->ChipMode) get_fifo_data(chip); // Slave Mode only chip->clocks_left = chip->post_drq_clocks; chip->state = chip->post_drq_state; break; /* Start state: we begin here as soon as a sample is triggered */ case STATE_START: chip->req_sample = chip->rom ? chip->fifo_in : 0x10; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: req_sample = %02X\n", chip->req_sample); /* 35+ cycles after we get here, the /DRQ goes low * (first byte (number of samples in ROM) should be sent in response) * * (35 is the minimum number of cycles I found during heavy tests. * Depending on the state the chip was in just before the /MD was set to 0 (reset, standby * or just-finished-playing-previous-sample) this number can range from 35 up to ~24000). * It also varies slightly from test to test, but not much - a few cycles at most.) */ chip->clocks_left = 70; /* 35 - breaks cotton */ chip->state = STATE_FIRST_REQ; break; /* First request state: issue a request for the first byte */ /* The expected response will be the index of the last sample */ case STATE_FIRST_REQ: if (DEBUG_STATES) DEBUG_METHOD("UPD7759: first data request\n"); chip->drq = 1; /* 44 cycles later, we will latch this value and request another byte */ chip->clocks_left = 44; chip->state = STATE_LAST_SAMPLE; break; /* Last sample state: latch the last sample value and issue a request for the second byte */ /* The second byte read will be just a dummy */ case STATE_LAST_SAMPLE: chip->last_sample = chip->rom ? chip->rom[0] : chip->fifo_in; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: last_sample = %02X, requesting dummy 1\n", chip->last_sample); chip->drq = 1; /* 28 cycles later, we will latch this value and request another byte */ chip->clocks_left = 28; /* 28 - breaks cotton */ chip->state = (chip->req_sample > chip->last_sample) ? STATE_IDLE : STATE_DUMMY1; break; /* First dummy state: ignore any data here and issue a request for the third byte */ /* The expected response will be the MSB of the sample address */ case STATE_DUMMY1: if (DEBUG_STATES) DEBUG_METHOD("UPD7759: dummy1, requesting offset_hi\n"); chip->drq = 1; /* 32 cycles later, we will latch this value and request another byte */ chip->clocks_left = 32; chip->state = STATE_ADDR_MSB; break; /* Address MSB state: latch the MSB of the sample address and issue a request for the fourth byte */ /* The expected response will be the LSB of the sample address */ case STATE_ADDR_MSB: chip->offset = (chip->rom ? chip->rom[chip->req_sample * 2 + 5] : chip->fifo_in) << 9; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: offset_hi = %02X, requesting offset_lo\n", chip->offset >> 9); chip->drq = 1; /* 44 cycles later, we will latch this value and request another byte */ chip->clocks_left = 44; chip->state = STATE_ADDR_LSB; break; /* Address LSB state: latch the LSB of the sample address and issue a request for the fifth byte */ /* The expected response will be just a dummy */ case STATE_ADDR_LSB: chip->offset |= (chip->rom ? chip->rom[chip->req_sample * 2 + 6] : chip->fifo_in) << 1; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: offset_lo = %02X, requesting dummy 2\n", (chip->offset >> 1) & 0xff); chip->drq = 1; /* 36 cycles later, we will latch this value and request another byte */ chip->clocks_left = 36; chip->state = STATE_DUMMY2; break; /* Second dummy state: ignore any data here and issue a request for the the sixth byte */ /* The expected response will be the first block header */ case STATE_DUMMY2: chip->offset++; chip->first_valid_header = 0; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: dummy2, requesting block header\n"); chip->drq = 1; /* 36?? cycles later, we will latch this value and request another byte */ chip->clocks_left = 36; chip->state = STATE_BLOCK_HEADER; break; /* Block header state: latch the header and issue a request for the first byte afterwards */ case STATE_BLOCK_HEADER: /* if we're in a repeat loop, reset the offset to the repeat point and decrement the count */ if (chip->repeat_count) { chip->repeat_count--; chip->offset = chip->repeat_offset; } chip->block_header = chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: header (@%05X) = %02X, requesting next byte\n", chip->offset, chip->block_header); chip->drq = 1; /* our next step depends on the top two bits */ switch (chip->block_header & 0xc0) { case 0x00: /* silence */ chip->clocks_left = 1024 * ((chip->block_header & 0x3f) + 1); chip->state = (chip->block_header == 0 && chip->first_valid_header) ? STATE_IDLE : STATE_BLOCK_HEADER; chip->sample = 0; chip->adpcm_state = 0; break; case 0x40: /* 256 nibbles */ chip->sample_rate = (chip->block_header & 0x3f) + 1; chip->nibbles_left = 256; chip->clocks_left = 36; /* just a guess */ chip->state = STATE_NIBBLE_MSN; break; case 0x80: /* n nibbles */ chip->sample_rate = (chip->block_header & 0x3f) + 1; chip->clocks_left = 36; /* just a guess */ chip->state = STATE_NIBBLE_COUNT; break; case 0xc0: /* repeat loop */ chip->repeat_count = (chip->block_header & 7) + 1; chip->repeat_offset = chip->offset; chip->clocks_left = 36; /* just a guess */ chip->state = STATE_BLOCK_HEADER; break; } /* set a flag when we get the first non-zero header */ if (chip->block_header != 0) chip->first_valid_header = 1; break; /* Nibble count state: latch the number of nibbles to play and request another byte */ /* The expected response will be the first data byte */ case STATE_NIBBLE_COUNT: chip->nibbles_left = (chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in) + 1; if (DEBUG_STATES) DEBUG_METHOD("UPD7759: nibble_count = %u, requesting next byte\n", (unsigned)chip->nibbles_left); chip->drq = 1; /* 36?? cycles later, we will latch this value and request another byte */ chip->clocks_left = 36; /* just a guess */ chip->state = STATE_NIBBLE_MSN; break; /* MSN state: latch the data for this pair of samples and request another byte */ /* The expected response will be the next sample data or another header */ case STATE_NIBBLE_MSN: chip->adpcm_data = chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in; update_adpcm(chip, chip->adpcm_data >> 4); chip->drq = 1; /* we stay in this state until the time for this sample is complete */ chip->clocks_left = chip->sample_rate * 4; if (--chip->nibbles_left == 0) chip->state = STATE_BLOCK_HEADER; else chip->state = STATE_NIBBLE_LSN; break; /* LSN state: process the lower nibble */ case STATE_NIBBLE_LSN: update_adpcm(chip, chip->adpcm_data & 15); /* we stay in this state until the time for this sample is complete */ chip->clocks_left = chip->sample_rate * 4; if (--chip->nibbles_left == 0) chip->state = STATE_BLOCK_HEADER; else chip->state = STATE_NIBBLE_MSN; break; } /* if there's a DRQ, fudge the state */ if (chip->drq) { chip->post_drq_state = chip->state; chip->post_drq_clocks = chip->clocks_left - 21; chip->state = STATE_DROP_DRQ; chip->clocks_left = 21; } } /************************************************************ Stream callback *************************************************************/ //static STREAM_UPDATE( upd7759_update ) void upd7759_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //upd7759_state *chip = (upd7759_state *)param; upd7759_state *chip = &UPD7759Data[ChipID]; INT32 clocks_left = chip->clocks_left; INT16 sample = chip->sample; UINT32 step = chip->step; UINT32 pos = chip->pos; stream_sample_t *buffer = outputs[0]; stream_sample_t *buffer2 = outputs[1]; /* loop until done */ if (chip->state != STATE_IDLE) while (samples != 0) { /* store the current sample */ *buffer++ = sample << 7; *buffer2++ = sample << 7; samples--; /* advance by the number of clocks/output sample */ pos += step; /* handle clocks, but only in standalone mode */ if (! chip->ChipMode) { while (chip->rom && pos >= FRAC_ONE) { int clocks_this_time = pos >> FRAC_BITS; if (clocks_this_time > clocks_left) clocks_this_time = clocks_left; /* clock once */ pos -= clocks_this_time * FRAC_ONE; clocks_left -= clocks_this_time; /* if we're out of clocks, time to handle the next state */ if (clocks_left == 0) { /* advance one state; if we hit idle, bail */ advance_state(chip); if (chip->state == STATE_IDLE) break; /* reimport the variables that we cached */ clocks_left = chip->clocks_left; sample = chip->sample; } } } else { UINT8 CntFour; if (! clocks_left) { advance_state(chip); clocks_left = chip->clocks_left; } // advance the state (4x because of Clock Divider /4) for (CntFour = 0; CntFour < 4; CntFour ++) { clocks_left --; if (! clocks_left) { advance_state(chip); clocks_left = chip->clocks_left; } } } } /* if we got out early, just zap the rest of the buffer */ if (samples != 0) { memset(buffer, 0, samples * sizeof(*buffer)); memset(buffer2, 0, samples * sizeof(*buffer2)); } /* flush the state back */ chip->clocks_left = clocks_left; chip->pos = pos; } /************************************************************ DRQ callback *************************************************************/ /*static TIMER_CALLBACK( upd7759_slave_update ) { upd7759_state *chip = (upd7759_state *)ptr; UINT8 olddrq = chip->drq; // update the stream //stream_update(chip->channel); // advance the state advance_state(chip); // if the DRQ changed, update it logerror("slave_update: DRQ %d->%d\n", olddrq, chip->drq); if (olddrq != chip->drq && chip->drqcallback) //(*chip->drqcallback)(chip->device, chip->drq); (*chip->drqcallback)(chip->drq); // set a timer to go off when that is done //if (chip->state != STATE_IDLE) // timer_adjust_oneshot(chip->timer, attotime_mul(chip->clock_period, chip->clocks_left), 0); }*/ /************************************************************ Sound startup *************************************************************/ static void upd7759_reset(upd7759_state *chip) { chip->pos = 0; chip->fifo_in = 0; chip->drq = 0; chip->state = STATE_IDLE; chip->clocks_left = 0; chip->nibbles_left = 0; chip->repeat_count = 0; chip->post_drq_state = STATE_IDLE; chip->post_drq_clocks = 0; chip->req_sample = 0; chip->last_sample = 0; chip->block_header = 0; chip->sample_rate = 0; chip->first_valid_header = 0; chip->offset = 0; chip->repeat_offset = 0; chip->adpcm_state = 0; chip->adpcm_data = 0; chip->sample = 0; // Valley Bell: reset buffer chip->data_buf[0] = chip->data_buf[1] = 0x00; chip->dbuf_pos_read = 0x00; chip->dbuf_pos_write = 0x00; /* turn off any timer */ //if (chip->timer) // timer_adjust_oneshot(chip->timer, attotime_never, 0); if (chip->ChipMode) chip->clocks_left = -1; } //static DEVICE_RESET( upd7759 ) void device_reset_upd7759(UINT8 ChipID) { upd7759_state *chip = &UPD7759Data[ChipID]; //upd7759_reset(get_safe_token(device)); upd7759_reset(chip); } //static STATE_POSTLOAD( upd7759_postload ) /*static void upd7759_postload(void* param) { upd7759_state *chip = (upd7759_state *)param; chip->rom = chip->rombase + chip->romoffset; }*/ /*static void register_for_save(upd7759_state *chip, running_device *device) { state_save_register_device_item(device, 0, chip->pos); state_save_register_device_item(device, 0, chip->step); state_save_register_device_item(device, 0, chip->fifo_in); state_save_register_device_item(device, 0, chip->reset); state_save_register_device_item(device, 0, chip->start); state_save_register_device_item(device, 0, chip->drq); state_save_register_device_item(device, 0, chip->state); state_save_register_device_item(device, 0, chip->clocks_left); state_save_register_device_item(device, 0, chip->nibbles_left); state_save_register_device_item(device, 0, chip->repeat_count); state_save_register_device_item(device, 0, chip->post_drq_state); state_save_register_device_item(device, 0, chip->post_drq_clocks); state_save_register_device_item(device, 0, chip->req_sample); state_save_register_device_item(device, 0, chip->last_sample); state_save_register_device_item(device, 0, chip->block_header); state_save_register_device_item(device, 0, chip->sample_rate); state_save_register_device_item(device, 0, chip->first_valid_header); state_save_register_device_item(device, 0, chip->offset); state_save_register_device_item(device, 0, chip->repeat_offset); state_save_register_device_item(device, 0, chip->adpcm_state); state_save_register_device_item(device, 0, chip->adpcm_data); state_save_register_device_item(device, 0, chip->sample); state_save_register_device_item(device, 0, chip->romoffset); state_save_register_postload(device->machine, upd7759_postload, chip); }*/ //static DEVICE_START( upd7759 ) int device_start_upd7759(UINT8 ChipID, int clock) { static const upd7759_interface defintrf = { 0 }; //const upd7759_interface *intf = (device->baseconfig().static_config() != NULL) ? (const upd7759_interface *)device->baseconfig().static_config() : &defintrf; const upd7759_interface *intf = &defintrf; //upd7759_state *chip = get_safe_token(device); upd7759_state *chip; if (ChipID >= MAX_CHIPS) return 0; chip = &UPD7759Data[ChipID]; //chip->device = device; chip->ChipMode = (clock & 0x80000000) >> 31; clock &= 0x7FFFFFFF; /* allocate a stream channel */ //chip->channel = stream_create(device, 0, 1, device->clock()/4, chip, upd7759_update); /* compute the stepping rate based on the chip's clock speed */ chip->step = 4 * FRAC_ONE; /* compute the clock period */ //chip->clock_period = ATTOTIME_IN_HZ(device->clock()); /* set the intial state */ chip->state = STATE_IDLE; /* compute the ROM base or allocate a timer */ //chip->rom = chip->rombase = *device->region(); chip->romsize = 0x00; chip->rom = chip->rombase = NULL; //if (chip->rom == NULL) // chip->timer = timer_alloc(device->machine, upd7759_slave_update, chip); chip->romoffset = 0x00; /* set the DRQ callback */ //chip->drqcallback = intf->drqcallback; /* assume /RESET and /START are both high */ chip->reset = 1; chip->start = 1; /* toggle the reset line to finish the reset */ upd7759_reset(chip); //register_for_save(chip, device); return clock / 4; } void device_stop_upd7759(UINT8 ChipID) { upd7759_state *chip = &UPD7759Data[ChipID]; free(chip->rombase); chip->rombase = NULL; return; } /************************************************************ I/O handlers *************************************************************/ //void upd7759_reset_w(running_device *device, UINT8 data) void upd7759_reset_w(UINT8 ChipID, UINT8 data) { /* update the reset value */ //upd7759_state *chip = get_safe_token(device); upd7759_state *chip = &UPD7759Data[ChipID]; UINT8 oldreset = chip->reset; chip->reset = (data != 0); /* update the stream first */ //stream_update(chip->channel); /* on the falling edge, reset everything */ if (oldreset && !chip->reset) upd7759_reset(chip); } //void upd7759_start_w(running_device *device, UINT8 data) void upd7759_start_w(UINT8 ChipID, UINT8 data) { /* update the start value */ //upd7759_state *chip = get_safe_token(device); upd7759_state *chip = &UPD7759Data[ChipID]; UINT8 oldstart = chip->start; chip->start = (data != 0); if (DEBUG_STATES) logerror("upd7759_start_w: %d->%d\n", oldstart, chip->start); /* update the stream first */ //stream_update(chip->channel); /* on the rising edge, if we're idle, start going, but not if we're held in reset */ if (chip->state == STATE_IDLE && !oldstart && chip->start && chip->reset) { chip->state = STATE_START; /* for slave mode, start the timer going */ //if (chip->timer) // timer_adjust_oneshot(chip->timer, attotime_zero, 0); chip->clocks_left = 0; } } //WRITE8_DEVICE_HANDLER( upd7759_port_w ) void upd7759_port_w(UINT8 ChipID, offs_t offset, UINT8 data) { /* update the FIFO value */ //upd7759_state *chip = get_safe_token(device); upd7759_state *chip = &UPD7759Data[ChipID]; if (! chip->ChipMode) { chip->fifo_in = data; } else { // Valley Bell: added FIFO buffer for Slave mode chip->data_buf[chip->dbuf_pos_write] = data; chip->dbuf_pos_write ++; chip->dbuf_pos_write &= 0x3F; } } //int upd7759_busy_r(running_device *device) int upd7759_busy_r(UINT8 ChipID) { /* return /BUSY */ //upd7759_state *chip = get_safe_token(device); upd7759_state *chip = &UPD7759Data[ChipID]; return (chip->state == STATE_IDLE); } //void upd7759_set_bank_base(running_device *device, UINT32 base) void upd7759_set_bank_base(UINT8 ChipID, UINT32 base) { //upd7759_state *chip = get_safe_token(device); upd7759_state *chip = &UPD7759Data[ChipID]; chip->rom = chip->rombase + base; chip->romoffset = base; } void upd7759_write(UINT8 ChipID, UINT8 Port, UINT8 Data) { switch(Port) { case 0x00: upd7759_reset_w(ChipID, Data); break; case 0x01: upd7759_start_w(ChipID, Data); break; case 0x02: upd7759_port_w(ChipID, 0x00, Data); break; case 0x03: upd7759_set_bank_base(ChipID, Data * 0x20000); break; } return; } void upd7759_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { upd7759_state *chip = &UPD7759Data[ChipID]; if (chip->romsize != ROMSize) { chip->rombase = (UINT8*)realloc(chip->rombase, ROMSize); chip->romsize = ROMSize; memset(chip->rombase, 0xFF, ROMSize); chip->rom = chip->rombase + chip->romoffset; } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->rombase + DataStart, ROMData, DataLength); return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( upd7759 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(upd7759_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( upd7759 ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( upd7759 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "UPD7759"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "NEC ADPCM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ //DEFINE_LEGACY_SOUND_DEVICE(UPD7759, upd7759); ================================================ FILE: VGMPlay/chips/upd7759.h ================================================ #pragma once //#include "devlegcy.h" /* There are two modes for the uPD7759, selected through the !MD pin. This is the mode select input. High is stand alone, low is slave. We're making the assumption that nobody switches modes through software. */ #define UPD7759_STANDARD_CLOCK 640000 typedef struct _upd7759_interface upd7759_interface; struct _upd7759_interface { //void (*drqcallback)(running_device *device, int param); /* drq callback (per chip, slave mode only) */ void (*drqcallback)(int param); /* drq callback (per chip, slave mode only) */ }; void upd7759_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void device_reset_upd7759(UINT8 ChipID); int device_start_upd7759(UINT8 ChipID, int clock); void device_stop_upd7759(UINT8 ChipID); //void upd7759_set_bank_base(running_device *device, offs_t base); //void upd7759_reset_w(running_device *device, UINT8 data); //void upd7759_start_w(running_device *device, UINT8 data); //int upd7759_busy_r(running_device *device); //WRITE8_DEVICE_HANDLER( upd7759_port_w ); void upd7759_set_bank_base(UINT8 ChipID, offs_t base); void upd7759_reset_w(UINT8 ChipID, UINT8 data); void upd7759_start_w(UINT8 ChipID, UINT8 data); int upd7759_busy_r(UINT8 ChipID); void upd7759_port_w(UINT8 ChipID, offs_t offset, UINT8 data); void upd7759_write(UINT8 ChipID, UINT8 Port, UINT8 Data); void upd7759_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); //DECLARE_LEGACY_SOUND_DEVICE(UPD7759, upd7759); ================================================ FILE: VGMPlay/chips/vrc7tone.h ================================================ /* VRC7 VOICE */ /* Dumped via VRC7 debug mode by Nuke.YKT */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, 0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, 0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, 0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, 0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, 0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, 0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, 0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, 0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, 0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, 0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, 0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, 0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, 0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, 0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, 0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, 0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, 0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, ================================================ FILE: VGMPlay/chips/vsu.c ================================================ /* Mednafen - Multi-system Emulator * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#include "vb.h" #include // for memset #include "mamedef.h" #include "vsu.h" typedef struct { UINT8 IntlControl[6]; UINT8 LeftLevel[6]; UINT8 RightLevel[6]; UINT16 Frequency[6]; UINT16 EnvControl[6]; // Channel 5/6 extra functionality tacked on too. UINT8 RAMAddress[6]; UINT8 SweepControl; UINT8 WaveData[5][0x20]; UINT8 ModData[0x20]; // // // INT32 EffFreq[6]; INT32 Envelope[6]; INT32 WavePos[6]; INT32 ModWavePos; INT32 LatcherClockDivider[6]; INT32 FreqCounter[6]; INT32 IntervalCounter[6]; INT32 EnvelopeCounter[6]; INT32 SweepModCounter; INT32 EffectsClockDivider[6]; INT32 IntervalClockDivider[6]; INT32 EnvelopeClockDivider[6]; INT32 SweepModClockDivider; INT32 NoiseLatcherClockDivider; UINT32 NoiseLatcher; UINT32 lfsr; //INT32 last_output[6][2]; INT32 last_ts; //Blip_Buffer *sbuf[2]; //Blip_Synth Synth; int clock; int smplrate; UINT8 Muted[6]; // values for Timing Calculation int tm_smpl; int tm_clk; } vsu_state; static void VSU_Power(vsu_state* chip); //void VSU_Write(UINT8 ChipID, UINT32 A, UINT8 V); INLINE void VSU_CalcCurrentOutput(vsu_state* chip, int ch, int* left, int* right); static void VSU_Update(vsu_state* chip, INT32 timestamp, int* outleft, int* outright); static const int Tap_LUT[8] = { 15 - 1, 11 - 1, 14 - 1, 5 - 1, 9 - 1, 7 - 1, 10 - 1, 12 - 1 }; extern UINT32 SampleRate; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static vsu_state VSUData[MAX_CHIPS]; static void VSU_Power(vsu_state* chip) { int ch; chip->SweepControl = 0; chip->SweepModCounter = 0; chip->SweepModClockDivider = 1; for(ch = 0; ch < 6; ch++) { chip->IntlControl[ch] = 0; chip->LeftLevel[ch] = 0; chip->RightLevel[ch] = 0; chip->Frequency[ch] = 0; chip->EnvControl[ch] = 0; chip->RAMAddress[ch] = 0; chip->EffFreq[ch] = 0; chip->Envelope[ch] = 0; chip->WavePos[ch] = 0; chip->FreqCounter[ch] = 0; chip->IntervalCounter[ch] = 0; chip->EnvelopeCounter[ch] = 0; chip->EffectsClockDivider[ch] = 4800; chip->IntervalClockDivider[ch] = 4; chip->EnvelopeClockDivider[ch] = 4; chip->LatcherClockDivider[ch] = 120; } chip->NoiseLatcherClockDivider = 120; chip->NoiseLatcher = 0; memset(chip->WaveData, 0, sizeof(chip->WaveData)); memset(chip->ModData, 0, sizeof(chip->ModData)); chip->last_ts = 0; } void VSU_Write(UINT8 ChipID, UINT32 A, UINT8 V) { vsu_state* chip = &VSUData[ChipID]; A <<= 2; A &= 0x7FF; //Update(timestamp); //printf("VSU Write: %d, %08x %02x\n", timestamp, A, V); if(A < 0x280) chip->WaveData[A >> 7][(A >> 2) & 0x1F] = V & 0x3F; else if(A < 0x400) { //if(A >= 0x300) // printf("Modulation mirror write? %08x %02x\n", A, V); chip->ModData[(A >> 2) & 0x1F] = V; } else if(A < 0x600) { int ch = (A >> 6) & 0xF; //if(ch < 6) //printf("Ch: %d, Reg: %d, Value: %02x\n", ch, (A >> 2) & 0xF, V); if(ch > 5) { if(A == 0x580 && (V & 1)) { int i; //puts("STOP, HAMMER TIME"); for(i = 0; i < 6; i++) chip->IntlControl[i] &= ~0x80; } } else switch((A >> 2) & 0xF) { case 0x0: chip->IntlControl[ch] = V & ~0x40; if(V & 0x80) { chip->EffFreq[ch] = chip->Frequency[ch]; if(ch == 5) chip->FreqCounter[ch] = 10 * (2048 - chip->EffFreq[ch]); else chip->FreqCounter[ch] = 2048 - chip->EffFreq[ch]; chip->IntervalCounter[ch] = (V & 0x1F) + 1; chip->EnvelopeCounter[ch] = (chip->EnvControl[ch] & 0x7) + 1; if(ch == 4) { chip->SweepModCounter = (chip->SweepControl >> 4) & 7; chip->SweepModClockDivider = (chip->SweepControl & 0x80) ? 8 : 1; chip->ModWavePos = 0; } chip->WavePos[ch] = 0; if(ch == 5) chip->lfsr = 1; //if(!(chip->IntlControl[ch] & 0x80)) // chip->Envelope[ch] = (chip->EnvControl[ch] >> 4) & 0xF; chip->EffectsClockDivider[ch] = 4800; chip->IntervalClockDivider[ch] = 4; chip->EnvelopeClockDivider[ch] = 4; } break; case 0x1: chip->LeftLevel[ch] = (V >> 4) & 0xF; chip->RightLevel[ch] = (V >> 0) & 0xF; break; case 0x2: chip->Frequency[ch] &= 0xFF00; chip->Frequency[ch] |= V << 0; chip->EffFreq[ch] &= 0xFF00; chip->EffFreq[ch] |= V << 0; break; case 0x3: chip->Frequency[ch] &= 0x00FF; chip->Frequency[ch] |= (V & 0x7) << 8; chip->EffFreq[ch] &= 0x00FF; chip->EffFreq[ch] |= (V & 0x7) << 8; break; case 0x4: chip->EnvControl[ch] &= 0xFF00; chip->EnvControl[ch] |= V << 0; chip->Envelope[ch] = (V >> 4) & 0xF; break; case 0x5: chip->EnvControl[ch] &= 0x00FF; if(ch == 4) chip->EnvControl[ch] |= (V & 0x73) << 8; else if(ch == 5) chip->EnvControl[ch] |= (V & 0x73) << 8; else chip->EnvControl[ch] |= (V & 0x03) << 8; break; case 0x6: chip->RAMAddress[ch] = V & 0xF; break; case 0x7: if(ch == 4) { chip->SweepControl = V; } break; } } } INLINE void VSU_CalcCurrentOutput(vsu_state* chip, int ch, int* left, int* right) { int WD; int l_ol, r_ol; if(!(chip->IntlControl[ch] & 0x80) || chip->Muted[ch]) { *left = *right = 0; return; } if(ch == 5) WD = chip->NoiseLatcher; //(NoiseLatcher << 6) - NoiseLatcher; else { if(chip->RAMAddress[ch] > 4) WD = 0x20; //0; else WD = chip->WaveData[chip->RAMAddress[ch]][chip->WavePos[ch]]; // - 0x20; } l_ol = chip->Envelope[ch] * chip->LeftLevel[ch]; if(l_ol) { l_ol >>= 3; l_ol += 1; } r_ol = chip->Envelope[ch] * chip->RightLevel[ch]; if(r_ol) { r_ol >>= 3; r_ol += 1; } WD -= 0x20; (*left) += WD * l_ol; (*right) += WD * r_ol; return; } static void VSU_Update(vsu_state* chip, INT32 timestamp, int* outleft, int* outright) { //int left, right; int ch; *outleft = 0; *outright = 0; //puts("VSU Start"); for(ch = 0; ch < 6; ch++) { INT32 clocks = timestamp - chip->last_ts; //INT32 running_timestamp = chip->last_ts; if(!(chip->IntlControl[ch] & 0x80) || chip->Muted[ch]) continue; // channel disabled - don't add anything to output //// Output sound here //VSU_CalcCurrentOutput(chip, ch, &left, &right); //Synth.offset_inline(running_timestamp, left - chip->last_output[ch][0], sbuf[0]); //Synth.offset_inline(running_timestamp, right - chip->last_output[ch][1], sbuf[1]); //chip->last_output[ch][0] = left; //chip->last_output[ch][1] = right; //if(!(chip->IntlControl[ch] & 0x80)) // continue; while(clocks > 0) { INT32 chunk_clocks = clocks; if(chunk_clocks > chip->EffectsClockDivider[ch]) chunk_clocks = chip->EffectsClockDivider[ch]; if(ch == 5) { if(chunk_clocks > chip->NoiseLatcherClockDivider) chunk_clocks = chip->NoiseLatcherClockDivider; } else { if(chip->EffFreq[ch] >= 2040) { if(chunk_clocks > chip->LatcherClockDivider[ch]) chunk_clocks = chip->LatcherClockDivider[ch]; } else { if(chunk_clocks > chip->FreqCounter[ch]) chunk_clocks = chip->FreqCounter[ch]; } } if(ch == 5 && chunk_clocks > chip->NoiseLatcherClockDivider) chunk_clocks = chip->NoiseLatcherClockDivider; chip->FreqCounter[ch] -= chunk_clocks; while(chip->FreqCounter[ch] <= 0) { if(ch == 5) { int feedback = ((chip->lfsr >> 7) & 1) ^ ((chip->lfsr >> Tap_LUT[(chip->EnvControl[5] >> 12) & 0x7]) & 1); chip->lfsr = ((chip->lfsr << 1) & 0x7FFF) | feedback; chip->FreqCounter[ch] += 10 * (2048 - chip->EffFreq[ch]); } else { chip->FreqCounter[ch] += 2048 - chip->EffFreq[ch]; chip->WavePos[ch] = (chip->WavePos[ch] + 1) & 0x1F; } } chip->LatcherClockDivider[ch] -= chunk_clocks; while(chip->LatcherClockDivider[ch] <= 0) chip->LatcherClockDivider[ch] += 120; if(ch == 5) { chip->NoiseLatcherClockDivider -= chunk_clocks; if(!chip->NoiseLatcherClockDivider) { chip->NoiseLatcherClockDivider = 120; chip->NoiseLatcher = ((chip->lfsr & 1) << 6) - (chip->lfsr & 1); } } chip->EffectsClockDivider[ch] -= chunk_clocks; while(chip->EffectsClockDivider[ch] <= 0) { chip->EffectsClockDivider[ch] += 4800; chip->IntervalClockDivider[ch]--; while(chip->IntervalClockDivider[ch] <= 0) { chip->IntervalClockDivider[ch] += 4; if(chip->IntlControl[ch] & 0x20) { chip->IntervalCounter[ch]--; if(!chip->IntervalCounter[ch]) { chip->IntlControl[ch] &= ~0x80; } } chip->EnvelopeClockDivider[ch]--; while(chip->EnvelopeClockDivider[ch] <= 0) { chip->EnvelopeClockDivider[ch] += 4; if(chip->EnvControl[ch] & 0x0100) // Enveloping enabled? { chip->EnvelopeCounter[ch]--; if(!chip->EnvelopeCounter[ch]) { chip->EnvelopeCounter[ch] = (chip->EnvControl[ch] & 0x7) + 1; if(chip->EnvControl[ch] & 0x0008) // Grow { if(chip->Envelope[ch] < 0xF || (chip->EnvControl[ch] & 0x200)) chip->Envelope[ch] = (chip->Envelope[ch] + 1) & 0xF; } else // Decay { if(chip->Envelope[ch] > 0 || (chip->EnvControl[ch] & 0x200)) chip->Envelope[ch] = (chip->Envelope[ch] - 1) & 0xF; } } } } // end while(chip->EnvelopeClockDivider[ch] <= 0) } // end while(chip->IntervalClockDivider[ch] <= 0) if(ch == 4) { chip->SweepModClockDivider--; while(chip->SweepModClockDivider <= 0) { chip->SweepModClockDivider += (chip->SweepControl & 0x80) ? 8 : 1; if(((chip->SweepControl >> 4) & 0x7) && (chip->EnvControl[ch] & 0x4000)) { if(chip->SweepModCounter) chip->SweepModCounter--; if(!chip->SweepModCounter) { chip->SweepModCounter = (chip->SweepControl >> 4) & 0x7; if(chip->EnvControl[ch] & 0x1000) // Modulation { if(chip->ModWavePos < 32 || (chip->EnvControl[ch] & 0x2000)) { chip->ModWavePos &= 0x1F; chip->EffFreq[ch] = (chip->EffFreq[ch] + (INT8)chip->ModData[chip->ModWavePos]); if(chip->EffFreq[ch] < 0) { //puts("Underflow"); chip->EffFreq[ch] = 0; } else if(chip->EffFreq[ch] > 0x7FF) { //puts("Overflow"); chip->EffFreq[ch] = 0x7FF; } chip->ModWavePos++; } //puts("Mod"); } else // Sweep { INT32 delta = chip->EffFreq[ch] >> (chip->SweepControl & 0x7); INT32 NewFreq = chip->EffFreq[ch] + ((chip->SweepControl & 0x8) ? delta : -delta); //printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq); if(NewFreq < 0) chip->EffFreq[ch] = 0; else if(NewFreq > 0x7FF) { //chip->EffFreq[ch] = 0x7FF; chip->IntlControl[ch] &= ~0x80; } else chip->EffFreq[ch] = NewFreq; } } } } // end while(chip->SweepModClockDivider <= 0) } // end if(ch == 4) } // end while(chip->EffectsClockDivider[ch] <= 0) clocks -= chunk_clocks; //running_timestamp += chunk_clocks; // Output sound here too. //VSU_CalcCurrentOutput(chip, ch, &left, &right); //Synth.offset_inline(running_timestamp, left - chip->last_output[ch][0], sbuf[0]); //Synth.offset_inline(running_timestamp, right - chip->last_output[ch][1], sbuf[1]); //chip->last_output[ch][0] = left; //chip->last_output[ch][1] = right; } VSU_CalcCurrentOutput(chip, ch, outleft, outright); } chip->last_ts = timestamp; //puts("VSU End"); } /*void VSU_EndFrame(INT32 timestamp) { Update(chip, timestamp); chip->last_ts = 0; }*/ /*int VSU_StateAction(StateMem *sm, int load, int data_only) { SFORMAT StateRegs[] = { SFARRAY(IntlControl, 6), SFARRAY(LeftLevel, 6), SFARRAY(RightLevel, 6), SFARRAY16(Frequency, 6), SFARRAY16(EnvControl, 6), SFARRAY(RAMAddress, 6), SFVAR(SweepControl), SFARRAY(&WaveData[0][0], 5 * 0x20), SFARRAY(ModData, 0x20), SFARRAY32(EffFreq, 6), SFARRAY32(Envelope, 6), SFARRAY32(WavePos, 6), SFVAR(ModWavePos), SFARRAY32(LatcherClockDivider, 6), SFARRAY32(FreqCounter, 6), SFARRAY32(IntervalCounter, 6), SFARRAY32(EnvelopeCounter, 6), SFVAR(SweepModCounter), SFARRAY32(EffectsClockDivider, 6), SFARRAY32(IntervalClockDivider, 6), SFARRAY32(EnvelopeClockDivider, 6), SFVAR(SweepModClockDivider), SFVAR(NoiseLatcherClockDivider), SFVAR(NoiseLatcher), SFVAR(lfsr), SFEND }; int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "VSU"); if(load) { } return(ret); }*/ void vsu_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { vsu_state* chip = &VSUData[ChipID]; int curSmpl; for (curSmpl = 0; curSmpl < samples; curSmpl ++) { chip->tm_smpl ++; chip->tm_clk = (int)((INT64)chip->tm_smpl * chip->clock / chip->smplrate); VSU_Update(chip, chip->tm_clk, &outputs[0][curSmpl], &outputs[1][curSmpl]); if (chip->last_ts >= chip->clock) { chip->last_ts -= chip->clock; chip->tm_clk -= chip->clock; chip->tm_smpl -= chip->smplrate; } // Volume per channel: 0x1F (envelope/volume) * 0x3F (unsigned sample) = 0x7A1 (~0x800) // I turned the samples into signed format (-0x20..0x1F), so the used range is +-0x400. // 16-bit values are up to 0x8000 // 0x8000 / 0x400 / 6 = 5.33 (possible boost without clipping) // Because music usually doesn't use the maximum volume (SFX do), I boost by 2^3 = 8. outputs[0][curSmpl] <<= 3; outputs[1][curSmpl] <<= 3; } return; } int device_start_vsu(UINT8 ChipID, int clock) { vsu_state* chip; UINT8 CurChn; if (ChipID >= MAX_CHIPS) return 0; chip = &VSUData[ChipID]; chip->clock = clock; chip->smplrate = chip->clock / 120; // most effects run with a /120 divider if (((CHIP_SAMPLING_MODE & 0x01) && chip->smplrate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) chip->smplrate = CHIP_SAMPLE_RATE; for (CurChn = 0; CurChn < 6; CurChn ++) chip->Muted[CurChn] = 0x00; return chip->smplrate; } void device_stop_vsu(UINT8 ChipID) { vsu_state* chip = &VSUData[ChipID]; } void device_reset_vsu(UINT8 ChipID) { vsu_state* chip = &VSUData[ChipID]; VSU_Power(chip); chip->tm_smpl = 0; chip->tm_clk = 0; return; } //void vsu_set_options(UINT16 Options); void vsu_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { vsu_state* chip = &VSUData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/vsu.h ================================================ #ifndef __VB_VSU_H #define __VB_VSU_H void VSU_Write(UINT8 ChipID, UINT32 A, UINT8 V); void vsu_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_vsu(UINT8 ChipID, int clock); void device_stop_vsu(UINT8 ChipID); void device_reset_vsu(UINT8 ChipID); //void vsu_set_options(UINT16 Options); void vsu_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); #endif // __VB_VSU_H ================================================ FILE: VGMPlay/chips/ws_audio.c ================================================ #include #include // for NULL #include // for memset #include "mamedef.h" typedef UINT8 BYTE; typedef UINT8 byte; typedef UINT8 uint8; //#include //#include "types.h" //#include "./nec/necintrf.h" //#include "ws_memory.h" #include "ws_initialIo.h" //#include "ws_io.h" #include "ws_audio.h" //#include "wsr_player.h" #define SNDP chip->ws_ioRam[0x80] #define SNDV chip->ws_ioRam[0x88] #define SNDSWP chip->ws_ioRam[0x8C] #define SWPSTP chip->ws_ioRam[0x8D] #define NSCTL chip->ws_ioRam[0x8E] #define WAVDTP chip->ws_ioRam[0x8F] #define SNDMOD chip->ws_ioRam[0x90] #define SNDOUT chip->ws_ioRam[0x91] #define PCSRL chip->ws_ioRam[0x92] #define PCSRH chip->ws_ioRam[0x93] #define DMASL chip->ws_ioRam[0x40] #define DMASH chip->ws_ioRam[0x41] #define DMASB chip->ws_ioRam[0x42] #define DMADB chip->ws_ioRam[0x43] #define DMADL chip->ws_ioRam[0x44] #define DMADH chip->ws_ioRam[0x45] #define DMACL chip->ws_ioRam[0x46] #define DMACH chip->ws_ioRam[0x47] #define DMACTL chip->ws_ioRam[0x48] #define SDMASL chip->ws_ioRam[0x4A] #define SDMASH chip->ws_ioRam[0x4B] #define SDMASB chip->ws_ioRam[0x4C] #define SDMACL chip->ws_ioRam[0x4E] #define SDMACH chip->ws_ioRam[0x4F] #define SDMACTL chip->ws_ioRam[0x52] //SoundDMA ̓]Ԋu // ۂ̐lȂ̂ŁA\zł // TvOlĂ݂Ĉȉ̂悤ɂ // 12KHz = 1.00HBlank = 256cyclesԊu // 16KHz = 0.75HBlank = 192cyclesԊu // 20KHz = 0.60HBlank = 154cyclesԊu // 24KHz = 0.50HBlank = 128cyclesԊu const int DMACycles[4] = { 256, 192, 154, 128 }; typedef struct { int wave; int lvol; int rvol; long offset; long delta; long pos; UINT8 Muted; } WS_AUDIO; typedef struct { WS_AUDIO ws_audio[4]; int sweepDelta; int sweepOffset; int SweepTime; int SweepStep; int SweepCount; int SweepFreq; int NoiseType; int NoiseRng; int MainVolume; int PCMVolumeLeft; int PCMVolumeRight; //int WaveAdrs; UINT8 ws_ioRam[0x100]; UINT8 *ws_internalRam; int clock; int smplrate; } wsa_state; #define DEFAULT_CLOCK 3072000 static void ws_audio_process(wsa_state* chip); extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; extern UINT32 SampleRate; #define MAX_CHIPS 0x02 static wsa_state WSAData[MAX_CHIPS]; int ws_audio_init(UINT8 ChipID, int clock) { wsa_state* chip; UINT8 CurChn; if (ChipID >= MAX_CHIPS) return 0; chip = &WSAData[ChipID]; chip->ws_internalRam = (UINT8*)malloc(0x4000); // actual size is 64 KB, but the audio chip can only access 16 KB chip->clock = clock; chip->smplrate = SampleRate; if (((CHIP_SAMPLING_MODE & 0x01) && chip->smplrate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) chip->smplrate = CHIP_SAMPLE_RATE; for (CurChn = 0; CurChn < 4; CurChn ++) chip->ws_audio[CurChn].Muted = 0x00; return chip->smplrate; } void ws_audio_reset(UINT8 ChipID) { wsa_state* chip = &WSAData[ChipID]; int i; memset(&chip->ws_audio, 0, sizeof(WS_AUDIO)); chip->SweepTime = 0; chip->SweepStep = 0; chip->NoiseType = 0; chip->NoiseRng = 1; chip->MainVolume = 0x02; // 0x04 chip->PCMVolumeLeft = 0; chip->PCMVolumeRight = 0; chip->sweepDelta = chip->clock * 256 / chip->smplrate; chip->sweepOffset = 0; for (i=0x80;i<0xc9;i++) ws_audio_port_write(ChipID, i, initialIoValue[i]); } void ws_audio_done(UINT8 ChipID) { wsa_state* chip = &WSAData[ChipID]; free(chip->ws_internalRam); chip->ws_internalRam = NULL; return; } void ws_audio_update(UINT8 ChipID, stream_sample_t** buffer, int length) { wsa_state* chip = &WSAData[ChipID]; stream_sample_t* bufL; stream_sample_t* bufR; int i, ch, cnt; long w, l, r; bufL = buffer[0]; bufR = buffer[1]; for (i=0; isweepOffset += chip->sweepDelta; while(chip->sweepOffset >= 0x10000) { chip->sweepOffset -= 0x10000; ws_audio_process(chip); } l = r = 0; for (ch=0; ch<4; ch++) { if (chip->ws_audio[ch].Muted) continue; if ((ch==1) && (SNDMOD&0x20)) { // Voiceo w = chip->ws_ioRam[0x89]; w -= 0x80; l += chip->PCMVolumeLeft * w; r += chip->PCMVolumeRight * w; } else if (SNDMOD&(1<ws_audio[ch].offset += chip->ws_audio[ch].delta; cnt = chip->ws_audio[ch].offset>>16; chip->ws_audio[ch].offset &= 0xffff; while (cnt > 0) { cnt--; chip->NoiseRng &= noise_bit[chip->NoiseType]-1; if (!chip->NoiseRng) chip->NoiseRng = noise_bit[chip->NoiseType]-1; Masked = chip->NoiseRng & noise_mask[chip->NoiseType]; XorReg = 0; while (Masked) { XorReg ^= Masked&1; Masked >>= 1; } if (XorReg) chip->NoiseRng |= noise_bit[chip->NoiseType]; chip->NoiseRng >>= 1; } PCSRL = (byte)(chip->NoiseRng&0xff); PCSRH = (byte)((chip->NoiseRng>>8)&0x7f); w = (chip->NoiseRng&1)? 0x7f:-0x80; l += chip->ws_audio[ch].lvol * w; r += chip->ws_audio[ch].rvol * w; } else { chip->ws_audio[ch].offset += chip->ws_audio[ch].delta; cnt = chip->ws_audio[ch].offset>>16; chip->ws_audio[ch].offset &= 0xffff; chip->ws_audio[ch].pos += cnt; chip->ws_audio[ch].pos &= 0x1f; w = chip->ws_internalRam[(chip->ws_audio[ch].wave&0xFFF0) + (chip->ws_audio[ch].pos>>1)]; if ((chip->ws_audio[ch].pos&1) == 0) w = (w<<4)&0xf0; //ʃju else w = w&0xf0; //ʃju w -= 0x80; l += chip->ws_audio[ch].lvol * w; r += chip->ws_audio[ch].rvol * w; } } } /*l = l * chip->MainVolume; if (l > 0x7fff) l = 0x7fff; else if (l < -0x8000) l = -0x8000; r = r * chip->MainVolume; if (r > 0x7fff) r = 0x7fff; else if (r < -0x8000) r = -0x8000; *buffer++ = (short)l; *buffer++ = (short)r;*/ bufL[i] = l * chip->MainVolume; bufR[i] = r * chip->MainVolume; } } void ws_audio_port_write(UINT8 ChipID, BYTE port, BYTE value) { wsa_state* chip = &WSAData[ChipID]; int i; long freq; //Update_SampleData(); chip->ws_ioRam[port]=value; switch (port) { // 0x80-0x87̎gWX^ɂ‚ // - bN}&tHe0x0f̋Ȃł́Ag=0xFFFF ̉sv // - fWfB[vWFNg0x0d̋Ȃ̃mCY g=0x07FF ʼno // ‚܂A0xFFFF ̎oȂĂƂ낤B // łA0x07FF ̎oȂǁAmCYôB case 0x80: case 0x81: i=(((unsigned int)chip->ws_ioRam[0x81])<<8)+((unsigned int)chip->ws_ioRam[0x80]); if (i == 0xffff) freq = 0; else freq = chip->clock/(2048-(i&0x7ff)); chip->ws_audio[0].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); break; case 0x82: case 0x83: i=(((unsigned int)chip->ws_ioRam[0x83])<<8)+((unsigned int)chip->ws_ioRam[0x82]); if (i == 0xffff) freq = 0; else freq = chip->clock/(2048-(i&0x7ff)); chip->ws_audio[1].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); break; case 0x84: case 0x85: i=(((unsigned int)chip->ws_ioRam[0x85])<<8)+((unsigned int)chip->ws_ioRam[0x84]); chip->SweepFreq = i; if (i == 0xffff) freq = 0; else freq = chip->clock/(2048-(i&0x7ff)); chip->ws_audio[2].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); break; case 0x86: case 0x87: i=(((unsigned int)chip->ws_ioRam[0x87])<<8)+((unsigned int)chip->ws_ioRam[0x86]); if (i == 0xffff) freq = 0; else freq = chip->clock/(2048-(i&0x7ff)); chip->ws_audio[3].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); break; case 0x88: chip->ws_audio[0].lvol = (value>>4)&0xf; chip->ws_audio[0].rvol = value&0xf; break; case 0x89: chip->ws_audio[1].lvol = (value>>4)&0xf; chip->ws_audio[1].rvol = value&0xf; break; case 0x8A: chip->ws_audio[2].lvol = (value>>4)&0xf; chip->ws_audio[2].rvol = value&0xf; break; case 0x8B: chip->ws_audio[3].lvol = (value>>4)&0xf; chip->ws_audio[3].rvol = value&0xf; break; case 0x8C: chip->SweepStep = (signed char)value; break; case 0x8D: //Sweep̊Ԋu 1/375[s] = 2.666..[ms] //CPU ClockŌ 3072000/375 = 8192[cycles] //̐ݒlnƂƁA8192[cycles]*(n+1) ԊuSweep邱ƂɂȂ // // HBlank (256cycles) ̊ԊuŌƁA //@8192/256 = 32 //Ȃ̂ŁA32[HBlank]*(n+1) ԊuƂȂ chip->SweepTime = (((unsigned int)value)+1)<<5; chip->SweepCount = chip->SweepTime; break; case 0x8E: chip->NoiseType = value&7; if (value&8) chip->NoiseRng = 1; //mCYJE^[Zbg break; case 0x8F: //WaveAdrs = (unsigned int)value<<6; chip->ws_audio[0].wave = (unsigned int)value<<6; chip->ws_audio[1].wave = chip->ws_audio[0].wave + 0x10; chip->ws_audio[2].wave = chip->ws_audio[1].wave + 0x10; chip->ws_audio[3].wave = chip->ws_audio[2].wave + 0x10; break; case 0x90: break; case 0x91: //ł̃{[́ASpeakerɑ΂Ă̒炵̂ŁA //wbhtHڑĂƔFΖ薳炵B chip->ws_ioRam[port] |= 0x80; break; case 0x92: case 0x93: break; case 0x94: chip->PCMVolumeLeft = (value&0xc)*2; chip->PCMVolumeRight = ((value<<2)&0xc)*2; break; /*case 0x52: if (value&0x80) ws_timer_set(2, DMACycles[value&3]); break;*/ } } BYTE ws_audio_port_read(UINT8 ChipID, BYTE port) { wsa_state* chip = &WSAData[ChipID]; return (chip->ws_ioRam[port]); } // HBlankԊuŌĂ΂ // Note: Must be called every 256 cycles (3072000 Hz clock), i.e. at 12000 Hz static void ws_audio_process(wsa_state* chip) { long freq; if (chip->SweepStep && (SNDMOD&0x40)) { if (chip->SweepCount < 0) { chip->SweepCount = chip->SweepTime; chip->SweepFreq += chip->SweepStep; chip->SweepFreq &= 0x7FF; //Update_SampleData(); freq = chip->clock/(2048-chip->SweepFreq); chip->ws_audio[2].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); } chip->SweepCount--; } } /*void ws_audio_sounddma(void) { int i, j, b; if ((SDMACTL&0x88)==0x80) { i=(SDMACH<<8)|SDMACL; j=(SDMASB<<16)|(SDMASH<<8)|SDMASL; b=cpu_readmem20(j); Update_SampleData(); ws_ioRam[0x89]=b; i--; j++; if(i<32) { i=0; SDMACTL&=0x7F; } else { ws_timer_set(2, DMACycles[SDMACTL&3]); } SDMASB=(byte)((j>>16)&0xFF); SDMASH=(byte)((j>>8)&0xFF); SDMASL=(byte)(j&0xFF); SDMACH=(byte)((i>>8)&0xFF); SDMACL=(byte)(i&0xFF); } }*/ void ws_write_ram(UINT8 ChipID, UINT16 offset, UINT8 value) { wsa_state* chip = &WSAData[ChipID]; // RAM - 16 KB (WS) / 64 KB (WSC) internal RAM if (offset >= 0x4000) return; // We only allocated 16 KB. chip->ws_internalRam[offset] = value; return; } void ws_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { wsa_state* chip = &WSAData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) chip->ws_audio[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/ws_audio.h ================================================ #ifndef __WS_AUDIO_H__ #define __WS_AUDIO_H__ int ws_audio_init(UINT8 ChipID, int clock); void ws_audio_reset(UINT8 ChipID); void ws_audio_done(UINT8 ChipID); void ws_audio_update(UINT8 ChipID, stream_sample_t** buffer, int length); void ws_audio_port_write(UINT8 ChipID, UINT8 port, UINT8 value); UINT8 ws_audio_port_read(UINT8 ChipID, UINT8 port); //void ws_audio_process(void); //void ws_audio_sounddma(void); void ws_write_ram(UINT8 ChipID, UINT16 offset, UINT8 value); void ws_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //extern int WaveAdrs; #endif ================================================ FILE: VGMPlay/chips/ws_initialIo.h ================================================ //////////////////////////////////////////////////////////////////////////////// // Initial I/O values //////////////////////////////////////////////////////////////////////////////// // // // // // // ////////////////////////////////////////////////////////////////////////////// uint8 initialIoValue[256]= { 0x00,//0 0x00,//1 0x9d,//2 0xbb,//3 0x00,//4 0x00,//5 0x00,//6 0x26,//7 0xfe,//8 0xde,//9 0xf9,//a 0xfb,//b 0xdb,//c 0xd7,//d 0x7f,//e 0xf5,//f 0x00,//10 0x00,//11 0x00,//12 0x00,//13 0x01,//14 0x00,//15 0x9e,//16 0x9b,//17 0x00,//18 0x00,//19 0x00,//1a 0x00,//1b 0x99,//1c 0xfd,//1d 0xb7,//1e 0xdf,//1f 0x30,//20 0x57,//21 0x75,//22 0x76,//23 0x15,//24 0x73,//25 0x77,//26 0x77,//27 0x20,//28 0x75,//29 0x50,//2a 0x36,//2b 0x70,//2c 0x67,//2d 0x50,//2e 0x77,//2f 0x57,//30 0x54,//31 0x75,//32 0x77,//33 0x75,//34 0x17,//35 0x37,//36 0x73,//37 0x50,//38 0x57,//39 0x60,//3a 0x77,//3b 0x70,//3c 0x77,//3d 0x10,//3e 0x73,//3f 0x00,//40 0x00,//41 0x00,//42 0x00,//43 0x00,//44 0x00,//45 0x00,//46 0x00,//47 0x00,//48 0x00,//49 0x00,//4a 0x00,//4b 0x00,//4c 0x00,//4d 0x00,//4e 0x00,//4f 0x00,//50 0x00,//51 0x00,//52 0x00,//53 0x00,//54 0x00,//55 0x00,//56 0x00,//57 0x00,//58 0x00,//59 0x00,//5a 0x00,//5b 0x00,//5c 0x00,//5d 0x00,//5e 0x00,//5f 0x0a,//60 0x00,//61 0x00,//62 0x00,//63 0x00,//64 0x00,//65 0x00,//66 0x00,//67 0x00,//68 0x00,//69 0x00,//6a 0x0f,//6b 0x00,//6c 0x00,//6d 0x00,//6e 0x00,//6f 0x00,//70 0x00,//71 0x00,//72 0x00,//73 0x00,//74 0x00,//75 0x00,//76 0x00,//77 0x00,//78 0x00,//79 0x00,//7a 0x00,//7b 0x00,//7c 0x00,//7d 0x00,//7e 0x00,//7f 0xFF,//80 0x07,//81 0xFF,//82 0x07,//83 0xFF,//84 0x07,//85 0xFF,//86 0x07,//87 0x00,//88 0x00,//89 0x00,//8a 0x00,//8b 0x00,//8c 0x1f,//8d 1d ? 0x00,//8e 0x00,//8f 0x00,//90 0x00,//91 0x00,//92 0x00,//93 0x00,//94 0x00,//95 0x00,//96 0x00,//97 0x00,//98 0x00,//99 0x00,//9a 0x00,//9b 0x00,//9c 0x00,//9d 0x03,//9e 0x00,//9f 0x87-2,//a0 0x00,//a1 0x00,//a2 0x00,//a3 0x0,//a4 2b 0x0,//a5 7f 0x4f,//a6 0xff,//a7 cf ? 0x00,//a8 0x00,//a9 0x00,//aa 0x00,//ab 0x00,//ac 0x00,//ad 0x00,//ae 0x00,//af 0x00,//b0 0xdb,//b1 0x00,//b2 0x00,//b3 0x00,//b4 0x40,//b5 0x00,//b6 0x00,//b7 0x00,//b8 0x00,//b9 0x01,//ba 0x00,//bb 0x42,//bc 0x00,//bd 0x83,//be 0x00,//bf 0x2f,//c0 0x3f,//c1 0xff,//c2 0xff,//c3 0x00,//c4 0x00,//c5 0x00,//c6 0x00,//c7 0xd1,//c8? 0xd1,//c9 0xd1,//ca 0xd1,//cb 0xd1,//cc 0xd1,//cd 0xd1,//ce 0xd1,//cf 0xd1,//d0 0xd1,//d1 0xd1,//d2 0xd1,//d3 0xd1,//d4 0xd1,//d5 0xd1,//d6 0xd1,//d7 0xd1,//d8 0xd1,//d9 0xd1,//da 0xd1,//db 0xd1,//dc 0xd1,//dd 0xd1,//de 0xd1,//df 0xd1,//e0 0xd1,//e1 0xd1,//e2 0xd1,//e3 0xd1,//e4 0xd1,//e5 0xd1,//e6 0xd1,//e7 0xd1,//e8 0xd1,//e9 0xd1,//ea 0xd1,//eb 0xd1,//ec 0xd1,//ed 0xd1,//ee 0xd1,//ef 0xd1,//f0 0xd1,//f1 0xd1,//f2 0xd1,//f3 0xd1,//f4 0xd1,//f5 0xd1,//f6 0xd1,//f7 0xd1,//f8 0xd1,//f9 0xd1,//fa 0xd1,//fb 0xd1,//fc 0xd1,//fd 0xd1,//fe 0xd1 //ff }; ================================================ FILE: VGMPlay/chips/x1_010.c ================================================ /*************************************************************************** -= Seta Hardware =- driver by Luca Elia (l.elia@tin.it) rewrite by Manbow-J(manbowj@hamal.freemail.ne.jp) X1-010 Seta Custom Sound Chip (80 Pin PQFP) Custom programmed Mitsubishi M60016 Gate Array, 3608 gates, 148 Max I/O ports The X1-010 is 16 Voices sound generator, each channel gets it's waveform from RAM (128 bytes per waveform, 8 bit unsigned data) or sampling PCM(8bit unsigned data). Registers: 8 registers per channel (mapped to the lower bytes of 16 words on the 68K) Reg: Bits: Meaning: 0 7654 3--- ---- -2-- PCM/Waveform repeat flag (0:Ones 1:Repeat) (*1) ---- --1- Sound out select (0:PCM 1:Waveform) ---- ---0 Key on / off 1 7654 ---- PCM Volume 1 (L?) ---- 3210 PCM Volume 2 (R?) Waveform No. 2 PCM Frequency Waveform Pitch Lo 3 Waveform Pitch Hi 4 PCM Sample Start / 0x1000 [Start/End in bytes] Waveform Envelope Time 5 PCM Sample End 0x100 - (Sample End / 0x1000) [PCM ROM is Max 1MB?] Waveform Envelope No. 6 Reserved 7 Reserved offset 0x0000 - 0x0fff Wave form data offset 0x1000 - 0x1fff Envelope data *1 : when 0 is specified, hardware interrupt is caused(allways return soon) ***************************************************************************/ //#include "emu.h" #include #include // for NULL #include // for memset #include "mamedef.h" #include "x1_010.h" #define VERBOSE_SOUND 0 #define VERBOSE_REGISTER_WRITE 0 #define VERBOSE_REGISTER_READ 0 #define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0) #define LOG_REGISTER_WRITE(x) do { if (VERBOSE_REGISTER_WRITE) logerror x; } while (0) #define LOG_REGISTER_READ(x) do { if (VERBOSE_REGISTER_READ) logerror x; } while (0) #define SETA_NUM_CHANNELS 16 //#define FREQ_BASE_BITS 8 // Frequency fixed decimal shift bits #define FREQ_BASE_BITS 14 // Frequency fixed decimal shift bits #define ENV_BASE_BITS 16 // wave form envelope fixed decimal shift bits #define VOL_BASE (2*32*256/30) // Volume base /* this structure defines the parameters for a channel */ typedef struct { unsigned char status; unsigned char volume; // volume / wave form no. unsigned char frequency; // frequency / pitch lo unsigned char pitch_hi; // reserved / pitch hi unsigned char start; // start address / envelope time unsigned char end; // end address / envelope no. unsigned char reserve[2]; } X1_010_CHANNEL; typedef struct _x1_010_state x1_010_state; struct _x1_010_state { /* Variables only used here */ int rate; // Output sampling rate (Hz) //sound_stream * stream; // Stream handle //int address; // address eor data //const UINT8 *region; // region name UINT32 ROMSize; UINT8* rom; int sound_enable; // sound output enable/disable UINT8 reg[0x2000]; // X1-010 Register & wave form area // UINT8 HI_WORD_BUF[0x2000]; // X1-010 16bit access ram check avoidance work UINT32 smp_offset[SETA_NUM_CHANNELS]; UINT32 env_offset[SETA_NUM_CHANNELS]; UINT32 base_clock; UINT8 Muted[SETA_NUM_CHANNELS]; }; /* mixer tables and internal buffers */ //static short *mixer_buffer = NULL; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x02 static x1_010_state X1010Data[MAX_CHIPS]; /*INLINE x1_010_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == X1_010); return (x1_010_state *)downcast(device)->token(); }*/ /*-------------------------------------------------------------- generate sound to the mix buffer --------------------------------------------------------------*/ //static STREAM_UPDATE( seta_update ) void seta_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //x1_010_state *info = (x1_010_state *)param; x1_010_state *info = &X1010Data[ChipID]; X1_010_CHANNEL *reg; int ch, i, volL, volR, freq, div; register INT8 *start, *end, data; register UINT8 *env; register UINT32 smp_offs, smp_step, env_offs, env_step, delta; // mixer buffer zero clear memset( outputs[0], 0, samples*sizeof(*outputs[0]) ); memset( outputs[1], 0, samples*sizeof(*outputs[1]) ); // if( info->sound_enable == 0 ) return; for( ch = 0; ch < SETA_NUM_CHANNELS; ch++ ) { reg = (X1_010_CHANNEL *)&(info->reg[ch*sizeof(X1_010_CHANNEL)]); if( (reg->status&1) != 0 && ! info->Muted[ch]) { // Key On stream_sample_t *bufL = outputs[0]; stream_sample_t *bufR = outputs[1]; div = (reg->status&0x80) ? 1 : 0; if( (reg->status&2) == 0 ) { // PCM sampling start = (INT8 *)(info->rom + reg->start*0x1000); end = (INT8 *)(info->rom + (0x100-reg->end)*0x1000); volL = ((reg->volume>>4)&0xf)*VOL_BASE; volR = ((reg->volume>>0)&0xf)*VOL_BASE; smp_offs = info->smp_offset[ch]; freq = reg->frequency>>div; // Meta Fox does write the frequency register, but this is a hack to make it "work" with the current setup // This is broken for Arbalester (it writes 8), but that'll be fixed later. if( freq == 0 ) freq = 4; smp_step = (UINT32)((float)info->base_clock/8192.0f *freq*(1<rate+0.5f); if( smp_offs == 0 ) { LOG_SOUND(( "Play sample %p - %p, channel %X volume %d:%d freq %X step %X offset %X\n", start, end, ch, volL, volR, freq, smp_step, smp_offs )); } for( i = 0; i < samples; i++ ) { delta = smp_offs>>FREQ_BASE_BITS; // sample ended? if( start+delta >= end ) { reg->status &= ~0x01; // Key off break; } data = *(start+delta); *bufL++ += (data*volL/256); *bufR++ += (data*volR/256); smp_offs += smp_step; } info->smp_offset[ch] = smp_offs; } else { // Wave form start = (INT8 *)&(info->reg[reg->volume*128+0x1000]); smp_offs = info->smp_offset[ch]; freq = ((reg->pitch_hi<<8)+reg->frequency)>>div; smp_step = (UINT32)((float)info->base_clock/128.0/1024.0/4.0*freq*(1<rate+0.5f); env = (UINT8 *)&(info->reg[reg->end*128]); env_offs = info->env_offset[ch]; env_step = (UINT32)((float)info->base_clock/128.0/1024.0/4.0*reg->start*(1<rate+0.5f); /* Print some more debug info */ if( smp_offs == 0 ) { LOG_SOUND(( "Play waveform %X, channel %X volume %X freq %4X step %X offset %X\n", reg->volume, ch, reg->end, freq, smp_step, smp_offs )); } for( i = 0; i < samples; i++ ) { int vol; delta = env_offs>>ENV_BASE_BITS; // Envelope one shot mode if( (reg->status&4) != 0 && delta >= 0x80 ) { reg->status &= ~0x01; // Key off break; } vol = *(env+(delta&0x7f)); volL = ((vol>>4)&0xf)*VOL_BASE; volR = ((vol>>0)&0xf)*VOL_BASE; data = *(start+((smp_offs>>FREQ_BASE_BITS)&0x7f)); *bufL++ += (data*volL/256); *bufR++ += (data*volR/256); smp_offs += smp_step; env_offs += env_step; } info->smp_offset[ch] = smp_offs; info->env_offset[ch] = env_offs; } } } } //static DEVICE_START( x1_010 ) int device_start_x1_010(UINT8 ChipID, int clock) { int i; //const x1_010_interface *intf = (const x1_010_interface *)device->static_config(); //x1_010_state *info = get_safe_token(device); x1_010_state *info; if (ChipID >= MAX_CHIPS) return 0; info = &X1010Data[ChipID]; //info->region = *device->region(); //info->base_clock = device->clock(); //info->rate = device->clock() / 1024; //info->address = intf->adr; info->ROMSize = 0x00; info->rom = NULL; info->base_clock = clock; info->rate = clock / 512; if (((CHIP_SAMPLING_MODE & 0x01) && info->rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) info->rate = CHIP_SAMPLE_RATE; for( i = 0; i < SETA_NUM_CHANNELS; i++ ) { info->smp_offset[i] = 0; info->env_offset[i] = 0; } /* Print some more debug info */ //LOG_SOUND(("masterclock = %d rate = %d\n", device->clock(), info->rate )); /* get stream channels */ //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->rate,info,seta_update); return info->rate; } void device_stop_x1_010(UINT8 ChipID) { x1_010_state *info = &X1010Data[ChipID]; free(info->rom); info->rom = NULL; return; } void device_reset_x1_010(UINT8 ChipID) { x1_010_state *info = &X1010Data[ChipID]; memset(info->reg, 0, 0x2000); //memset(info->HI_WORD_BUF, 0, 0x2000); memset(info->smp_offset, 0, SETA_NUM_CHANNELS * sizeof(UINT32)); memset(info->env_offset, 0, SETA_NUM_CHANNELS * sizeof(UINT32)); return; } /*void seta_sound_enable_w(device_t *device, int data) { x1_010_state *info = get_safe_token(device); info->sound_enable = data; }*/ /* Use these for 8 bit CPUs */ //READ8_DEVICE_HANDLER( seta_sound_r ) UINT8 seta_sound_r(UINT8 ChipID, offs_t offset) { //x1_010_state *info = get_safe_token(device); x1_010_state *info = &X1010Data[ChipID]; //offset ^= info->address; return info->reg[offset]; } //WRITE8_DEVICE_HANDLER( seta_sound_w ) void seta_sound_w(UINT8 ChipID, offs_t offset, UINT8 data) { //x1_010_state *info = get_safe_token(device); x1_010_state *info = &X1010Data[ChipID]; int channel, reg; //offset ^= info->address; channel = offset/sizeof(X1_010_CHANNEL); reg = offset%sizeof(X1_010_CHANNEL); if( channel < SETA_NUM_CHANNELS && reg == 0 && (info->reg[offset]&1) == 0 && (data&1) != 0 ) { info->smp_offset[channel] = 0; info->env_offset[channel] = 0; } //LOG_REGISTER_WRITE(("%s: offset %6X : data %2X\n", device->machine().describe_context(), offset, data )); info->reg[offset] = data; } void x1_010_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { x1_010_state *info = &X1010Data[ChipID]; if (info->ROMSize != ROMSize) { info->rom = (UINT8*)realloc(info->rom, ROMSize); info->ROMSize = ROMSize; memset(info->rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(info->rom + DataStart, ROMData, DataLength); return; } void x1_010_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { x1_010_state *info = &X1010Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < SETA_NUM_CHANNELS; CurChn ++) info->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } /* Use these for 16 bit CPUs */ /*READ16_DEVICE_HANDLER( seta_sound_word_r ) { //x1_010_state *info = get_safe_token(device); x1_010_state *info = &X1010Data[ChipID]; UINT16 ret; ret = info->HI_WORD_BUF[offset]<<8; ret += (seta_sound_r( device, offset )&0xff); LOG_REGISTER_READ(( "%s: Read X1-010 Offset:%04X Data:%04X\n", device->machine().describe_context(), offset, ret )); return ret; } WRITE16_DEVICE_HANDLER( seta_sound_word_w ) { //x1_010_state *info = get_safe_token(device); x1_010_state *info = &X1010Data[ChipID]; info->HI_WORD_BUF[offset] = (data>>8)&0xff; seta_sound_w( device, offset, data&0xff ); LOG_REGISTER_WRITE(( "%s: Write X1-010 Offset:%04X Data:%04X\n", device->machine().describe_context(), offset, data )); }*/ /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( x1_010 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(x1_010_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( x1_010 ); break; case DEVINFO_FCT_STOP: // Nothing // break; case DEVINFO_FCT_RESET: // Nothing // break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "X1-010"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Seta custom"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(X1_010, x1_010);*/ ================================================ FILE: VGMPlay/chips/x1_010.h ================================================ #pragma once #ifndef __X1_010_H__ #define __X1_010_H__ //#include "devlegcy.h" /*typedef struct _x1_010_interface x1_010_interface; struct _x1_010_interface { int adr; // address };*/ void seta_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_x1_010(UINT8 ChipID, int clock); void device_stop_x1_010(UINT8 ChipID); void device_reset_x1_010(UINT8 ChipID); //READ8_DEVICE_HANDLER ( seta_sound_r ); //WRITE8_DEVICE_HANDLER( seta_sound_w ); UINT8 seta_sound_r(UINT8 ChipID, offs_t offset); void seta_sound_w(UINT8 ChipID, offs_t offset, UINT8 data); //READ16_DEVICE_HANDLER ( seta_sound_word_r ); //WRITE16_DEVICE_HANDLER( seta_sound_word_w ); //void seta_sound_enable_w(device_t *device, int data); void x1_010_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void x1_010_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(X1_010, x1_010); #endif /* __X1_010_H__ */ ================================================ FILE: VGMPlay/chips/ym2151.c ================================================ /***************************************************************************** * * Yamaha YM2151 driver (version 2.150 final beta) * ******************************************************************************/ #include #include #include "mamedef.h" #include #include // for memset #include // for NULL //#include "sndintrf.h" //#include "streams.h" #include "ym2151.h" /* undef this to not use MAME timer system */ //#define USE_MAME_TIMERS /*#define FM_EMU*/ #ifdef FM_EMU #ifdef USE_MAME_TIMERS #undef USE_MAME_TIMERS #endif #endif //static char LOG_CYM_FILE = 0x00; //static FILE * cymfile = NULL; /* struct describing a single operator */ typedef struct{ UINT32 phase; /* accumulated operator phase */ UINT32 freq; /* operator frequency count */ INT32 dt1; /* current DT1 (detune 1 phase inc/decrement) value */ UINT32 mul; /* frequency count multiply */ UINT32 dt1_i; /* DT1 index * 32 */ UINT32 dt2; /* current DT2 (detune 2) value */ signed int *connect; /* operator output 'direction' */ /* only M1 (operator 0) is filled with this data: */ signed int *mem_connect; /* where to put the delayed sample (MEM) */ INT32 mem_value; /* delayed sample (MEM) value */ /* channel specific data; note: each operator number 0 contains channel specific data */ UINT32 fb_shift; /* feedback shift value for operators 0 in each channel */ INT32 fb_out_curr; /* operator feedback value (used only by operators 0) */ INT32 fb_out_prev; /* previous feedback value (used only by operators 0) */ UINT32 kc; /* channel KC (copied to all operators) */ UINT32 kc_i; /* just for speedup */ UINT32 pms; /* channel PMS */ UINT32 ams; /* channel AMS */ /* end of channel specific data */ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ UINT32 state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT32 tl; /* Total attenuation Level */ INT32 volume; /* current envelope attenuation level */ UINT8 eg_sh_d1r; /* (decay state) */ UINT8 eg_sel_d1r; /* (decay state) */ UINT32 d1l; /* envelope switches to sustain state after reaching this level */ UINT8 eg_sh_d2r; /* (sustain state) */ UINT8 eg_sel_d2r; /* (sustain state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT32 key; /* 0=last key was KEY OFF, 1=last key was KEY ON */ UINT32 ks; /* key scale */ UINT32 ar; /* attack rate */ UINT32 d1r; /* decay rate */ UINT32 d2r; /* sustain rate */ UINT32 rr; /* release rate */ UINT32 reserved0; /**/ UINT32 reserved1; /**/ } YM2151Operator; typedef struct { YM2151Operator oper[32]; /* the 32 operators */ UINT32 pan[16]; /* channels output masks (0xffffffff = enable) */ UINT8 Muted[8]; /* used for muting */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */ UINT32 lfo_phase; /* accumulated LFO phase (0 to 255) */ UINT32 lfo_timer; /* LFO timer */ UINT32 lfo_timer_add; /* step of lfo_timer */ UINT32 lfo_overflow; /* LFO generates new output when lfo_timer reaches this value */ UINT32 lfo_counter; /* LFO phase increment counter */ UINT32 lfo_counter_add; /* step of lfo_counter */ UINT8 lfo_wsel; /* LFO waveform (0-saw, 1-square, 2-triangle, 3-random noise) */ UINT8 amd; /* LFO Amplitude Modulation Depth */ INT8 pmd; /* LFO Phase Modulation Depth */ UINT32 lfa; /* LFO current AM output */ INT32 lfp; /* LFO current PM output */ UINT8 test; /* TEST register */ UINT8 ct; /* output control pins (bit1-CT2, bit0-CT1) */ UINT32 noise; /* noise enable/period register (bit 7 - noise enable, bits 4-0 - noise period */ UINT32 noise_rng; /* 17 bit noise shift register */ UINT32 noise_p; /* current noise 'phase'*/ UINT32 noise_f; /* current noise period */ UINT32 csm_req; /* CSM KEY ON / KEY OFF sequence request */ UINT32 irq_enable; /* IRQ enable for timer B (bit 3) and timer A (bit 2); bit 7 - CSM mode (keyon to all slots, everytime timer A overflows) */ UINT32 status; /* chip status (BUSY, IRQ Flags) */ UINT8 connect[8]; /* channels connections */ #ifdef USE_MAME_TIMERS /* ASG 980324 -- added for tracking timers */ emu_timer *timer_A; emu_timer *timer_B; attotime timer_A_time[1024]; /* timer A times for MAME */ attotime timer_B_time[256]; /* timer B times for MAME */ int irqlinestate; #else UINT8 tim_A; /* timer A enable (0-disabled) */ UINT8 tim_B; /* timer B enable (0-disabled) */ INT32 tim_A_val; /* current value of timer A */ INT32 tim_B_val; /* current value of timer B */ UINT32 tim_A_tab[1024]; /* timer A deltas */ UINT32 tim_B_tab[256]; /* timer B deltas */ #endif UINT32 timer_A_index; /* timer A index */ UINT32 timer_B_index; /* timer B index */ UINT32 timer_A_index_old; /* timer A previous index */ UINT32 timer_B_index_old; /* timer B previous index */ /* Frequency-deltas to get the closest frequency possible. * There are 11 octaves because of DT2 (max 950 cents over base frequency) * and LFO phase modulation (max 800 cents below AND over base frequency) * Summary: octave explanation * 0 note code - LFO PM * 1 note code * 2 note code * 3 note code * 4 note code * 5 note code * 6 note code * 7 note code * 8 note code * 9 note code + DT2 + LFO PM * 10 note code + DT2 + LFO PM */ UINT32 freq[11*768]; /* 11 octaves, 768 'cents' per octave */ /* Frequency deltas for DT1. These deltas alter operator frequency * after it has been taken from frequency-deltas table. */ INT32 dt1_freq[8*32]; /* 8 DT1 levels, 32 KC values */ UINT32 noise_tab[32]; /* 17bit Noise Generator periods */ //void (*irqhandler)(const device_config *device, int irq); /* IRQ function handler */ //write8_device_func porthandler; /* port write function handler */ //const device_config *device; unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */ unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */ } YM2151; #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ #define LFO_SH 10 /* 22.10 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>3) /* sin waveform table in 'decibel' scale */ static unsigned int sin_tab[SIN_LEN]; /* translate from D1L to volume index (16 D1L levels) */ static UINT32 d1l_tab[16]; #define RATE_STEPS (8) static const UINT8 eg_inc[19*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ /*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ /*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ /*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ /*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ /*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ /*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(17) in this table - it's directly in the code */ static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ /* 32 dummy (infinite time) rates */ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), /* rates 00-11 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 12 */ O( 4),O( 5),O( 6),O( 7), /* rate 13 */ O( 8),O( 9),O(10),O(11), /* rate 14 */ O(12),O(13),O(14),O(15), /* rate 15 */ O(16),O(16),O(16),O(16), /* 32 dummy rates (same as 15 3) */ O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ /*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ /*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ #define O(a) (a*1) static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-11 */ O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 12 */ O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 32 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) }; #undef O /* DT2 defines offset in cents from base note * * This table defines offset in frequency-deltas table. * User's Manual page 22 * * Values below were calculated using formula: value = orig.val / 1.5625 * * DT2=0 DT2=1 DT2=2 DT2=3 * 0 600 781 950 */ static const UINT32 dt2_tab[4] = { 0, 384, 500, 608 }; /* DT1 defines offset in Hertz from base note * This table is converted while initialization... * Detune table shown in YM2151 User's Manual is wrong (verified on the real chip) */ static const UINT8 dt1_tab[4*32] = { /* 4*32 DT1 values */ /* DT1=0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* DT1=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, /* DT1=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, /* DT1=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 UINT16 phaseinc_rom[768]={ 1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316, 1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335, 1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354, 1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374, 1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394, 1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414, 1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435, 1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455, 1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477, 1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498, 1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521, 1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543, 1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565, 1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587, 1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610, 1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633, 1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657, 1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682, 1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707, 1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731, 1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757, 1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782, 1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809, 1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835, 1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861, 1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888, 1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915, 1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944, 1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972, 1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001, 2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029, 2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059, 2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089, 2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119, 2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150, 2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181, 2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213, 2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246, 2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279, 2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311, 2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345, 2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378, 2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414, 2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449, 2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484, 2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520, 2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556, 2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593 }; /* Noise LFO waveform. Here are just 256 samples out of much longer data. It does NOT repeat every 256 samples on real chip and I wasnt able to find the point where it repeats (even in strings as long as 131072 samples). I only put it here because its better than nothing and perhaps someone might be able to figure out the real algorithm. Note that (due to the way the LFO output is calculated) it is quite possible that two values: 0x80 and 0x00 might be wrong in this table. To be exact: some 0x80 could be 0x81 as well as some 0x00 could be 0x01. */ static const UINT8 lfo_noise_waveform[256] = { 0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A, 0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE, 0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3, 0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2, 0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D, 0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24, 0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA, 0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB, 0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5, 0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F, 0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79, 0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51, 0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE, 0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92, 0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD, 0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55 }; /* these variables stay here for speedup purposes only */ static YM2151 * PSG; static signed int chanout[8]; static signed int m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ static signed int mem; /* one sample delay memory */ /* save output as raw 16-bit sample */ // #define SAVE_SAMPLE // #define SAVE_SEPARATE_CHANNELS #if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS #include static FILE *sample[9]; #endif static void init_tables(void) { signed int i,x,n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to closest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 2; /* 13 bits here (as in real chip) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; for (i=1; i<13; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } #if 0 logerror("tl %04i", x*2); for (i=0; i<13; i++) logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); logerror("\n"); #endif } /*logerror("TL_TAB_LEN = %i (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ /*logerror("ENV_QUIET= %i\n",ENV_QUIET );*/ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to closest */ n = (n>>1)+1; else n = n>>1; sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("sin [0x%4x]= %4i (tl_tab value=%8x)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ } /* calculate d1l_tab table */ for (i=0; i<16; i++) { m = (i!=15 ? i : i+16) * (4.0/ENV_STEP); /* every 3 'dB' except for all bits = 1 = 45+48 'dB' */ d1l_tab[i] = m; /*logerror("d1l_tab[%02x]=%08x\n",i,d1l_tab[i] );*/ } #ifdef SAVE_SAMPLE sample[8]=fopen("sampsum.pcm","wb"); #endif #ifdef SAVE_SEPARATE_CHANNELS sample[0]=fopen("samp0.pcm","wb"); sample[1]=fopen("samp1.pcm","wb"); sample[2]=fopen("samp2.pcm","wb"); sample[3]=fopen("samp3.pcm","wb"); sample[4]=fopen("samp4.pcm","wb"); sample[5]=fopen("samp5.pcm","wb"); sample[6]=fopen("samp6.pcm","wb"); sample[7]=fopen("samp7.pcm","wb"); #endif } static void init_chip_tables(YM2151 *chip) { int i,j; double mult,phaseinc,Hz; double scaler; //attotime pom; double pom; scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); /*logerror("scaler = %20.15f\n", scaler);*/ /* this loop calculates Hertz values for notes from c-0 to b-7 */ /* including 64 'cents' (100/64 that is 1.5625 of real cent) per note */ /* i*100/64/1200 is equal to i/768 */ /* real chip works with 10 bits fixed point values (10.10) */ mult = (1<<(FREQ_SH-10)); /* -10 because phaseinc_rom table values are already in 10.10 format */ for (i=0; i<768; i++) { /* 3.4375 Hz is note A; C# is 4 semitones higher */ Hz = 1000; #if 0 /* Hz is close, but not perfect */ //Hz = scaler * 3.4375 * pow (2, (i + 4 * 64 ) / 768.0 ); /* calculate phase increment */ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; #endif phaseinc = phaseinc_rom[i]; /* real chip phase increment */ phaseinc *= scaler; /* adjust */ /* octave 2 - reference octave */ chip->freq[ 768+2*768+i ] = ((int)(phaseinc*mult)) & 0xffffffc0; /* adjust to X.10 fixed point */ /* octave 0 and octave 1 */ for (j=0; j<2; j++) { chip->freq[768 + j*768 + i] = (chip->freq[ 768+2*768+i ] >> (2-j) ) & 0xffffffc0; /* adjust to X.10 fixed point */ } /* octave 3 to 7 */ for (j=3; j<8; j++) { chip->freq[768 + j*768 + i] = chip->freq[ 768+2*768+i ] << (j-2); } #if 0 pom = (double)chip->freq[ 768+2*768+i ] / ((double)(1<sampfreq / (double)SIN_LEN; logerror("1freq[%4i][%08x]= real %20.15f Hz emul %20.15f Hz\n", i, chip->freq[ 768+2*768+i ], Hz, pom); #endif } /* octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) */ for (i=0; i<768; i++) { chip->freq[ 0*768 + i ] = chip->freq[1*768+0]; } /* octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) */ for (j=8; j<10; j++) { for (i=0; i<768; i++) { chip->freq[768+ j*768 + i ] = chip->freq[768 + 8*768 -1]; } } #if 0 for (i=0; i<11*768; i++) { pom = (double)chip->freq[i] / ((double)(1<sampfreq / (double)SIN_LEN; logerror("freq[%4i][%08x]= emul %20.15f Hz\n", i, chip->freq[i], pom); } #endif mult = (1<clock/64.0) ) / (double)(1<<20); /*calculate phase increment*/ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; /*positive and negative values*/ chip->dt1_freq[ (j+0)*32 + i ] = phaseinc * mult; chip->dt1_freq[ (j+4)*32 + i ] = -chip->dt1_freq[ (j+0)*32 + i ]; #if 0 { int x = j*32 + i; pom = (double)chip->dt1_freq[x] / mult; pom = pom * (double)chip->sampfreq / (double)SIN_LEN; logerror("DT1(%03i)[%02i %02i][%08x]= real %19.15f Hz emul %19.15f Hz\n", x, j, i, chip->dt1_freq[x], Hz, pom); } #endif } } /* calculate timers' deltas */ /* User's Manual pages 15,16 */ mult = (1<clock), 64 * (1024 - i)); pom = ((double)64 * (1024 - i) / chip->clock); #ifdef USE_MAME_TIMERS chip->timer_A_time[i] = pom; #else //chip->tim_A_tab[i] = attotime_to_double(pom) * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ chip->tim_A_tab[i] = pom * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ #endif } for (i=0; i<256; i++) { /* ASG 980324: changed to compute both tim_B_tab and timer_B_time */ //pom= attotime_mul(ATTOTIME_IN_HZ(chip->clock), 1024 * (256 - i)); pom = ((double)1024 * (256 - i) / chip->clock); #ifdef USE_MAME_TIMERS chip->timer_B_time[i] = pom; #else //chip->tim_B_tab[i] = attotime_to_double(pom) * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ chip->tim_B_tab[i] = pom * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ #endif } /* calculate noise periods table */ scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); for (i=0; i<32; i++) { j = (i!=31 ? i : 30); /* rate 30 and 31 are the same */ j = 32-j; j = (65536.0 / (double)(j*32.0)); /* number of samples per one shift of the shift register */ /*chip->noise_tab[i] = j * 64;*/ /* number of chip clock cycles per one shift */ chip->noise_tab[i] = j * 64 * scaler; /*logerror("noise_tab[%02x]=%08x\n", i, chip->noise_tab[i]);*/ } } #define KEY_ON(op, key_set){ \ if (!(op)->key) \ { \ (op)->phase = 0; /* clear phase */ \ (op)->state = EG_ATT; /* KEY ON = attack */ \ (op)->volume += (~(op)->volume * \ (eg_inc[(op)->eg_sel_ar + ((PSG->eg_cnt>>(op)->eg_sh_ar)&7)]) \ ) >>4; \ if ((op)->volume <= MIN_ATT_INDEX) \ { \ (op)->volume = MIN_ATT_INDEX; \ (op)->state = EG_DEC; \ } \ } \ (op)->key |= key_set; \ } #define KEY_OFF(op, key_clr){ \ if ((op)->key) \ { \ (op)->key &= key_clr; \ if (!(op)->key) \ { \ if ((op)->state>EG_REL) \ (op)->state = EG_REL;/* KEY OFF = release */\ } \ } \ } INLINE void envelope_KONKOFF(YM2151Operator * op, int v) { if (v&0x08) /* M1 */ KEY_ON (op+0, 1) else KEY_OFF(op+0,~1) if (v&0x20) /* M2 */ KEY_ON (op+1, 1) else KEY_OFF(op+1,~1) if (v&0x10) /* C1 */ KEY_ON (op+2, 1) else KEY_OFF(op+2,~1) if (v&0x40) /* C2 */ KEY_ON (op+3, 1) else KEY_OFF(op+3,~1) } #ifdef USE_MAME_TIMERS /*static TIMER_CALLBACK( irqAon_callback ) { YM2151 *chip = (YM2151 *)ptr; int oldstate = chip->irqlinestate; chip->irqlinestate |= 1; if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1); } static TIMER_CALLBACK( irqBon_callback ) { YM2151 *chip = (YM2151 *)ptr; int oldstate = chip->irqlinestate; chip->irqlinestate |= 2; if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1); } static TIMER_CALLBACK( irqAoff_callback ) { YM2151 *chip = (YM2151 *)ptr; int oldstate = chip->irqlinestate; chip->irqlinestate &= ~1; if (oldstate == 1 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0); } static TIMER_CALLBACK( irqBoff_callback ) { YM2151 *chip = (YM2151 *)ptr; int oldstate = chip->irqlinestate; chip->irqlinestate &= ~2; if (oldstate == 2 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0); } static TIMER_CALLBACK( timer_callback_a ) { YM2151 *chip = (YM2151 *)ptr; timer_adjust_oneshot(chip->timer_A, chip->timer_A_time[ chip->timer_A_index ], 0); chip->timer_A_index_old = chip->timer_A_index; if (chip->irq_enable & 0x04) { chip->status |= 1; timer_set(machine, attotime_zero,chip,0,irqAon_callback); } if (chip->irq_enable & 0x80) chip->csm_req = 2; // request KEY ON / KEY OFF sequence } static TIMER_CALLBACK( timer_callback_b ) { YM2151 *chip = (YM2151 *)ptr; timer_adjust_oneshot(chip->timer_B, chip->timer_B_time[ chip->timer_B_index ], 0); chip->timer_B_index_old = chip->timer_B_index; if (chip->irq_enable & 0x08) { chip->status |= 2; timer_set(machine, attotime_zero,chip,0,irqBon_callback); } }*/ #if 0 static TIMER_CALLBACK( timer_callback_chip_busy ) { YM2151 *chip = (YM2151 *)ptr; chip->status &= 0x7f; /* reset busy flag */ } #endif #endif INLINE void set_connect( YM2151Operator *om1, int cha, int v) { YM2151Operator *om2 = om1+1; YM2151Operator *oc1 = om1+2; /* set connect algorithm */ /* MEM is simply one sample delay */ switch( v&7 ) { case 0: /* M1---C1---MEM---M2---C2---OUT */ om1->connect = &c1; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 1: /* M1------+-MEM---M2---C2---OUT */ /* C1-+ */ om1->connect = &mem; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 2: /* M1-----------------+-C2---OUT */ /* C1---MEM---M2-+ */ om1->connect = &c2; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 3: /* M1---C1---MEM------+-C2---OUT */ /* M2-+ */ om1->connect = &c1; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &c2; break; case 4: /* M1---C1-+-OUT */ /* M2---C2-+ */ /* MEM: not used */ om1->connect = &c1; oc1->connect = &chanout[cha]; om2->connect = &c2; om1->mem_connect = &mem; /* store it anywhere where it will not be used */ break; case 5: /* +----C1----+ */ /* M1-+-MEM---M2-+-OUT */ /* +----C2----+ */ om1->connect = 0; /* special mark */ oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &m2; break; case 6: /* M1---C1-+ */ /* M2-+-OUT */ /* C2-+ */ /* MEM: not used */ om1->connect = &c1; oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &mem; /* store it anywhere where it will not be used */ break; case 7: /* M1-+ */ /* C1-+-OUT */ /* M2-+ */ /* C2-+ */ /* MEM: not used*/ om1->connect = &chanout[cha]; oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &mem; /* store it anywhere where it will not be used */ break; } } INLINE void refresh_EG(YM2151Operator * op) { UINT32 kc; UINT32 v; kc = op->kc; /* v = 32 + 2*RATE + RKS = max 126 */ v = kc >> op->ks; if ((op->ar+v) < 32+62) { op->eg_sh_ar = eg_rate_shift [op->ar + v ]; op->eg_sel_ar = eg_rate_select[op->ar + v ]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17*RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r= eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r= eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op+=1; v = kc >> op->ks; if ((op->ar+v) < 32+62) { op->eg_sh_ar = eg_rate_shift [op->ar + v ]; op->eg_sel_ar = eg_rate_select[op->ar + v ]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17*RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r= eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r= eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op+=1; v = kc >> op->ks; if ((op->ar+v) < 32+62) { op->eg_sh_ar = eg_rate_shift [op->ar + v ]; op->eg_sel_ar = eg_rate_select[op->ar + v ]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17*RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r= eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r= eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op+=1; v = kc >> op->ks; if ((op->ar+v) < 32+62) { op->eg_sh_ar = eg_rate_shift [op->ar + v ]; op->eg_sel_ar = eg_rate_select[op->ar + v ]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17*RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r= eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r= eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; } /* write a register on YM2151 chip number 'n' */ void ym2151_write_reg(void *_chip, int r, int v) { YM2151 *chip = (YM2151 *)_chip; YM2151Operator *op = &chip->oper[ (r&0x07)*4+((r&0x18)>>3) ]; /* adjust bus to 8 bits */ r &= 0xff; v &= 0xff; #if 0 /* There is no info on what YM2151 really does when busy flag is set */ if ( chip->status & 0x80 ) return; timer_set ( attotime_mul(ATTOTIME_IN_HZ(chip->clock), 64), chip, 0, timer_callback_chip_busy); chip->status |= 0x80; /* set busy flag for 64 chip clock cycles */ #endif /*if (LOG_CYM_FILE && (cymfile) && (r!=0) ) { fputc( (unsigned char)r, cymfile ); fputc( (unsigned char)v, cymfile ); }*/ switch(r & 0xe0){ case 0x00: switch(r){ case 0x01: /* LFO reset(bit 1), Test Register (other bits) */ chip->test = v; if (v&2) chip->lfo_phase = 0; break; case 0x08: PSG = chip; /* PSG is used in KEY_ON macro */ envelope_KONKOFF(&chip->oper[ (v&7)*4 ], v ); break; case 0x0f: /* noise mode enable, noise period */ chip->noise = v; chip->noise_f = chip->noise_tab[ v & 0x1f ]; break; case 0x10: /* timer A hi */ chip->timer_A_index = (chip->timer_A_index & 0x003) | (v<<2); break; case 0x11: /* timer A low */ chip->timer_A_index = (chip->timer_A_index & 0x3fc) | (v & 3); break; case 0x12: /* timer B */ chip->timer_B_index = v; break; case 0x14: /* CSM, irq flag reset, irq enable, timer start/stop */ chip->irq_enable = v; /* bit 3-timer B, bit 2-timer A, bit 7 - CSM */ if (v&0x10) /* reset timer A irq flag */ { #ifdef USE_MAME_TIMERS chip->status &= ~1; timer_set(chip->device->machine, attotime_zero,chip,0,irqAoff_callback); #else int oldstate = chip->status & 3; chip->status &= ~1; //if ((oldstate==1) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0); #endif } if (v&0x20) /* reset timer B irq flag */ { #ifdef USE_MAME_TIMERS chip->status &= ~2; timer_set(chip->device->machine, attotime_zero,chip,0,irqBoff_callback); #else int oldstate = chip->status & 3; chip->status &= ~2; //if ((oldstate==2) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0); #endif } if (v&0x02){ /* load and start timer B */ #ifdef USE_MAME_TIMERS /* ASG 980324: added a real timer */ /* start timer _only_ if it wasn't already started (it will reload time value next round) */ if (!timer_enable(chip->timer_B, 1)) { timer_adjust_oneshot(chip->timer_B, chip->timer_B_time[ chip->timer_B_index ], 0); chip->timer_B_index_old = chip->timer_B_index; } #else if (!chip->tim_B) { chip->tim_B = 1; chip->tim_B_val = chip->tim_B_tab[ chip->timer_B_index ]; } #endif }else{ /* stop timer B */ #ifdef USE_MAME_TIMERS /* ASG 980324: added a real timer */ timer_enable(chip->timer_B, 0); #else chip->tim_B = 0; #endif } if (v&0x01){ /* load and start timer A */ #ifdef USE_MAME_TIMERS /* ASG 980324: added a real timer */ /* start timer _only_ if it wasn't already started (it will reload time value next round) */ if (!timer_enable(chip->timer_A, 1)) { timer_adjust_oneshot(chip->timer_A, chip->timer_A_time[ chip->timer_A_index ], 0); chip->timer_A_index_old = chip->timer_A_index; } #else if (!chip->tim_A) { chip->tim_A = 1; chip->tim_A_val = chip->tim_A_tab[ chip->timer_A_index ]; } #endif }else{ /* stop timer A */ #ifdef USE_MAME_TIMERS /* ASG 980324: added a real timer */ timer_enable(chip->timer_A, 0); #else chip->tim_A = 0; #endif } break; case 0x18: /* LFO frequency */ { chip->lfo_overflow = ( 1 << ((15-(v>>4))+3) ) * (1<lfo_counter_add = 0x10 + (v & 0x0f); } break; case 0x19: /* PMD (bit 7==1) or AMD (bit 7==0) */ if (v&0x80) chip->pmd = v & 0x7f; else chip->amd = v & 0x7f; break; case 0x1b: /* CT2, CT1, LFO waveform */ chip->ct = v >> 6; chip->lfo_wsel = v & 3; //if (chip->porthandler) (*chip->porthandler)(chip->device, 0 , chip->ct ); break; default: #ifdef _DEBUG //logerror("YM2151 Write %02x to undocumented register #%02x\n",v,r); #endif break; } break; case 0x20: op = &chip->oper[ (r&7) * 4 ]; switch(r & 0x18){ case 0x00: /* RL enable, Feedback, Connection */ op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0; chip->pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0; chip->pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0; chip->connect[r&7] = v&7; set_connect(op, r&7, v&7); break; case 0x08: /* Key Code */ v &= 0x7f; if (v != op->kc) { UINT32 kc, kc_channel; kc_channel = (v - (v>>2))*64; kc_channel += 768; kc_channel |= (op->kc_i & 63); (op+0)->kc = v; (op+0)->kc_i = kc_channel; (op+1)->kc = v; (op+1)->kc_i = kc_channel; (op+2)->kc = v; (op+2)->kc_i = kc_channel; (op+3)->kc = v; (op+3)->kc_i = kc_channel; kc = v>>2; (op+0)->dt1 = chip->dt1_freq[ (op+0)->dt1_i + kc ]; (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; (op+1)->dt1 = chip->dt1_freq[ (op+1)->dt1_i + kc ]; (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; (op+2)->dt1 = chip->dt1_freq[ (op+2)->dt1_i + kc ]; (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; (op+3)->dt1 = chip->dt1_freq[ (op+3)->dt1_i + kc ]; (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; refresh_EG( op ); } break; case 0x10: /* Key Fraction */ v >>= 2; if (v != (op->kc_i & 63)) { UINT32 kc_channel; kc_channel = v; kc_channel |= (op->kc_i & ~63); (op+0)->kc_i = kc_channel; (op+1)->kc_i = kc_channel; (op+2)->kc_i = kc_channel; (op+3)->kc_i = kc_channel; (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; } break; case 0x18: /* PMS, AMS */ op->pms = (v>>4) & 7; op->ams = (v & 3); break; } break; case 0x40: /* DT1, MUL */ { UINT32 olddt1_i = op->dt1_i; UINT32 oldmul = op->mul; op->dt1_i = (v&0x70)<<1; op->mul = (v&0x0f) ? (v&0x0f)<<1: 1; if (olddt1_i != op->dt1_i) op->dt1 = chip->dt1_freq[ op->dt1_i + (op->kc>>2) ]; if ( (olddt1_i != op->dt1_i) || (oldmul != op->mul) ) op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; } break; case 0x60: /* TL */ op->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ break; case 0x80: /* KS, AR */ { UINT32 oldks = op->ks; UINT32 oldar = op->ar; op->ks = 5-(v>>6); op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; if ( (op->ar != oldar) || (op->ks != oldks) ) { if ((op->ar + (op->kc>>op->ks)) < 32+62) { op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks) ]; op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks) ]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17*RATE_STEPS; } } if (op->ks != oldks) { op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; } } break; case 0xa0: /* LFO AM enable, D1R */ op->AMmask = (v&0x80) ? ~0 : 0; op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; break; case 0xc0: /* DT2, D2R */ { UINT32 olddt2 = op->dt2; op->dt2 = dt2_tab[ v>>6 ]; if (op->dt2 != olddt2) op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; } op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; break; case 0xe0: /* D1L, RR */ op->d1l = d1l_tab[ v>>4 ]; op->rr = 34 + ((v&0x0f)<<2); op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; break; } } /*static TIMER_CALLBACK( cymfile_callback ) { if (cymfile) fputc( (unsigned char)0, cymfile ); }*/ int ym2151_read_status( void *_chip ) { YM2151 *chip = (YM2151 *)_chip; return chip->status; } //#ifdef USE_MAME_TIMERS #if 1 // disabled for now due to crashing with winalloc.c (ERROR_NOT_ENOUGH_MEMORY) /* * state save support for MAME */ //STATE_POSTLOAD( ym2151_postload ) /*void ym2151_postload(void *param) { YM2151 *YM2151_chip = (YM2151 *)param; int j; for (j=0; j<8; j++) set_connect(&YM2151_chip->oper[j*4], j, YM2151_chip->connect[j]); } static void ym2151_state_save_register( YM2151 *chip, const device_config *device ) { int j; // save all 32 operators of chip #i for (j=0; j<32; j++) { YM2151Operator *op; op = &chip->oper[(j&7)*4+(j>>3)]; state_save_register_device_item(device, j, op->phase); state_save_register_device_item(device, j, op->freq); state_save_register_device_item(device, j, op->dt1); state_save_register_device_item(device, j, op->mul); state_save_register_device_item(device, j, op->dt1_i); state_save_register_device_item(device, j, op->dt2); // operators connection is saved in chip data block state_save_register_device_item(device, j, op->mem_value); state_save_register_device_item(device, j, op->fb_shift); state_save_register_device_item(device, j, op->fb_out_curr); state_save_register_device_item(device, j, op->fb_out_prev); state_save_register_device_item(device, j, op->kc); state_save_register_device_item(device, j, op->kc_i); state_save_register_device_item(device, j, op->pms); state_save_register_device_item(device, j, op->ams); state_save_register_device_item(device, j, op->AMmask); state_save_register_device_item(device, j, op->state); state_save_register_device_item(device, j, op->eg_sh_ar); state_save_register_device_item(device, j, op->eg_sel_ar); state_save_register_device_item(device, j, op->tl); state_save_register_device_item(device, j, op->volume); state_save_register_device_item(device, j, op->eg_sh_d1r); state_save_register_device_item(device, j, op->eg_sel_d1r); state_save_register_device_item(device, j, op->d1l); state_save_register_device_item(device, j, op->eg_sh_d2r); state_save_register_device_item(device, j, op->eg_sel_d2r); state_save_register_device_item(device, j, op->eg_sh_rr); state_save_register_device_item(device, j, op->eg_sel_rr); state_save_register_device_item(device, j, op->key); state_save_register_device_item(device, j, op->ks); state_save_register_device_item(device, j, op->ar); state_save_register_device_item(device, j, op->d1r); state_save_register_device_item(device, j, op->d2r); state_save_register_device_item(device, j, op->rr); state_save_register_device_item(device, j, op->reserved0); state_save_register_device_item(device, j, op->reserved1); } state_save_register_device_item_array(device, 0, chip->pan); state_save_register_device_item(device, 0, chip->eg_cnt); state_save_register_device_item(device, 0, chip->eg_timer); state_save_register_device_item(device, 0, chip->eg_timer_add); state_save_register_device_item(device, 0, chip->eg_timer_overflow); state_save_register_device_item(device, 0, chip->lfo_phase); state_save_register_device_item(device, 0, chip->lfo_timer); state_save_register_device_item(device, 0, chip->lfo_timer_add); state_save_register_device_item(device, 0, chip->lfo_overflow); state_save_register_device_item(device, 0, chip->lfo_counter); state_save_register_device_item(device, 0, chip->lfo_counter_add); state_save_register_device_item(device, 0, chip->lfo_wsel); state_save_register_device_item(device, 0, chip->amd); state_save_register_device_item(device, 0, chip->pmd); state_save_register_device_item(device, 0, chip->lfa); state_save_register_device_item(device, 0, chip->lfp); state_save_register_device_item(device, 0, chip->test); state_save_register_device_item(device, 0, chip->ct); state_save_register_device_item(device, 0, chip->noise); state_save_register_device_item(device, 0, chip->noise_rng); state_save_register_device_item(device, 0, chip->noise_p); state_save_register_device_item(device, 0, chip->noise_f); state_save_register_device_item(device, 0, chip->csm_req); state_save_register_device_item(device, 0, chip->irq_enable); state_save_register_device_item(device, 0, chip->status); state_save_register_device_item(device, 0, chip->timer_A_index); state_save_register_device_item(device, 0, chip->timer_B_index); state_save_register_device_item(device, 0, chip->timer_A_index_old); state_save_register_device_item(device, 0, chip->timer_B_index_old); #ifdef USE_MAME_TIMERS state_save_register_device_item(device, 0, chip->irqlinestate); #endif state_save_register_device_item_array(device, 0, chip->connect); state_save_register_postload(device->machine, ym2151_postload, chip); }*/ #else /*STATE_POSTLOAD( ym2151_postload ) { } static void ym2151_state_save_register( YM2151 *chip, const device_config *device ) { }*/ #endif /* * Initialize YM2151 emulator(s). * * 'num' is the number of virtual YM2151's to allocate * 'clock' is the chip clock in Hz * 'rate' is sampling rate */ void * ym2151_init(int clock, int rate) { YM2151 *PSG; int chn; PSG = (YM2151 *)malloc(sizeof(YM2151)); if (PSG == NULL) return NULL; memset(PSG, 0, sizeof(YM2151)); //ym2151_state_save_register( PSG, device ); init_tables(); //PSG->device = device; PSG->clock = clock; /*rate = clock/64;*/ PSG->sampfreq = rate ? rate : 44100; /* avoid division by 0 in init_chip_tables() */ //PSG->irqhandler = NULL; /* interrupt handler */ //PSG->porthandler = NULL; /* port write handler */ init_chip_tables( PSG ); PSG->lfo_timer_add = (1<sampfreq; PSG->eg_timer_add = (1<sampfreq; PSG->eg_timer_overflow = ( 3 ) * (1<eg_timer_add, PSG->eg_timer_overflow);*/ #ifdef USE_MAME_TIMERS /* this must be done _before_ a call to ym2151_reset_chip() */ PSG->timer_A = timer_alloc(device->machine, timer_callback_a, PSG); PSG->timer_B = timer_alloc(device->machine, timer_callback_b, PSG); #else PSG->tim_A = 0; PSG->tim_B = 0; #endif for (chn = 0; chn < 8; chn ++) PSG->Muted[chn] = 0x00; //ym2151_reset_chip(PSG); /*logerror("YM2151[init] clock=%i sampfreq=%i\n", PSG->clock, PSG->sampfreq);*/ /*LOG_CYM_FILE = (options_get_int(mame_options(), OPTION_VGMWRITE) > 0x00); if (LOG_CYM_FILE) { cymfile = fopen("2151_.cym","wb"); if (cymfile) timer_pulse ( device->machine, ATTOTIME_IN_HZ(60), NULL, 0, cymfile_callback); //110 Hz pulse timer else logerror("Could not create file 2151_.cym\n"); }*/ return PSG; } void ym2151_shutdown(void *_chip) { YM2151 *chip = (YM2151 *)_chip; free (chip); /*if (cymfile) fclose (cymfile); cymfile = NULL;*/ #ifdef SAVE_SAMPLE fclose(sample[8]); #endif #ifdef SAVE_SEPARATE_CHANNELS fclose(sample[0]); fclose(sample[1]); fclose(sample[2]); fclose(sample[3]); fclose(sample[4]); fclose(sample[5]); fclose(sample[6]); fclose(sample[7]); #endif } /* * Reset chip number 'n'. */ void ym2151_reset_chip(void *_chip) { int i; YM2151 *chip = (YM2151 *)_chip; /* initialize hardware registers */ for (i=0; i<32; i++) { memset(&chip->oper[i],'\0',sizeof(YM2151Operator)); chip->oper[i].volume = MAX_ATT_INDEX; chip->oper[i].kc_i = 768; /* min kc_i value */ } chip->eg_timer = 0; chip->eg_cnt = 0; chip->lfo_timer = 0; chip->lfo_counter= 0; chip->lfo_phase = 0; chip->lfo_wsel = 0; chip->pmd = 0; chip->amd = 0; chip->lfa = 0; chip->lfp = 0; chip->test= 0; chip->irq_enable = 0; #ifdef USE_MAME_TIMERS /* ASG 980324 -- reset the timers before writing to the registers */ timer_enable(chip->timer_A, 0); timer_enable(chip->timer_B, 0); #else chip->tim_A = 0; chip->tim_B = 0; chip->tim_A_val = 0; chip->tim_B_val = 0; #endif chip->timer_A_index = 0; chip->timer_B_index = 0; chip->timer_A_index_old = 0; chip->timer_B_index_old = 0; chip->noise = 0; chip->noise_rng = 0; chip->noise_p = 0; chip->noise_f = chip->noise_tab[0]; chip->csm_req = 0; chip->status = 0; ym2151_write_reg(chip, 0x1b, 0); /* only because of CT1, CT2 output pins */ ym2151_write_reg(chip, 0x18, 0); /* set LFO frequency */ for (i=0x20; i<0x100; i++) /* set the operators */ { ym2151_write_reg(chip, i, 0); } } INLINE signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((OP->phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm) { UINT32 p; INT32 i; i = (OP->phase & ~FREQ_MASK) + pm; /*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, OP->phase>>FREQ_SH, pm);*/ p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK]; /*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } #define volume_calc(OP) ((OP)->tl + ((UINT32)(OP)->volume) + (AM & (OP)->AMmask)) INLINE void chan_calc(unsigned int chan) { YM2151Operator *op; unsigned int env; UINT32 AM = 0; if (PSG->Muted[chan]) return; m2 = c1 = c2 = mem = 0; op = &PSG->oper[chan*4]; /* M1 */ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ if (op->ams) AM = PSG->lfa << (op->ams-1); env = volume_calc(op); { INT32 out = op->fb_out_prev + op->fb_out_curr; op->fb_out_prev = op->fb_out_curr; if (!op->connect) /* algorithm 5 */ mem = c1 = c2 = op->fb_out_prev; else /* other algorithms */ *op->connect = op->fb_out_prev; op->fb_out_curr = 0; if (env < ENV_QUIET) { if (!op->fb_shift) out=0; op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); } } env = volume_calc(op+1); /* M2 */ if (env < ENV_QUIET) *(op+1)->connect += op_calc(op+1, env, m2); env = volume_calc(op+2); /* C1 */ if (env < ENV_QUIET) *(op+2)->connect += op_calc(op+2, env, c1); env = volume_calc(op+3); /* C2 */ if (env < ENV_QUIET) chanout[chan] += op_calc(op+3, env, c2); if (chanout[chan] > +16384) chanout[chan] = +16384; else if (chanout[chan] < -16384) chanout[chan] = -16384; /* M1 */ op->mem_value = mem; } INLINE void chan7_calc(void) { YM2151Operator *op; unsigned int env; UINT32 AM = 0; if (PSG->Muted[7]) return; m2 = c1 = c2 = mem = 0; op = &PSG->oper[7*4]; /* M1 */ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ if (op->ams) AM = PSG->lfa << (op->ams-1); env = volume_calc(op); { INT32 out = op->fb_out_prev + op->fb_out_curr; op->fb_out_prev = op->fb_out_curr; if (!op->connect) /* algorithm 5 */ mem = c1 = c2 = op->fb_out_prev; else /* other algorithms */ *op->connect = op->fb_out_prev; op->fb_out_curr = 0; if (env < ENV_QUIET) { if (!op->fb_shift) out=0; op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); } } env = volume_calc(op+1); /* M2 */ if (env < ENV_QUIET) *(op+1)->connect += op_calc(op+1, env, m2); env = volume_calc(op+2); /* C1 */ if (env < ENV_QUIET) *(op+2)->connect += op_calc(op+2, env, c1); env = volume_calc(op+3); /* C2 */ if (PSG->noise & 0x80) { INT32 noiseout; noiseout = 0; if (env < 0x3ff) noiseout = (env ^ 0x3ff) * 2; /* range of the YM2151 noise output is -2044 to 2040 */ chanout[7] += ((PSG->noise_rng&0x10000) ? noiseout: -noiseout); /* bit 16 -> output */ } else { if (env < ENV_QUIET) chanout[7] += op_calc(op+3, env, c2); } if (chanout[7] > +16384) chanout[7] = +16384; else if (chanout[7] < -16384) chanout[7] = -16384; /* M1 */ op->mem_value = mem; } /* The 'rate' is calculated from following formula (example on decay rate): rks = notecode after key scaling (a value from 0 to 31) DR = value written to the chip register rate = 2*DR + rks; (max rate = 2*31+31 = 93) Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15) Two LSBs of the 'rate' above are the value 'x' (the shape type). (eg. '11 2' means that 'rate' is 11*4+2=46) NOTE: A 'sample' in the description below is actually 3 output samples, thats because the Envelope Generator clock is equal to internal_clock/3. Single '-' (minus) character in the diagrams below represents one sample on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3) these 'main' rates: 00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples) 01 x: single '-' = 1024 samples; 02 x: single '-' = 512 samples; 03 x: single '-' = 256 samples; 04 x: single '-' = 128 samples; 05 x: single '-' = 64 samples; 06 x: single '-' = 32 samples; 07 x: single '-' = 16 samples; 08 x: single '-' = 8 samples; 09 x: single '-' = 4 samples; 10 x: single '-' = 2 samples; 11 x: single '-' = 1 sample; (ie. level can change every 1 sample) Shapes for rates 11 x look like this: rate: step: 11 0 01234567 level: 0 -- 1 -- 2 -- 3 -- rate: step: 11 1 01234567 level: 0 -- 1 -- 2 - 3 - 4 -- rate: step: 11 2 01234567 level: 0 -- 1 - 2 - 3 -- 4 - 5 - rate: step: 11 3 01234567 level: 0 -- 1 - 2 - 3 - 4 - 5 - 6 - For rates 12 x, 13 x, 14 x and 15 x output level changes on every sample - this means that the waveform looks like this: (but the level changes by different values on different steps) 12 3 01234567 0 - 2 - 4 - 8 - 10 - 12 - 14 - 18 - 20 - Notes about the timing: ---------------------- 1. Synchronism Output level of each two (or more) voices running at the same 'main' rate (eg 11 0 and 11 1 in the diagram below) will always be changing in sync, even if there're started with some delay. Note that, in the diagram below, the decay phase in channel 0 starts at sample #2, while in channel 1 it starts at sample #6. Anyway, both channels will always change their levels at exactly the same (following) samples. (S - start point of this channel, A-attack phase, D-decay phase): step: 01234567012345670123456 channel 0: -- | -- | - | - | -- | -- | -- | - | - | -- AADDDDDDDDDDDDDDDD S 01234567012345670123456 channel 1: - | - | -- | -- | -- | - | - | -- | -- | -- AADDDDDDDDDDDDDDDD S 01234567012345670123456 2. Shifted (delayed) synchronism Output of each two (or more) voices running at different 'main' rate (9 1, 10 1 and 11 1 in the diagrams below) will always be changing in 'delayed-sync' (even if there're started with some delay as in "1.") Note that the shapes are delayed by exactly one sample per one 'main' rate increment. (Normally one would expect them to start at the same samples.) See diagram below (* - start point of the shape). cycle: 0123456701234567012345670123456701234567012345670123456701234567 rate 09 1 *------- -------- ---- ---- -------- *------- | -------- | ---- | ---- | -------- rate 10 1 | -- | *--- | ---- | -- | -- | ---- | *--- | | ---- | | -- | | <- one step (two samples) delay between 9 1 and 10 1 | -- | | | ----| | *--- | ---- | -- | -- | ---- rate 11 1 | - | -- | *- | -- | - | - | -- | *- | -- | - || <- one step (one sample) delay between 10 1 and 11 1 - || --| *- -- - - -- *- -- - - -- */ INLINE void advance_eg(void) { YM2151Operator *op; unsigned int i; PSG->eg_timer += PSG->eg_timer_add; while (PSG->eg_timer >= PSG->eg_timer_overflow) { PSG->eg_timer -= PSG->eg_timer_overflow; PSG->eg_cnt++; /* envelope generator */ op = &PSG->oper[0]; /* CH 0 M1 */ i = 32; do { switch(op->state) { case EG_ATT: /* attack phase */ if ( !(PSG->eg_cnt & ((1<eg_sh_ar)-1) ) ) { op->volume += (~op->volume * (eg_inc[op->eg_sel_ar + ((PSG->eg_cnt>>op->eg_sh_ar)&7)]) ) >>4; if (op->volume <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ if ( !(PSG->eg_cnt & ((1<eg_sh_d1r)-1) ) ) { op->volume += eg_inc[op->eg_sel_d1r + ((PSG->eg_cnt>>op->eg_sh_d1r)&7)]; if ( op->volume >= op->d1l ) op->state = EG_SUS; } break; case EG_SUS: /* sustain phase */ if ( !(PSG->eg_cnt & ((1<eg_sh_d2r)-1) ) ) { op->volume += eg_inc[op->eg_sel_d2r + ((PSG->eg_cnt>>op->eg_sh_d2r)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } break; case EG_REL: /* release phase */ if ( !(PSG->eg_cnt & ((1<eg_sh_rr)-1) ) ) { op->volume += eg_inc[op->eg_sel_rr + ((PSG->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } break; } op++; i--; }while (i); } } INLINE void advance(void) { YM2151Operator *op; unsigned int i; int a,p; /* LFO */ if (PSG->test&2) PSG->lfo_phase = 0; else { PSG->lfo_timer += PSG->lfo_timer_add; if (PSG->lfo_timer >= PSG->lfo_overflow) { PSG->lfo_timer -= PSG->lfo_overflow; PSG->lfo_counter += PSG->lfo_counter_add; PSG->lfo_phase += (PSG->lfo_counter>>4); PSG->lfo_phase &= 255; PSG->lfo_counter &= 15; } } i = PSG->lfo_phase; /* calculate LFO AM and PM waveform value (all verified on real chip, except for noise algorithm which is impossible to analyse)*/ switch (PSG->lfo_wsel) { case 0: /* saw */ /* AM: 255 down to 0 */ /* PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) */ a = 255 - i; if (i<128) p = i; else p = i - 255; break; case 1: /* square */ /* AM: 255, 0 */ /* PM: 128,-128 (LFP = exactly +PMD, -PMD) */ if (i<128){ a = 255; p = 128; }else{ a = 0; p = -128; } break; case 2: /* triangle */ /* AM: 255 down to 1 step -2; 0 up to 254 step +2 */ /* PM: 0 to 126 step +2, 127 to 1 step -2, 0 to -126 step -2, -127 to -1 step +2*/ if (i<128) a = 255 - (i*2); else a = (i*2) - 256; if (i<64) /* i = 0..63 */ p = i*2; /* 0 to 126 step +2 */ else if (i<128) /* i = 64..127 */ p = 255 - i*2; /* 127 to 1 step -2 */ else if (i<192) /* i = 128..191 */ p = 256 - i*2; /* 0 to -126 step -2*/ else /* i = 192..255 */ p = i*2 - 511; /*-127 to -1 step +2*/ break; case 3: default: /*keep the compiler happy*/ /* random */ /* the real algorithm is unknown !!! We just use a snapshot of data from real chip */ /* AM: range 0 to 255 */ /* PM: range -128 to 127 */ a = lfo_noise_waveform[i]; p = a-128; break; } PSG->lfa = a * PSG->amd / 128; PSG->lfp = p * PSG->pmd / 128; /* The Noise Generator of the YM2151 is 17-bit shift register. * Input to the bit16 is negated (bit0 XOR bit3) (EXNOR). * Output of the register is negated (bit0 XOR bit3). * Simply use bit16 as the noise output. */ PSG->noise_p += PSG->noise_f; i = (PSG->noise_p>>16); /* number of events (shifts of the shift register) */ PSG->noise_p &= 0xffff; while (i) { UINT32 j; j = ( (PSG->noise_rng ^ (PSG->noise_rng>>3) ) & 1) ^ 1; PSG->noise_rng = (j<<16) | (PSG->noise_rng>>1); i--; } /* phase generator */ op = &PSG->oper[0]; /* CH 0 M1 */ i = 8; do { if (op->pms) /* only when phase modulation from LFO is enabled for this channel */ { INT32 mod_ind = PSG->lfp; /* -128..+127 (8bits signed) */ if (op->pms < 6) mod_ind >>= (6 - op->pms); else mod_ind <<= (op->pms - 5); if (mod_ind) { UINT32 kc_channel = op->kc_i + mod_ind; (op+0)->phase += ( (PSG->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; (op+1)->phase += ( (PSG->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; (op+2)->phase += ( (PSG->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; (op+3)->phase += ( (PSG->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; } else /* phase modulation from LFO is equal to zero */ { (op+0)->phase += (op+0)->freq; (op+1)->phase += (op+1)->freq; (op+2)->phase += (op+2)->freq; (op+3)->phase += (op+3)->freq; } } else /* phase modulation from LFO is disabled */ { (op+0)->phase += (op+0)->freq; (op+1)->phase += (op+1)->freq; (op+2)->phase += (op+2)->freq; (op+3)->phase += (op+3)->freq; } op+=4; i--; }while (i); /* CSM is calculated *after* the phase generator calculations (verified on real chip) * CSM keyon line seems to be ORed with the KO line inside of the chip. * The result is that it only works when KO (register 0x08) is off, ie. 0 * * Interesting effect is that when timer A is set to 1023, the KEY ON happens * on every sample, so there is no KEY OFF at all - the result is that * the sound played is the same as after normal KEY ON. */ if (PSG->csm_req) /* CSM KEYON/KEYOFF seqeunce request */ { if (PSG->csm_req==2) /* KEY ON */ { op = &PSG->oper[0]; /* CH 0 M1 */ i = 32; do { KEY_ON(op, 2); op++; i--; }while (i); PSG->csm_req = 1; } else /* KEY OFF */ { op = &PSG->oper[0]; /* CH 0 M1 */ i = 32; do { KEY_OFF(op,~2); op++; i--; }while (i); PSG->csm_req = 0; } } } #if 0 INLINE signed int acc_calc(signed int value) { if (value>=0) { if (value < 0x0200) return (value & ~0); if (value < 0x0400) return (value & ~1); if (value < 0x0800) return (value & ~3); if (value < 0x1000) return (value & ~7); if (value < 0x2000) return (value & ~15); if (value < 0x4000) return (value & ~31); return (value & ~63); } /*else value < 0*/ if (value > -0x0200) return (~abs(value) & ~0); if (value > -0x0400) return (~abs(value) & ~1); if (value > -0x0800) return (~abs(value) & ~3); if (value > -0x1000) return (~abs(value) & ~7); if (value > -0x2000) return (~abs(value) & ~15); if (value > -0x4000) return (~abs(value) & ~31); return (~abs(value) & ~63); } #endif /* first macro saves left and right channels to mono file */ /* second macro saves left and right channels to stereo file */ #if 0 /*MONO*/ #ifdef SAVE_SEPARATE_CHANNELS #define SAVE_SINGLE_CHANNEL(j) \ { signed int pom= -(chanout[j] & PSG->pan[j*2]); \ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ fputc((unsigned short)pom&0xff,sample[j]); \ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ } #else #define SAVE_SINGLE_CHANNEL(j) #endif #else /*STEREO*/ #ifdef SAVE_SEPARATE_CHANNELS #define SAVE_SINGLE_CHANNEL(j) \ { signed int pom = -(chanout[j] & PSG->pan[j*2]); \ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ fputc((unsigned short)pom&0xff,sample[j]); \ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ pom = -(chanout[j] & PSG->pan[j*2+1]); \ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ fputc((unsigned short)pom&0xff,sample[j]); \ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ } #else #define SAVE_SINGLE_CHANNEL(j) #endif #endif /* first macro saves left and right channels to mono file */ /* second macro saves left and right channels to stereo file */ #if 1 /*MONO*/ #ifdef SAVE_SAMPLE #define SAVE_ALL_CHANNELS \ { signed int pom = outl; \ /*pom = acc_calc(pom);*/ \ /*fprintf(sample[8]," %i\n",pom);*/ \ fputc((unsigned short)pom&0xff,sample[8]); \ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ } #else #define SAVE_ALL_CHANNELS #endif #else /*STEREO*/ #ifdef SAVE_SAMPLE #define SAVE_ALL_CHANNELS \ { signed int pom = outl; \ fputc((unsigned short)pom&0xff,sample[8]); \ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ pom = outr; \ fputc((unsigned short)pom&0xff,sample[8]); \ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ } #else #define SAVE_ALL_CHANNELS #endif #endif /* Generate samples for one of the YM2151's * * 'num' is the number of virtual YM2151 * '**buffers' is table of pointers to the buffers: left and right * 'length' is the number of samples that should be generated */ void ym2151_update_one(void *chip, SAMP **buffers, int length) { int i, chn; signed int outl,outr; SAMP *bufL, *bufR; bufL = buffers[0]; bufR = buffers[1]; PSG = (YM2151 *)chip; #ifdef USE_MAME_TIMERS /* ASG 980324 - handled by real timers now */ #else if (PSG->tim_B) { PSG->tim_B_val -= ( length << TIMER_SH ); if (PSG->tim_B_val<=0) { PSG->tim_B_val += PSG->tim_B_tab[ PSG->timer_B_index ]; if ( PSG->irq_enable & 0x08 ) { int oldstate = PSG->status & 3; PSG->status |= 2; //if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1); } } } #endif for (i=0; ipan[0]; outr = chanout[0] & PSG->pan[1]; outl += (chanout[1] & PSG->pan[2]); outr += (chanout[1] & PSG->pan[3]); outl += (chanout[2] & PSG->pan[4]); outr += (chanout[2] & PSG->pan[5]); outl += (chanout[3] & PSG->pan[6]); outr += (chanout[3] & PSG->pan[7]); outl += (chanout[4] & PSG->pan[8]); outr += (chanout[4] & PSG->pan[9]); outl += (chanout[5] & PSG->pan[10]); outr += (chanout[5] & PSG->pan[11]); outl += (chanout[6] & PSG->pan[12]); outr += (chanout[6] & PSG->pan[13]); outl += (chanout[7] & PSG->pan[14]); outr += (chanout[7] & PSG->pan[15]); outl >>= FINAL_SH; outr >>= FINAL_SH; //if (outl > MAXOUT) outl = MAXOUT; // else if (outl < MINOUT) outl = MINOUT; //if (outr > MAXOUT) outr = MAXOUT; // else if (outr < MINOUT) outr = MINOUT; ((SAMP*)bufL)[i] = (SAMP)outl; ((SAMP*)bufR)[i] = (SAMP)outr; SAVE_ALL_CHANNELS #ifdef USE_MAME_TIMERS /* ASG 980324 - handled by real timers now */ #else /* calculate timer A */ if (PSG->tim_A) { PSG->tim_A_val -= ( 1 << TIMER_SH ); if (PSG->tim_A_val <= 0) { PSG->tim_A_val += PSG->tim_A_tab[ PSG->timer_A_index ]; if (PSG->irq_enable & 0x04) { int oldstate = PSG->status & 3; PSG->status |= 1; //if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1); } if (PSG->irq_enable & 0x80) PSG->csm_req = 2; /* request KEY ON / KEY OFF sequence */ } } #endif advance(); } } /*void ym2151_set_irq_handler(void *chip, void(*handler)(int irq)) { YM2151 *PSG = (YM2151 *)chip; PSG->irqhandler = handler; } void ym2151_set_port_write_handler(void *chip, write8_device_func handler) { YM2151 *PSG = (YM2151 *)chip; PSG->porthandler = handler; }*/ void ym2151_set_mutemask(void *chip, UINT32 MuteMask) { YM2151 *PSG = (YM2151 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) PSG->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/ym2151.h ================================================ /* ** File: ym2151.h - header file for software implementation of YM2151 ** FM Operator Type-M(OPM) ** ** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net) ** Some of the optimizing ideas by Tatsuyuki Satoh ** ** Version 2.150 final beta May, 11th 2002 ** ** ** I would like to thank following people for making this project possible: ** ** Beauty Planets - for making a lot of real YM2151 samples and providing ** additional informations about the chip. Also for the time spent making ** the samples and the speed of replying to my endless requests. ** ** Shigeharu Isoda - for general help, for taking time to scan his YM2151 ** Japanese Manual first of all, and answering MANY of my questions. ** ** Nao - for giving me some info about YM2151 and pointing me to Shigeharu. ** Also for creating fmemu (which I still use to test the emulator). ** ** Aaron Giles and Chris Hardy - they made some samples of one of my favourite ** arcade games so I could compare it to my emulator. ** ** Bryan McPhail and Tim (powerjaw) - for making some samples. ** ** Ishmair - for the datasheet and motivation. */ #pragma once /* 16- and 8-bit samples (signed) are supported*/ #define SAMPLE_BITS 16 typedef stream_sample_t SAMP; /* #if (SAMPLE_BITS==16) typedef INT16 SAMP; #endif #if (SAMPLE_BITS==8) typedef signed char SAMP; #endif */ /* ** Initialize YM2151 emulator(s). ** ** 'num' is the number of virtual YM2151's to allocate ** 'clock' is the chip clock in Hz ** 'rate' is sampling rate */ void *ym2151_init(int clock, int rate); /* shutdown the YM2151 emulators*/ void ym2151_shutdown(void *chip); /* reset all chip registers for YM2151 number 'num'*/ void ym2151_reset_chip(void *chip); /* ** Generate samples for one of the YM2151's ** ** 'num' is the number of virtual YM2151 ** '**buffers' is table of pointers to the buffers: left and right ** 'length' is the number of samples that should be generated */ void ym2151_update_one(void *chip, SAMP **buffers, int length); /* write 'v' to register 'r' on YM2151 chip number 'n'*/ void ym2151_write_reg(void *chip, int r, int v); /* read status register on YM2151 chip number 'n'*/ int ym2151_read_status(void *chip); /* set interrupt handler on YM2151 chip number 'n'*/ //void ym2151_set_irq_handler(void *chip, void (*handler)(int irq)); /* set port write handler on YM2151 chip number 'n'*/ //void ym2151_set_port_write_handler(void *chip, write8_device_func handler); /* refresh chip when load state */ //STATE_POSTLOAD( ym2151_postload ); void ym2151_postload(void *param); void ym2151_set_mutemask(void *chip, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/ym2413.c ================================================ /* ** ** File: ym2413.c - software implementation of YM2413 ** FM sound generator type OPLL ** ** Copyright Jarek Burczynski ** ** Version 1.0 ** Features as listed in LSI-212413A2 data sheet: 1. FM Sound Generator for real sound creation. 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds (different tones can be used together in either case). 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones). 4. Built-in DA Converter. 5. Built-in Quartz Oscillator. 6. Built-in Vibrato Oscillator/AM Oscillator 7. TTL Compatible Input. 8. Si-Gate NMOS LSI 9. A single 5V power source. to do: - make sure of the sinus amplitude bits - make sure of the EG resolution bits (looks like the biggest modulation index generated by the modulator is 123, 124 = no modulation) - find proper algorithm for attack phase of EG - tune up instruments ROM - support sample replay in test mode (it is NOT as simple as setting bit 0 in register 0x0f and using register 0x10 for sample data). Which games use this feature ? */ #include #include #include "mamedef.h" #include #include // for memset #include // for NULL //#include "sndintrf.h" #include "ym2413.h" /* output final shift */ #if (SAMPLE_BITS==16) #define FINAL_SH (0) #define MAXOUT (+32767) #define MINOUT (-32768) #else #define FINAL_SH (8) #define MAXOUT (+127) #define MINOUT (-128) #endif #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (EG timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define FREQ_MASK ((1< INLINE signed int acc_calc(signed int value) { if (value>=0) { if (value < 0x0200) return (value & ~0); if (value < 0x0400) return (value & ~1); if (value < 0x0800) return (value & ~3); if (value < 0x1000) return (value & ~7); if (value < 0x2000) return (value & ~15); if (value < 0x4000) return (value & ~31); return (value & ~63); } /*else value < 0*/ if (value > -0x0200) return (~abs(value) & ~0); if (value > -0x0400) return (~abs(value) & ~1); if (value > -0x0800) return (~abs(value) & ~3); if (value > -0x1000) return (~abs(value) & ~7); if (value > -0x2000) return (~abs(value) & ~15); if (value > -0x4000) return (~abs(value) & ~31); return (~abs(value) & ~63); } static FILE *sample[1]; #if 0 /*save to MONO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = acc_calc(mo); \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = mo; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = ro; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #define SAVE_SEPARATE_CHANNEL(j) \ { signed int pom = outchan; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = chip->instvol_r[j]>>4; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif //#define LOG_CYM_FILE 0 //static FILE * cymfile = NULL; typedef struct{ UINT32 ar; /* attack rate: AR<<2 */ UINT32 dr; /* decay rate: DR<<2 */ UINT32 rr; /* release rate:RR<<2 */ UINT8 KSR; /* key scale rate */ UINT8 ksl; /* keyscale level */ UINT8 ksr; /* key scale rate: kcode>>KSR */ UINT8 mul; /* multiple: mul_tab[ML] */ /* Phase Generator */ UINT32 phase; /* frequency counter */ UINT32 freq; /* frequency counter step */ UINT8 fb_shift; /* feedback shift value */ INT32 op1_out[2]; /* slot1 output for feedback */ /* Envelope Generator */ UINT8 eg_type; /* percussive/nonpercussive mode*/ UINT8 state; /* phase type */ UINT32 TL; /* total level: TL << 2 */ INT32 TLL; /* adjusted now TL */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level: sl_tab[SL] */ UINT8 eg_sh_dp; /* (dump state) */ UINT8 eg_sel_dp; /* (dump state) */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT8 eg_sh_dr; /* (decay state) */ UINT8 eg_sel_dr; /* (decay state) */ UINT8 eg_sh_rr; /* (release state for non-perc.)*/ UINT8 eg_sel_rr; /* (release state for non-perc.)*/ UINT8 eg_sh_rs; /* (release state for perc.mode)*/ UINT8 eg_sel_rs; /* (release state for perc.mode)*/ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ /* LFO */ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ /* waveform select */ unsigned int wavetable; } OPLL_SLOT; typedef struct{ OPLL_SLOT SLOT[2]; /* phase generator state */ UINT32 block_fnum; /* block+fnum */ UINT32 fc; /* Freq. freqement base */ UINT32 ksl_base; /* KeyScaleLevel Base step */ UINT8 kcode; /* key code (for key scaling) */ UINT8 sus; /* sus on/off (release speed in percussive mode)*/ UINT8 Muted; } OPLL_CH; /* chip state */ typedef struct { OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/ UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/ UINT8 MuteSpc[5]; /* Mute Special: 5 Rhythm */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ UINT8 rhythm; /* Rhythm mode */ /* LFO */ UINT32 LFO_AM; INT32 LFO_PM; UINT32 lfo_am_cnt; UINT32 lfo_am_inc; UINT32 lfo_pm_cnt; UINT32 lfo_pm_inc; UINT32 noise_rng; /* 23 bit noise shift register */ UINT32 noise_p; /* current noise 'phase' */ UINT32 noise_f; /* current noise period */ /* instrument settings */ /* 0-user instrument 1-15 - fixed instruments 16 -bass drum settings 17,18 - other percussion instruments */ UINT8 inst_tab[19][8]; /* external event callback handlers */ OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */ void * UpdateParam; /* stream update parameter */ UINT32 fn_tab[1024]; /* fnumber->increment counter */ UINT8 address; /* address register */ UINT8 status; /* status flag */ UINT8 VRC7_Mode; int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ signed int output[2]; signed int outchan; } YM2413; /* key scale level */ /* table is 3dB/octave, DV converts this into 6dB/octave */ /* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ #define DV (0.1875/1.0) static const UINT32 ksl_tab[8*16]= { /* OCT 0 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, /* OCT 1 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, /* OCT 2 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, /* OCT 3 */ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, /* OCT 4 */ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, 10.875/DV,11.250/DV,11.625/DV,12.000/DV, /* OCT 5 */ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, 9.000/DV,10.125/DV,10.875/DV,11.625/DV, 12.000/DV,12.750/DV,13.125/DV,13.500/DV, 13.875/DV,14.250/DV,14.625/DV,15.000/DV, /* OCT 6 */ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, 12.000/DV,13.125/DV,13.875/DV,14.625/DV, 15.000/DV,15.750/DV,16.125/DV,16.500/DV, 16.875/DV,17.250/DV,17.625/DV,18.000/DV, /* OCT 7 */ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, 15.000/DV,16.125/DV,16.875/DV,17.625/DV, 18.000/DV,18.750/DV,19.125/DV,19.500/DV, 19.875/DV,20.250/DV,20.625/DV,21.000/DV }; #undef DV /* 0 / 1.5 / 3.0 / 6.0 dB/OCT, confirmed on a real YM2413 (the application manual is incorrect) */ static const UINT32 ksl_shift[4] = { 31, 2, 1, 0 }; /* sustain level table (3dB per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/ #define SC(db) (UINT32) ( db * (1.0/ENV_STEP) ) static const UINT32 sl_tab[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15) }; #undef SC #define RATE_STEPS (8) static const unsigned char eg_inc[15*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ /*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ /*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(13) in this table - it's directly in the code */ static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), /* rates 00-12 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 13 */ O( 4),O( 5),O( 6),O( 7), /* rate 14 */ O( 8),O( 9),O(10),O(11), /* rate 15 */ O(12),O(12),O(12),O(12), /* 16 dummy rates (same as 15 3) */ O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ /*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */ /*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */ #define O(a) (a*1) static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-12 */ O(13),O(13),O(13),O(13), O(12),O(12),O(12),O(12), O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 16 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), }; #undef O /* multiple table */ #define ML 2 static const UINT8 mul_tab[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML }; #undef ML /* TL_TAB_LEN is calculated as: * 11 - sinus amplitude bits (Y axis) * 2 - sinus sign bit (Y axis) * TL_RES_LEN - sinus resolution (X axis) */ #define TL_TAB_LEN (11*2*TL_RES_LEN) static signed int tl_tab[TL_TAB_LEN]; #define ENV_QUIET (TL_TAB_LEN>>5) /* sin waveform table in 'decibel' scale */ /* two waveforms on OPLL type chips */ static unsigned int sin_tab[SIN_LEN * 2]; /* LFO Amplitude Modulation table (verified on real YM3812) 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples Length: 210 elements. Each of the elements has to be repeated exactly 64 times (on 64 consecutive samples). The whole table takes: 64 * 210 = 13440 samples. We use data>>1, until we find what it really is on real chip... */ #define LFO_AM_TAB_ELEMENTS 210 static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; /* LFO Phase Modulation table (verified on real YM2413) */ static const INT8 lfo_pm_table[8*8] = { /* FNUM2/FNUM = 0 00xxxxxx (0x0000) */ 0, 0, 0, 0, 0, 0, 0, 0, /* FNUM2/FNUM = 0 01xxxxxx (0x0040) */ 1, 0, 0, 0,-1, 0, 0, 0, /* FNUM2/FNUM = 0 10xxxxxx (0x0080) */ 2, 1, 0,-1,-2,-1, 0, 1, /* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */ 3, 1, 0,-1,-3,-1, 0, 1, /* FNUM2/FNUM = 1 00xxxxxx (0x0100) */ 4, 2, 0,-2,-4,-2, 0, 2, /* FNUM2/FNUM = 1 01xxxxxx (0x0140) */ 5, 2, 0,-2,-5,-2, 0, 2, /* FNUM2/FNUM = 1 10xxxxxx (0x0180) */ 6, 3, 0,-3,-6,-3, 0, 3, /* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */ 7, 3, 0,-3,-7,-3, 0, 3, }; /* This is not 100% perfect yet but very close */ /* - multi parameters are 100% correct (instruments and drums) - LFO PM and AM enable are 100% correct - waveform DC and DM select are 100% correct */ static const unsigned char table[19][8] = { /* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0 {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1 {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, //2 {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, //3 {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, //4 //{0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, //5 //{0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, //6 {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, //7 {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, //8 {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, //9 //{0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, //A {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, //B {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, //C //{0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, //D {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, //E {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, //F /* drum instruments definitions */ /* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ }; /* lock level of common table */ static int num_lock = 0; /* work table */ #define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) #define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) #define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) #define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) /*INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) val = min; return val; }*/ /* advance LFO to next sample */ INLINE void advance_lfo(YM2413 *chip) { /* LFO */ chip->lfo_am_cnt += chip->lfo_am_inc; if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1; chip->lfo_pm_cnt += chip->lfo_pm_inc; chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7; } /* advance to next sample */ INLINE void advance(YM2413 *chip) { OPLL_CH *CH; OPLL_SLOT *op; unsigned int i; /* Envelope Generator */ chip->eg_timer += chip->eg_timer_add; while (chip->eg_timer >= chip->eg_timer_overflow) { chip->eg_timer -= chip->eg_timer_overflow; chip->eg_cnt++; for (i=0; i<9*2; i++) { CH = &chip->P_CH[i/2]; op = &CH->SLOT[i&1]; switch(op->state) { case EG_DMP: /* dump phase */ /*dump phase is performed by both operators in each channel*/ /*when CARRIER envelope gets down to zero level, ** phases in BOTH opearators are reset (at the same time ?) */ if ( !(chip->eg_cnt & ((1<eg_sh_dp)-1) ) ) { op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_ATT; /* restart Phase Generator */ op->phase = 0; } } break; case EG_ATT: /* attack phase */ if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) { op->volume += (~op->volume * (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) ) >>2; if (op->volume <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) { op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; if ( op->volume >= op->sl ) op->state = EG_SUS; } break; case EG_SUS: /* sustain phase */ /* this is important behaviour: one can change percusive/non-percussive modes on the fly and the chip will remain in sustain phase - verified on real YM3812 */ if(op->eg_type) /* non-percussive mode (sustained tone) */ { /* do nothing */ } else /* percussive mode */ { /* during sustain phase chip adds Release Rate (in percussive mode) */ if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) { op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) op->volume = MAX_ATT_INDEX; } /* else do nothing in sustain phase */ } break; case EG_REL: /* release phase */ /* exclude modulators in melody channels from performing anything in this mode*/ /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */ /*This table shows which operators and on what conditions are allowed to perform EG_REL: (a) - always perform EG_REL (n) - never perform EG_REL (r) - perform EG_REL in Rhythm mode ONLY 0: 0 (n), 1 (a) 1: 2 (n), 3 (a) 2: 4 (n), 5 (a) 3: 6 (n), 7 (a) 4: 8 (n), 9 (a) 5: 10(n), 11(a) 6: 12(r), 13(a) 7: 14(r), 15(a) 8: 16(r), 17(a) */ if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */ { if(op->eg_type) /* non-percussive mode (sustained tone) */ /*this is correct: use RR when SUS = OFF*/ /*and use RS when SUS = ON*/ { if (CH->sus) { if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) { op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } } else { if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) { op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } } } else /* percussive mode */ { if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) { op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } } } break; default: break; } } } for (i=0; i<9*2; i++) { CH = &chip->P_CH[i/2]; op = &CH->SLOT[i&1]; /* Phase Generator */ if(op->vib) { UINT8 block; unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6); unsigned int block_fnum = CH->block_fnum * 2; signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { block_fnum += lfo_fn_table_index_offset; block = (block_fnum&0x1c00) >> 10; op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; } else /* LFO phase modulation = zero */ { op->phase += op->freq; } } else /* LFO phase modulation disabled for this operator */ { op->phase += op->freq; } } /* The Noise Generator of the YM3812 is 23-bit shift register. * Period is equal to 2^23-2 samples. * Register works at sampling frequency of the chip, so output * can change on every sample. * * Output of the register and input to the bit 22 is: * bit0 XOR bit14 XOR bit15 XOR bit22 * * Simply use bit 22 as the noise output. */ chip->noise_p += chip->noise_f; i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ chip->noise_p &= FREQ_MASK; while (i) { /* UINT32 j; j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; chip->noise_rng = (j<<22) | (chip->noise_rng>>1); */ /* Instead of doing all the logic operations above, we use a trick here (and use bit 0 as the noise output). The difference is only that the noise bit changes one step ahead. This doesn't matter since we don't know what is real state of the noise_rng after the reset. */ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; chip->noise_rng >>= 1; i--; } } INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; INT32 i; i = (phase & ~FREQ_MASK) + pm; /*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/ p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)]; /*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } #define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) /* calculate output */ INLINE void chan_calc( YM2413*chip, OPLL_CH *CH ) { OPLL_SLOT *SLOT; unsigned int env; signed int out; signed int phase_modulation; /* phase modulation input (SLOT 2) */ if (CH->Muted) return; /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; phase_modulation = SLOT->op1_out[0]; SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->fb_shift) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); } /* SLOT 2 */ chip->outchan=0; SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) { signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); chip->output[0] += outp; chip->outchan = outp; //chip->output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); } } /* operators used in the rhythm sounds generation process: Envelope Generator: channel operator register number Bass High Snare Tom Top / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal 6 / 0 12 50 70 90 f0 + 6 / 1 15 53 73 93 f3 + 7 / 0 13 51 71 91 f1 + 7 / 1 16 54 74 94 f4 + 8 / 0 14 52 72 92 f2 + 8 / 1 17 55 75 95 f5 + Phase Generator: channel operator register number Bass High Snare Tom Top / slot number MULTIPLE Drum Hat Drum Tom Cymbal 6 / 0 12 30 + 6 / 1 15 33 + 7 / 0 13 31 + + + 7 / 1 16 34 ----- n o t u s e d ----- 8 / 0 14 32 + 8 / 1 17 35 + + channel operator register number Bass High Snare Tom Top number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal 6 12,15 B6 A6 + 7 13,16 B7 A7 + + + 8 14,17 B8 A8 + + + */ /* calculate rhythm */ INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise ) { OPLL_SLOT *SLOT; signed int out; unsigned int env; signed int phase_modulation; /* phase modulation input (SLOT 2) */ /* Bass Drum (verified on real YM3812): - depends on the channel 6 'connect' register: when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - output sample always is multiplied by 2 */ /* SLOT 1 */ SLOT = &CH[6].SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; phase_modulation = SLOT->op1_out[0]; SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->fb_shift) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); } /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET && ! chip->MuteSpc[0] ) chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2; /* Phase generation is based on: */ // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) // SD (16) channel 7->slot 1 // TOM (14) channel 8->slot 1 // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) /* Envelope generation based on: */ // HH channel 7->slot1 // SD channel 7->slot2 // TOM channel 8->slot1 // TOP channel 8->slot2 /* The following formulas can be well optimized. I leave them in direct form for now (in case I've missed something). */ /* High Hat (verified on real YM3812) */ env = volume_calc(SLOT7_1); if( env < ENV_QUIET && ! chip->MuteSpc[4] ) { /* high hat phase generation: phase = d0 or 234 (based on frequency only) phase = 34 or 2d0 (based on noise) */ /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0xd0; */ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e | bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ if (res2) phase = (0x200|(0xd0>>2)); /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ if (phase&0x200) { if (noise) phase = 0x200|0xd0; } else /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ { if (noise) phase = 0xd0>>2; } chip->output[1] += op_calc(phase<wavetable) * 2; } /* Snare Drum (verified on real YM3812) */ env = volume_calc(SLOT7_2); if( env < ENV_QUIET && ! chip->MuteSpc[1] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1; /* when bit8 = 0 phase = 0x100; */ /* when bit8 = 1 phase = 0x200; */ UINT32 phase = bit8 ? 0x200 : 0x100; /* Noise bit XOR'es phase by 0x100 */ /* when noisebit = 0 pass the phase from calculation above */ /* when noisebit = 1 phase ^= 0x100; */ /* in other words: phase ^= (noisebit<<8); */ if (noise) phase ^= 0x100; chip->output[1] += op_calc(phase<wavetable) * 2; } /* Tom Tom (verified on real YM3812) */ env = volume_calc(SLOT8_1); if( env < ENV_QUIET && ! chip->MuteSpc[2] ) chip->output[1] += op_calc(SLOT8_1->phase, env, 0, SLOT8_1->wavetable) * 2; /* Top Cymbal (verified on real YM2413) */ env = volume_calc(SLOT8_2); if( env < ENV_QUIET && ! chip->MuteSpc[3] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0x100; */ /* when res1 = 1 phase = 0x200 | 0x100; */ UINT32 phase = res1 ? 0x300 : 0x100; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e | bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | 0x100; */ if (res2) phase = 0x300; chip->output[1] += op_calc(phase<wavetable) * 2; } } /* generic table initialize */ static int init_tables(void) { signed int i,x; signed int n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; for (i=1; i<11; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } //#if 0 // logerror("tl %04i", x*2); // for (i=0; i<11; i++) // logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); // logerror("\n"); //#endif*/ } /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* waveform 0: standard sinus */ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ /* waveform 1: __ __ */ /* / \____/ \____*/ /* output only first half of the sinus waveform (positive one) */ if (i & (1<<(SIN_BITS-1)) ) sin_tab[1*SIN_LEN+i] = TL_TAB_LEN; else sin_tab[1*SIN_LEN+i] = sin_tab[i]; /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/ } /*#if 0 logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 ); for (i=0; iinstvol_r); state_save_register_device_item(device, 0, chip->eg_cnt); state_save_register_device_item(device, 0, chip->eg_timer); state_save_register_device_item(device, 0, chip->eg_timer_add); state_save_register_device_item(device, 0, chip->eg_timer_overflow); state_save_register_device_item(device, 0, chip->rhythm); state_save_register_device_item(device, 0, chip->lfo_am_cnt); state_save_register_device_item(device, 0, chip->lfo_am_inc); state_save_register_device_item(device, 0, chip->lfo_pm_cnt); state_save_register_device_item(device, 0, chip->lfo_pm_inc); state_save_register_device_item(device, 0, chip->noise_rng); state_save_register_device_item(device, 0, chip->noise_p); state_save_register_device_item(device, 0, chip->noise_f); state_save_register_device_item_2d_array(device, 0, chip->inst_tab); state_save_register_device_item(device, 0, chip->address); state_save_register_device_item(device, 0, chip->status); for (chnum = 0; chnum < ARRAY_LENGTH(chip->P_CH); chnum++) { OPLL_CH *ch = &chip->P_CH[chnum]; int slotnum; state_save_register_device_item(device, chnum, ch->block_fnum); state_save_register_device_item(device, chnum, ch->fc); state_save_register_device_item(device, chnum, ch->ksl_base); state_save_register_device_item(device, chnum, ch->kcode); state_save_register_device_item(device, chnum, ch->sus); for (slotnum = 0; slotnum < ARRAY_LENGTH(ch->SLOT); slotnum++) { OPLL_SLOT *sl = &ch->SLOT[slotnum]; state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ar); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->dr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->rr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->KSR); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ksl); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ksr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->mul); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->phase); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->freq); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->fb_shift); state_save_register_device_item_array(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->op1_out); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_type); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->state); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->TL); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->TLL); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->volume); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->sl); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_dp); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_dp); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_ar); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_ar); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_dr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_dr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_rr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_rr); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_rs); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_rs); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->key); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->AMmask); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->vib); state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->wavetable); } } }*/ //static void OPLL_initalize(YM2413 *chip, const device_config *device) static void OPLL_initalize(YM2413 *chip) { int i; //OPLL_init_save(chip, device); /* frequency base */ chip->freqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0; /*#if 0 chip->rate = (double)chip->clock / 72.0; chip->freqbase = 1.0; logerror("freqbase=%f\n", chip->freqbase); #endif*/ /* make fnumber -> increment counter table */ for( i = 0 ; i < 1024; i++ ) { /* OPLL (YM2413) phase increment counter = 18bit */ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ /*#if 0 logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n", i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); #endif*/ } /*#if 0 for( i=0 ; i < 16 ; i++ ) { logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] ); } for( i=0 ; i < 8 ; i++ ) { int j; logerror("ym2413.c: ksl_tab[oct=%2i] =",i); for (j=0; j<16; j++) { logerror("%08x ", ksl_tab[i*16+j] ); } logerror("\n"); } #endif*/ for(i = 0; i < 9; i ++) chip->P_CH[i].Muted = 0x00; for(i = 0; i < 5; i ++) chip->MuteSpc[i] = 0x00; /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ /* One entry from LFO_AM_TABLE lasts for 64 samples */ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ /* Noise generator: a step takes 1 sample */ chip->noise_f = (1.0 / 1.0) * (1<freqbase; /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/ chip->eg_timer_add = (1<freqbase; chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ } INLINE void KEY_ON(OPLL_SLOT *SLOT, UINT32 key_set) { if( !SLOT->key ) { /* do NOT restart Phase Generator (verified on real YM2413)*/ /* phase -> Dump */ SLOT->state = EG_DMP; } SLOT->key |= key_set; } INLINE void KEY_OFF(OPLL_SLOT *SLOT, UINT32 key_clr) { if( SLOT->key ) { SLOT->key &= key_clr; if( !SLOT->key ) { /* phase -> Release */ if (SLOT->state>EG_REL) SLOT->state = EG_REL; } } } /* update phase increment counter of operator (also update the EG rates if necessary) */ INLINE void CALC_FCSLOT(OPLL_CH *CH,OPLL_SLOT *SLOT) { int ksr; UINT32 SLOT_rs; UINT32 SLOT_dp; /* (frequency) phase increment counter */ SLOT->freq = CH->fc * SLOT->mul; ksr = CH->kcode >> SLOT->KSR; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 16+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } if (CH->sus) SLOT_rs = 16 + (5<<2); else SLOT_rs = 16 + (7<<2); SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ]; SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ]; SLOT_dp = 16 + (13<<2); SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ]; SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ]; } /* set multi,am,vib,EG-TYP,KSR,mul */ INLINE void set_mul(YM2413 *chip,int slot,int v) { OPLL_CH *CH = &chip->P_CH[slot/2]; OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->mul = mul_tab[v&0x0f]; SLOT->KSR = (v&0x10) ? 0 : 2; SLOT->eg_type = (v&0x20); SLOT->vib = (v&0x40); SLOT->AMmask = (v&0x80) ? ~0 : 0; CALC_FCSLOT(CH,SLOT); } /* set ksl, tl */ INLINE void set_ksl_tl(YM2413 *chip,int chan,int v) { OPLL_CH *CH = &chip->P_CH[chan]; /* modulator */ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; SLOT->ksl = ksl_shift[v >> 6]; SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } /* set ksl , waveforms, feedback */ INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v) { OPLL_CH *CH = &chip->P_CH[chan]; /* modulator */ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN; SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0; /*carrier*/ SLOT = &CH->SLOT[SLOT2]; SLOT->ksl = ksl_shift[v >> 6]; SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN; } /* set attack rate & decay rate */ INLINE void set_ar_dr(YM2413 *chip,int slot,int v) { OPLL_CH *CH = &chip->P_CH[slot/2]; OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; if ((SLOT->ar + SLOT->ksr) < 16+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; } /* set sustain level & release rate */ INLINE void set_sl_rr(YM2413 *chip,int slot,int v) { OPLL_CH *CH = &chip->P_CH[slot/2]; OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->sl = sl_tab[ v>>4 ]; SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst ) { set_mul (chip, slot, inst[0]); set_mul (chip, slot+1, inst[1]); set_ksl_tl (chip, chan, inst[2]); set_ksl_wave_fb (chip, chan, inst[3]); set_ar_dr (chip, slot, inst[4]); set_ar_dr (chip, slot+1, inst[5]); set_sl_rr (chip, slot, inst[6]); set_sl_rr (chip, slot+1, inst[7]); } static void update_instrument_zero(YM2413 *chip, UINT8 r ) { UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */ UINT32 chan; UINT32 chan_max; chan_max = 9; if (chip->rhythm & 0x20) chan_max=6; switch(r) { case 0: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_mul (chip, chan*2, inst[0]); } } break; case 1: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_mul (chip, chan*2+1,inst[1]); } } break; case 2: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ksl_tl (chip, chan, inst[2]); } } break; case 3: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ksl_wave_fb (chip, chan, inst[3]); } } break; case 4: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ar_dr (chip, chan*2, inst[4]); } } break; case 5: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ar_dr (chip, chan*2+1,inst[5]); } } break; case 6: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_sl_rr (chip, chan*2, inst[6]); } } break; case 7: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_sl_rr (chip, chan*2+1,inst[7]); } } break; } } /* write a value v to register r on chip chip */ static void OPLLWriteReg(YM2413 *chip, int r, int v) { OPLL_CH *CH; OPLL_SLOT *SLOT; UINT8 *inst; int chan; int slot; /* adjust bus to 8 bits */ r &= 0xff; v &= 0xff; /*if (LOG_CYM_FILE && (cymfile) && (r!=8) ) { fputc( (unsigned char)r, cymfile ); fputc( (unsigned char)v, cymfile ); }*/ switch(r&0xf0) { case 0x00: /* 00-0f:control */ { switch(r&0x0f) { case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */ case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */ case 0x02: /* Key Scale Level, Total Level (modulator) */ case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */ case 0x04: /* Attack, Decay (modulator) */ case 0x05: /* Attack, Decay (carrier) */ case 0x06: /* Sustain, Release (modulator) */ case 0x07: /* Sustain, Release (carrier) */ chip->inst_tab[0][r & 0x07] = v; update_instrument_zero(chip,r&7); break; case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */ { if (chip->VRC7_Mode) break; if(v&0x20) { if ((chip->rhythm&0x20)==0) /*rhythm off to on*/ { //logerror("YM2413: Rhythm mode enable\n"); /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */ chan = 6; inst = &chip->inst_tab[16][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel eight. (High hat and snare drum) */ chan = 7; inst = &chip->inst_tab[17][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */ chan = 8; inst = &chip->inst_tab[18][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } /* BD key on/off */ if(v&0x10) { KEY_ON (&chip->P_CH[6].SLOT[SLOT1], 2); KEY_ON (&chip->P_CH[6].SLOT[SLOT2], 2); } else { KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); } /* HH key on/off */ if(v&0x01) KEY_ON (&chip->P_CH[7].SLOT[SLOT1], 2); else KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); /* SD key on/off */ if(v&0x08) KEY_ON (&chip->P_CH[7].SLOT[SLOT2], 2); else KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); /* TOM key on/off */ if(v&0x04) KEY_ON (&chip->P_CH[8].SLOT[SLOT1], 2); else KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY key on/off */ if(v&0x02) KEY_ON (&chip->P_CH[8].SLOT[SLOT2], 2); else KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); } else { if ((chip->rhythm&0x20)!=0) /*rhythm on to off*/ { //logerror("YM2413: Rhythm mode disable\n"); /* Load instrument settings for channel seven(chan=6 since we're zero based).*/ chan = 6; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel eight.*/ chan = 7; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel nine.*/ chan = 8; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); } /* BD key off */ KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); /* HH key off */ KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); /* SD key off */ KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); /* TOM key off */ KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY off */ KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); } chip->rhythm = v&0x3f; } break; } } break; case 0x10: case 0x20: { int block_fnum; chan = r&0x0f; if (chan >= 9) chan -= 9; /* verified on real YM2413 */ if (chip->VRC7_Mode && chan >= 6) break; CH = &chip->P_CH[chan]; if(r&0x10) { /* 10-18: FNUM 0-7 */ block_fnum = (CH->block_fnum&0x0f00) | v; } else { /* 20-28: suson, keyon, block, FNUM 8 */ block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff); if(v&0x10) { KEY_ON (&CH->SLOT[SLOT1], 1); KEY_ON (&CH->SLOT[SLOT2], 1); } else { KEY_OFF(&CH->SLOT[SLOT1],~1); KEY_OFF(&CH->SLOT[SLOT2],~1); } //if (CH->sus!=(v&0x20)) // logerror("chan=%i sus=%2x\n",chan,v&0x20); CH->sus = v & 0x20; } /* update */ if(CH->block_fnum != block_fnum) { UINT8 block; CH->block_fnum = block_fnum; /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */ CH->kcode = (block_fnum&0x0f00)>>8; CH->ksl_base = ksl_tab[block_fnum>>5]; block_fnum = block_fnum * 2; block = (block_fnum&0x1c00) >> 10; CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); } } break; case 0x30: /* inst 4 MSBs, VOL 4 LSBs */ { UINT8 old_instvol; chan = r&0x0f; if (chan >= 9) chan -= 9; /* verified on real YM2413 */ if (chip->VRC7_Mode && chan >= 6) break; old_instvol = chip->instvol_r[chan]; chip->instvol_r[chan] = v; /* store for later use */ CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT2]; /* carrier */ SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); /*check whether we are in rhythm mode and handle instrument/volume register accordingly*/ if ((chan>=6) && (chip->rhythm&0x20)) { /* we're in rhythm mode*/ if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/ { SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } } else { if ( (old_instvol&0xf0) == (v&0xf0) ) return; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /*#if 0 logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v); logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]); logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]); #endif*/ } } break; default: break; } } /*static TIMER_CALLBACK( cymfile_callback ) { if (cymfile) { fputc( (unsigned char)8, cymfile ); } }*/ /* lock/unlock for common table */ //static int OPLL_LockTable(const device_config *device) static int OPLL_LockTable(void) { num_lock++; if(num_lock>1) return 0; /* first time */ /* allocate total level table (128kb space) */ if( !init_tables() ) { num_lock--; return -1; } /*if (LOG_CYM_FILE) { cymfile = fopen("2413_.cym","wb"); if (cymfile) timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer else logerror("Could not create file 2413_.cym\n"); }*/ return 0; } static void OPLL_UnLockTable(void) { if(num_lock) num_lock--; if(num_lock) return; /* last time */ OPLCloseTable(); /*if (cymfile) fclose (cymfile); cymfile = NULL;*/ } static void OPLLResetChip(YM2413 *chip) { int c,s; int i; chip->eg_timer = 0; chip->eg_cnt = 0; chip->noise_rng = 1; /* noise shift register */ /* setup instruments table */ for (i=0; i<19; i++) { for (c=0; c<8; c++) { chip->inst_tab[i][c] = table[i][c]; } } /* reset with register write */ OPLLWriteReg(chip,0x0f,0); /*test reg*/ for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg(chip,i,0x00); /* reset operator parameters */ for( c = 0 ; c < 9 ; c++ ) { OPLL_CH *CH = &chip->P_CH[c]; for(s = 0 ; s < 2 ; s++ ) { /* wave table */ CH->SLOT[s].wavetable = 0; CH->SLOT[s].state = EG_OFF; CH->SLOT[s].volume = MAX_ATT_INDEX; } } } /* Create one of virtual YM2413 */ /* 'clock' is chip clock in Hz */ /* 'rate' is sampling rate */ //static YM2413 *OPLLCreate(const device_config *device, int clock, int rate) static YM2413 *OPLLCreate(int clock, int rate) { char *ptr; YM2413 *chip; int state_size; if (OPLL_LockTable() == -1) return NULL; /* calculate chip state size */ state_size = sizeof(YM2413); /* allocate memory block */ ptr = (char *)malloc(state_size); if (ptr==NULL) return NULL; /* clear */ memset(ptr,0,state_size); chip = (YM2413 *)ptr; chip->clock = clock; chip->rate = rate; /* init global tables */ OPLL_initalize(chip); /* reset chip */ OPLLResetChip(chip); return chip; } /* Destroy one of virtual YM3812 */ static void OPLLDestroy(YM2413 *chip) { OPLL_UnLockTable(); free(chip); } /* Option handlers */ static void OPLLSetUpdateHandler(YM2413 *chip,OPLL_UPDATEHANDLER UpdateHandler,void * param) { chip->UpdateHandler = UpdateHandler; chip->UpdateParam = param; } /* YM3812 I/O interface */ static void OPLLWrite(YM2413 *chip,int a,int v) { if( !(a&1) ) { /* address port */ chip->address = v & 0xff; } else { /* data port */ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); OPLLWriteReg(chip,chip->address,v); } } static unsigned char OPLLRead(YM2413 *chip,int a) { if( !(a&1) ) { /* status port */ return chip->status; } return 0xff; } //void * ym2413_init(const device_config *device, int clock, int rate) void * ym2413_init(int clock, int rate) { /* emulator create */ return OPLLCreate(clock, rate); } void ym2413_shutdown(void *chip) { YM2413 *OPLL = (YM2413 *)chip; /* emulator shutdown */ OPLLDestroy(OPLL); } void ym2413_reset_chip(void *chip) { YM2413 *OPLL = (YM2413 *)chip; OPLLResetChip(OPLL); } void ym2413_write(void *chip, int a, int v) { YM2413 *OPLL = (YM2413 *)chip; OPLLWrite(OPLL, a, v); } unsigned char ym2413_read(void *chip, int a) { YM2413 *OPLL = (YM2413 *)chip; return OPLLRead(OPLL, a) & 0x03 ; } void ym2413_set_update_handler(void *chip,OPLL_UPDATEHANDLER UpdateHandler,void *param) { YM2413 *OPLL = (YM2413 *)chip; OPLLSetUpdateHandler(OPLL, UpdateHandler, param); } /* ** Generate samples for one of the YM2413's ** ** 'which' is the virtual YM2413 number ** '*buffer' is the output buffer pointer ** 'length' is the number of samples that should be generated */ void ym2413_update_one(void *_chip, SAMP **buffers, int length) { YM2413 *chip = (YM2413 *)_chip; UINT8 rhythm = chip->rhythm&0x20; SAMP *bufMO = buffers[0]; SAMP *bufRO = buffers[1]; int i; for( i=0; i < length ; i++ ) { int mo,ro; chip->output[0] = 0; chip->output[1] = 0; advance_lfo(chip); /* FM part */ chan_calc(chip, &chip->P_CH[0]); //SAVE_SEPARATE_CHANNEL(0); chan_calc(chip, &chip->P_CH[1]); chan_calc(chip, &chip->P_CH[2]); chan_calc(chip, &chip->P_CH[3]); chan_calc(chip, &chip->P_CH[4]); chan_calc(chip, &chip->P_CH[5]); if (! chip->VRC7_Mode) { if(!rhythm) { chan_calc(chip, &chip->P_CH[6]); chan_calc(chip, &chip->P_CH[7]); chan_calc(chip, &chip->P_CH[8]); } else /* Rhythm part */ { rhythm_calc(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); } } mo = chip->output[0]; ro = chip->output[1]; mo >>= FINAL_SH; ro >>= FINAL_SH; /* limit check */ //mo = limit( mo , MAXOUT, MINOUT ); //ro = limit( ro , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE //if (which==0) //{ SAVE_ALL_CHANNELS //} #endif /* store to sound buffer */ bufMO[i] = mo + ro; bufRO[i] = mo + ro; advance(chip); } } void ym2413_set_mutemask(void* chip, UINT32 MuteMask) { YM2413* OPLL = (YM2413*)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 9; CurChn ++) OPLL->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 5; CurChn ++) OPLL->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01; return; } void ym2413_set_chip_mode(void* chip, UINT8 Mode) { // Enable/Disable VRC7 Mode (with only 6 instead of 9 channels and no rhythm part) YM2413* OPLL = (YM2413*)chip; OPLL->VRC7_Mode = Mode; return; } void ym2413_override_patches(void* chip, const UINT8* PatchDump) { YM2413* OPLL = (YM2413*)chip; UINT8 CurIns; UINT8 CurReg; for (CurIns = 0; CurIns < 19; CurIns ++) { for (CurReg = 0; CurReg < 8; CurReg ++) { OPLL->inst_tab[CurIns][CurReg] = PatchDump[CurIns * 8 + CurReg]; } } return; } ================================================ FILE: VGMPlay/chips/ym2413.h ================================================ #pragma once /* select output bits size of output : 8 or 16 */ #define SAMPLE_BITS 16 /* compiler dependence */ //#ifndef __OSDCOMM_H__ //#define __OSDCOMM_H__ /*typedef unsigned char UINT8; // unsigned 8bit typedef unsigned short UINT16; // unsigned 16bit typedef unsigned int UINT32; // unsigned 32bit typedef signed char INT8; // signed 8bit typedef signed short INT16; // signed 16bit typedef signed int INT32; // signed 32bit */ //#endif //typedef INT32 stream_sample_t; typedef stream_sample_t SAMP; /* #if (SAMPLE_BITS==16) typedef INT16 SAMP; #endif #if (SAMPLE_BITS==8) typedef INT8 SAMP; #endif */ //void *ym2413_init(const device_config *device, int clock, int rate); void *ym2413_init(int clock, int rate); void ym2413_shutdown(void *chip); void ym2413_reset_chip(void *chip); void ym2413_write(void *chip, int a, int v); unsigned char ym2413_read(void *chip, int a); void ym2413_update_one(void *chip, SAMP **buffers, int length); typedef void (*OPLL_UPDATEHANDLER)(void *param,int min_interval_us); void ym2413_set_update_handler(void *chip, OPLL_UPDATEHANDLER UpdateHandler, void *param); void ym2413_set_mutemask(void* chip, UINT32 MuteMask); void ym2413_set_chip_mode(void* chip, UINT8 Mode); void ym2413_override_patches(void* chip, const UINT8* PatchDump); ================================================ FILE: VGMPlay/chips/ym2413_opl.c ================================================ #include "mamedef.h" void OPL_RegMapper(UINT16 Reg, UINT8 Data); /* register number to channel number , slot offset */ #define SLOT1 0 #define SLOT2 1 typedef struct{ UINT32 ar; /* attack rate */ UINT32 dr; /* decay rate */ UINT32 rr; /* release rate */ UINT8 KSR; /* key scale rate */ UINT8 ksl; /* keyscale level */ UINT8 mul; /* multiple: mul_tab[ML] */ /* Phase Generator */ //UINT32 phase; /* frequency counter */ //UINT32 freq; /* frequency counter step */ UINT8 fb_shift; /* feedback shift value */ //INT32 op1_out[2]; /* slot1 output for feedback */ /* Envelope Generator */ UINT8 eg_type; /* percussive/nonpercussive mode*/ //UINT8 state; /* phase type */ UINT32 TL; /* total level */ //INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level: sl_tab[SL] */ //UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ /* LFO */ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ /* waveform select */ unsigned int wavetable; } OPLL_SLOT_OPL; typedef struct{ OPLL_SLOT_OPL SLOT[2]; /* phase generator state */ //UINT32 block_fnum; /* block+fnum */ UINT8 fnum_lsb; UINT8 fnum_lmsb; UINT8 fnum_msb; UINT8 block; UINT8 keyon; UINT8 sus; /* sus on/off (release speed in percussive mode)*/ UINT8 fnumlsb_null; } OPLL_CH_OPL; /* chip state */ typedef struct { OPLL_CH_OPL P_CH[9]; /* OPLL chips have 9 channels*/ UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/ UINT8 rhythm; /* Rhythm mode */ /* instrument settings */ /* 0-user instrument 1-15 - fixed instruments 16 -bass drum settings 17,18 - other percussion instruments */ UINT8 inst_tab[19][8]; UINT8 address; /* address register */ UINT8 status; /* status flag */ } YM2413; static const unsigned char table[19][8] = { /* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0 {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1 {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, //2 {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, //3 {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, //4 //{0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, //5 //{0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, //6 {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, //7 {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, //8 {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, //9 //{0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, //A {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, //B {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, //C //{0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, //D {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, //E {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, //F /* drum instruments definitions */ /* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ }; static const unsigned char SLOT2OPL[6 * 3] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0B, 0x09, 0x0C, 0x0A, 0x0D, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15}; #define MAX_CHIPS 0x10 static YM2413 YM2413Data[MAX_CHIPS]; /* set multi,am,vib,EG-TYP,KSR,mul */ INLINE void set_mul(YM2413 *chip,int slot,int v) { OPLL_CH_OPL *CH = &chip->P_CH[slot/2]; OPLL_SLOT_OPL *SLOT = &CH->SLOT[slot&1]; SLOT->mul = v & 0x0F; SLOT->KSR = v & 0x10; SLOT->eg_type = v & 0x20; SLOT->vib = v & 0x40; SLOT->AMmask = v & 0x80; OPL_RegMapper(0x20 | SLOT2OPL[slot], SLOT->AMmask | SLOT->vib | SLOT->eg_type | SLOT->KSR | SLOT->mul); } /* set ksl, tl */ INLINE void set_ksl_tl(YM2413 *chip,int chan,int v) { OPLL_CH_OPL *CH = &chip->P_CH[chan]; OPLL_SLOT_OPL *SLOT = &CH->SLOT[SLOT1]; /* modulator */ // OPLL KSL (0/1.5/3/6) -> OPL KSL (0/3/1.5/6) SLOT->ksl = ((v & 0x40) << 1) | ((v & 0x80) >> 1); // swap bits 6<->7 SLOT->TL = v & 0x3F; OPL_RegMapper(0x40 | SLOT2OPL[chan * 2 + SLOT1], SLOT->ksl | SLOT->TL); } /* set ksl , waveforms, feedback */ INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v) { OPLL_CH_OPL *CH = &chip->P_CH[chan]; OPLL_SLOT_OPL *SLOT; /* modulator */ SLOT = &CH->SLOT[SLOT1]; SLOT->wavetable = (v & 0x08) >> 3; SLOT->fb_shift = (v & 0x07) << 1; OPL_RegMapper(0xE0 | SLOT2OPL[chan * 2 + SLOT1], SLOT->wavetable); OPL_RegMapper(0xC0 | chan, SLOT->fb_shift | 0x30); /*carrier*/ SLOT = &CH->SLOT[SLOT2]; SLOT->ksl = v & 0xC0; SLOT->wavetable = (v & 0x10) >> 4; OPL_RegMapper(0xE0 | SLOT2OPL[chan * 2 + SLOT2], SLOT->wavetable); OPL_RegMapper(0x40 | SLOT2OPL[chan * 2 + SLOT2], SLOT->ksl | SLOT->TL); } /* set attack rate & decay rate */ INLINE void set_ar_dr(YM2413 *chip,int slot,int v) { OPLL_CH_OPL *CH = &chip->P_CH[slot/2]; OPLL_SLOT_OPL *SLOT = &CH->SLOT[slot&1]; SLOT->ar = v & 0xF0; SLOT->dr = v & 0x0F; OPL_RegMapper(0x60 | SLOT2OPL[slot], SLOT->ar | SLOT->dr); } /* set sustain level & release rate */ INLINE void set_sl_rr(YM2413 *chip,int slot,int v) { OPLL_CH_OPL *CH = &chip->P_CH[slot/2]; OPLL_SLOT_OPL *SLOT = &CH->SLOT[slot&1]; SLOT->sl = v & 0xF0; SLOT->rr = v & 0x0F; OPL_RegMapper(0x80 | SLOT2OPL[slot], SLOT->sl | SLOT->rr); } static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst ) { set_mul (chip, slot, inst[0]); set_mul (chip, slot+1, inst[1]); set_ksl_tl (chip, chan, inst[2]); //set_ksl_wave_fb (chip, chan, inst[3]); set_ar_dr (chip, slot, inst[4]); set_ar_dr (chip, slot+1, inst[5]); set_sl_rr (chip, slot, inst[6]); set_sl_rr (chip, slot+1, inst[7]); set_ksl_wave_fb (chip, chan, inst[3]); // called last to avoid a 'click' } static void update_instrument_zero(YM2413 *chip, UINT8 r ) { UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */ UINT32 chan; UINT32 chan_max; chan_max = 9; if (chip->rhythm & 0x20) chan_max=6; switch(r) { case 0: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_mul (chip, chan*2, inst[0]); } } break; case 1: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_mul (chip, chan*2+1,inst[1]); } } break; case 2: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ksl_tl (chip, chan, inst[2]); } } break; case 3: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ksl_wave_fb (chip, chan, inst[3]); } } break; case 4: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ar_dr (chip, chan*2, inst[4]); } } break; case 5: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_ar_dr (chip, chan*2+1,inst[5]); } } break; case 6: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_sl_rr (chip, chan*2, inst[6]); } } break; case 7: for (chan=0; chaninstvol_r[chan]&0xf0)==0) { set_sl_rr (chip, chan*2+1,inst[7]); } } break; } } /* write a value v to register r on chip chip */ static void OPLLWriteReg2OPL(YM2413 *chip, int r, int v) { OPLL_CH_OPL *CH; OPLL_SLOT_OPL *SLOT; UINT8 *inst; int chan; int slot; UINT8 fnln_old; /* adjust bus to 8 bits */ r &= 0xff; v &= 0xff; switch(r&0xf0) { case 0x00: /* 00-0f:control */ { switch(r&0x0f) { case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */ case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */ case 0x02: /* Key Scale Level, Total Level (modulator) */ case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */ case 0x04: /* Attack, Decay (modulator) */ case 0x05: /* Attack, Decay (carrier) */ case 0x06: /* Sustain, Release (modulator) */ case 0x07: /* Sustain, Release (carrier) */ chip->inst_tab[0][r & 0x07] = v; update_instrument_zero(chip,r&7); break; case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */ { if(v&0x20) { if ((chip->rhythm&0x20)==0) /*rhythm off to on*/ { //logerror("YM2413: Rhythm mode enable\n"); /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */ chan = 6; inst = &chip->inst_tab[16][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel eight. (High hat and snare drum) */ chan = 7; inst = &chip->inst_tab[17][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */ SLOT->TL = (chip->instvol_r[chan] >> 4) << 2; /* 7 bits TL (bit 6 = always 0) */ OPL_RegMapper(0x40 | SLOT2OPL[slot], SLOT->ksl | SLOT->TL); /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */ chan = 8; inst = &chip->inst_tab[18][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */ SLOT->TL = (chip->instvol_r[chan] >> 4) << 2; /* 7 bits TL (bit 6 = always 0) */ OPL_RegMapper(0x40 | SLOT2OPL[slot], SLOT->ksl | SLOT->TL); } } else { if (chip->rhythm&0x20) /*rhythm on to off*/ { //logerror("YM2413: Rhythm mode disable\n"); /* Load instrument settings for channel seven(chan=6 since we're zero based).*/ chan = 6; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel eight.*/ chan = 7; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /* Load instrument settings for channel nine.*/ chan = 8; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); } } chip->rhythm = v&0x3f; OPL_RegMapper(0xBD, v & 0xFF); } break; } } break; case 0x10: case 0x20: { chan = r&0x0f; if (chan >= 9) chan -= 9; /* verified on real YM2413 */ CH = &chip->P_CH[chan]; // ((FM_OPL_Regs[0x10] & 0x00ff) | ((FM_OPL_Regs[0x20] & 0x01) << 8)) << 1; if(r&0x10) { /* 10-18: FNUM 0-7 */ CH->fnum_lsb = (v & 0x7F) << 1; if (! CH->fnumlsb_null) OPL_RegMapper(0xA0 | chan, CH->fnum_lsb); if (CH->fnum_lmsb != ((v & 0x80) >> 7)) { CH->fnum_lmsb = (v & 0x80) >> 7; OPL_RegMapper(0xB0 | chan, CH->fnum_lmsb | CH->fnum_msb | CH->block | CH->keyon | CH->sus); } } else { /* 20-28: suson, keyon, block, FNUM 8 */ //block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff); CH->fnum_msb = (v & 0x01) << 1; CH->block = (v & 0x0E) << 1; CH->keyon = (v & 0x10) << 1; // fixes a behaviour that causes a rumble because of Reg A0 being not 0 fnln_old = CH->fnumlsb_null; CH->fnumlsb_null = (! CH->fnum_msb && ! CH->block); if (CH->fnumlsb_null != fnln_old) { OPL_RegMapper(0xA0 | chan, CH->fnumlsb_null ? 0x00 : CH->fnum_lsb); } CH->sus = (v & 0x20) << 1; // actually n/a on real OPL OPL_RegMapper(0xB0 | chan, CH->fnum_lmsb | CH->fnum_msb | CH->block | CH->keyon | CH->sus); } } break; case 0x30: /* inst 4 MSBs, VOL 4 LSBs */ { UINT8 old_instvol; chan = r&0x0f; if (chan >= 9) chan -= 9; /* verified on real YM2413 */ old_instvol = chip->instvol_r[chan]; chip->instvol_r[chan] = v; /* store for later use */ CH = &chip->P_CH[chan]; SLOT = &CH->SLOT[SLOT2]; /* carrier */ SLOT->TL = (chip->instvol_r[chan] & 0x0F) << 2; //SLOT->TL = (chip->instvol_r[chan] & 0x0F) << 0; OPL_RegMapper(0x40 | SLOT2OPL[chan * 2 + SLOT2], SLOT->ksl | SLOT->TL); /*check wether we are in rhythm mode and handle instrument/volume register accordingly*/ if ((chan>=6) && (chip->rhythm&0x20)) { /* we're in rhythm mode*/ if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/ { SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */ SLOT->TL = (chip->instvol_r[chan] >> 4) << 2; OPL_RegMapper(0x40 | SLOT2OPL[chan * 2 + SLOT1], SLOT->ksl | SLOT->TL); } } else { if ( (old_instvol&0xf0) == (v&0xf0) ) return; inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; slot = chan*2; load_instrument(chip, chan, slot, inst); /*#if 0 logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v); logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]); logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]); #endif*/ } } break; default: break; } } static void OPLLWrite(YM2413 *chip,int a,int v) { if( !(a&1) ) { /* address port */ chip->address = v & 0xff; } else { /* data port */ OPLLWriteReg2OPL(chip,chip->address,v); } } static void OPLLResetChip(YM2413 *chip) { int c,s; int i; // setup instruments table for (i=0; i<19; i++) { for (c=0; c<8; c++) { chip->inst_tab[i][c] = table[i][c]; } } // reset with register write OPLLWriteReg2OPL(chip,0x0f,0); //test reg for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg2OPL(chip,i,0x00); chip->rhythm = 0; OPLLWriteReg2OPL(chip,0x0E,0x00); // reset operator parameters for( c = 0 ; c < 9 ; c++ ) { OPLL_CH_OPL *CH = &chip->P_CH[c]; chip->instvol_r[c] = 0; CH->fnum_lsb = 0; CH->fnum_msb = 0; CH->block = 0; CH->keyon = 0; CH->sus = 0; CH->fnumlsb_null = 0; for(s = 0 ; s < 2 ; s++ ) { CH->SLOT[s].ar = 0; CH->SLOT[s].dr = 0; CH->SLOT[s].rr = 0; CH->SLOT[s].KSR = 0; CH->SLOT[s].ksl = 0; CH->SLOT[s].mul = 0; CH->SLOT[s].fb_shift = 0; CH->SLOT[s].eg_type = 0; CH->SLOT[s].TL = 0; CH->SLOT[s].sl = 0; CH->SLOT[s].AMmask = 0; CH->SLOT[s].vib = 0; // wave table CH->SLOT[s].wavetable = 0; } } } void start_ym2413_opl(UINT8 ChipID) { YM2413 *chip; if (ChipID >= MAX_CHIPS) return; chip = &YM2413Data[ChipID]; OPLLResetChip(chip); return; } void ym2413_w_opl(UINT8 ChipID, offs_t offset, UINT8 data) { YM2413 *chip = &YM2413Data[ChipID]; OPLLWrite(chip, offset & 1, data); } ================================================ FILE: VGMPlay/chips/ym2413hd.c ================================================ /*-----------------------------------*/ /* YM-2413 emulator using OPL */ /* (c) by Hiromitsu Shioya */ /* Modified by Omar Cornut */ /*-----------------------------------*/ // added |0x30 to all Reg 0xC0 writes (else it doesn't sound on OPL3) void OPL_RegMapper(unsigned short int Reg, unsigned char Data); //#include "shared.h" #define MEKA_ERR_OK (0) typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword; #define UBYTE unsigned char // Unsigned byte ( $00 to $FF ) #define BYTE signed char // Signed byte ( -$80 to $7F ) #define UWORD unsigned short // Unsigned word ( $0000 to $FFFF ) #define ULONG unsigned int // Unsigned long ( $00000000 to $FFFFFFFF) #define CPTR unsigned int // 32-bit address pointer format #define FALSE 0 #define TRUE 1 #include "ym2413hd.h" #define Sound_OPL_Write OPL_RegMapper //----------------------------------------------------------------------------- // YM-2413 Instruments Table //----------------------------------------------------------------------------- // FIXME: Currently placed outside of the MEKA_OPL test, as it is being // used by the FM Editor. //----------------------------------------------------------------------------- static FM_OPL_Patch FM_OPL_Patchs[YM2413_INSTRUMENTS] = { /*KSL | MUL | AR | SL | EG | DR | RR | TL | KSR | WAVE| FB/CN */ { 1, 0, 1, 1,15, 8, 9, 8, 0, 0, 1, 4,13,14,50,63, 0, 1, 0, 0, 4, 0,}, /* 0: User voice */ { 0, 0, 1, 2,15,15,14,15, 1, 1, 0, 0, 8, 7,35,63, 1, 0, 0, 0, 7, 0,}, /* 1: Violin */ { 1, 0, 3, 1,15, 7, 7, 9, 0, 1, 2, 4, 9,12,40,63, 0, 1, 0, 0, 5, 0,}, /* 2: Guitar */ { 1, 0, 1, 1,15,13, 7, 7, 0, 0, 2, 2, 7,11,50,63, 0, 1, 0, 0, 4, 0,}, /* 3: Piano */ { 1, 0, 4, 1,12,10,15,14, 1, 1, 0, 1, 8, 8,27,63, 0, 0, 0, 0, 5, 0,}, /* 4: Flute */ { 1, 0, 4, 1,13,10,10,14, 1, 1, 2, 1, 7, 8,44,63, 0, 0, 0, 0, 3, 0,}, /* 5: Clarinet */ { 2, 0, 1, 2,15,15,15,14, 1, 1, 1, 1, 4,14,58,63, 1, 0, 0, 0, 1, 0,}, /* 6: Oboe */ { 0, 0, 1, 1,15,15,12, 9, 1, 1, 1, 1,14,11,40,63, 0, 0, 0, 0, 6, 0,}, /* 7: Trumpet */ { 1, 0, 5, 1,15,15,15,15, 1, 1, 0, 0,15,13,32,63, 0, 0, 0, 0, 0, 0,}, /* 8: Organ */ { 0, 0, 0, 1, 8,15, 7, 9, 1, 1, 5, 1, 9,13,43,63, 0, 0, 0, 0, 6, 0,}, /* 9: Tube */ { 2, 0, 1, 1, 5, 6, 5,10, 1, 1, 3, 2,14,15,63,63, 1, 0, 1, 0, 2, 0,}, /* 10: Synthesizer */ { 1, 0, 3, 1,13,10, 9,11, 0, 1, 4, 3, 4,14,48,63, 0, 0, 1, 0, 7, 0,}, /* 11: Harpsicode */ { 2, 0,11, 1,15, 9,11,10, 1, 1, 6, 3,13,12,36,63, 0, 0, 0, 0, 7, 0,}, /* 12: Vibraphone */ { 2, 0, 1, 3, 7,15,12,14, 1, 1, 7, 1, 7, 8,45,63, 0, 0, 0, 0, 7, 0,}, /* 13: Synth bass */ { 0, 0, 1, 1,15,14,11,13, 1, 1, 3, 2, 6, 9,42,63, 0, 0, 0, 0, 3, 0,}, /* 14: Wood bass */ { 0, 0, 1, 3,15,14,11,13, 1, 1, 3, 2, 6, 9,55,63, 0, 0, 0, 0, 3, 0,}, /* 15: Electric bass */ }; static int vcref[9] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int vlref[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static word fref[9]; //----------------------------------------------------------------------------- //#ifdef MEKA_OPL //----------------------------------------------------------------------------- // Variables //----------------------------------------------------------------------------- static int FM_OPL_Initialized = FALSE; /*t_fm_unit_interface FM_OPL_Interface = { "YM-2413 OPL Emulator", "Hiromitsu Shioya & Omar Cornut", FM_OPL_Reset, FM_OPL_Write, FM_OPL_Mute, FM_OPL_Resume, FM_OPL_Regenerate };*/ //----------------------------------------------------------------------------- // Delayed writes stuff //----------------------------------------------------------------------------- //#define DELAY_BUFFER_MAX (60*3) /* max 3second */ //#define DELAY_STOCK_MAX (DELAY_BUFFER_MAX * 64) /*typedef struct delay_rec { BYTE reg; BYTE data; } DelayRec;*/ /*unsigned int fm_delay_size = 6; unsigned int fm_write_d, fm_update_d; unsigned int delay_point[DELAY_BUFFER_MAX]; unsigned int w_delay; DelayRec delay_chip[DELAY_STOCK_MAX];*/ static int fmVol[YM2413_VOLUME_STEPS] = { #if 0 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x14, 0x1c, 0x24, 0x2c, 0x34, 0x3f #else 0x00, 0x03, 0x06, 0x03, 0x09, 0x0c, 0x0f, 0x12, 0x15, 0x18, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a #endif }; //----------------------------------------------------------------------------- // FM_OPL_Init () // Initialize emulation (FIXME: not really necessary this way ?) //----------------------------------------------------------------------------- int FM_OPL_Init (void *userdata /* unused */) { //ConsolePrintf ("%s ", Msg_Get (MSG_Sound_Init_YM2413_OPL)); FM_OPL_Reset (); FM_OPL_Initialized = TRUE; //ConsolePrintf ("%s\n", Msg_Get (MSG_Ok)); return (MEKA_ERR_OK); } //----------------------------------------------------------------------------- // FM_OPL_Close() // Close emulation (actually only mute) //----------------------------------------------------------------------------- void FM_OPL_Close (void) { if (FM_OPL_Initialized) { //FM_OPL_Mute (); // FM_OPL_Reset (); } } //----------------------------------------------------------------------------- // FM_OPL_Active() // Active this engine as being the current FM interface to use //----------------------------------------------------------------------------- /*void FM_OPL_Active (void) { FM_Set_Interface (&FM_OPL_Interface, FM_OPL_Regs); }*/ //----------------------------------------------------------------------------- // FM_OPL_Reset() // Reset emulated YM-2413 //----------------------------------------------------------------------------- void FM_OPL_Reset (void) { int i; // printf("...OPL addr= %x\n", Sound.OPL_Address); // Clear all OPL registers // already done by global Chip Mapper /*for (i = 0; i < 255; i++) Sound_OPL_Write (i, 0x00); Sound_OPL_Write (0x01, 0x20);*/ // Sound_OPL_Write (0xBD, 0x28); // Set all YM-2413 registers to zero for (i = 0; i < YM2413_REGISTERS; i++) FM_OPL_Regs[i] = 0x00; // Initialize delayed update system /*fm_write_d = fm_update_d = 0; w_delay = 0; for (i = 0; i < DELAY_BUFFER_MAX; i++) delay_point[i] = 0;*/ // Initialize volume & voices for (i = 0; i < 9; i++) { vcref[i] = 0xff; vlref[i] = 0; } Sound_OPL_Write (0xbd, 0x00); for (i = 0; i < 3; i++) { Sound_OPL_Write(0x20 + i, 0x01); Sound_OPL_Write(0x23 + i, 0x01); Sound_OPL_Write(0x40 + i, 0x3f); Sound_OPL_Write(0x43 + i, 0x3f); Sound_OPL_Write(0x60 + i, 0xf0); Sound_OPL_Write(0x63 + i, 0xf0); Sound_OPL_Write(0x80 + i, 0xff); Sound_OPL_Write(0x83 + i, 0xff); Sound_OPL_Write(0xc0 + i, 0x00 | 0x30); Sound_OPL_Write(0x28 + i, 0x01); Sound_OPL_Write(0x2b + i, 0x01); Sound_OPL_Write(0x48 + i, 0x3f); Sound_OPL_Write(0x4b + i, 0x3f); Sound_OPL_Write(0x68 + i, 0xf0); Sound_OPL_Write(0x6b + i, 0xf0); Sound_OPL_Write(0x88 + i, 0xff); Sound_OPL_Write(0x8b + i, 0xff); Sound_OPL_Write(0xc3 + i, 0x00 | 0x30); Sound_OPL_Write(0x30 + i, 0x21); Sound_OPL_Write(0x33 + i, 0x21); Sound_OPL_Write(0x50 + i, 0x3f); Sound_OPL_Write(0x53 + i, 0x3f); Sound_OPL_Write(0x70 + i, 0xf0); Sound_OPL_Write(0x73 + i, 0xf0); Sound_OPL_Write(0x90 + i, 0xf0); Sound_OPL_Write(0x93 + i, 0xf0); Sound_OPL_Write(0xc6 + i, 0x00 | 0x30); } // Sound_OPL_Write (0x01, 0x20); } //----------------------------------------------------------------------------- // FM_OPL_Mute() // Mute FM Sound by setting all OPL volumes to zero //----------------------------------------------------------------------------- /*void FM_OPL_Mute (void) { int i; // Msg (MSGT_DEBUG, __FUNCTION__); for (i = 0; i < 9; i++) { // Clear bit 5 of all channels (makes voice silent) Sound_OPL_Write (0xb0 + i, 0x00); // //int vl = vlref[i]; //FM_OPL_Set_Voice (i, 0xff, 0x3f); // All sounds off. //vlref[i] = vl; // } }*/ //----------------------------------------------------------------------------- // FM_OPL_Resume() // Resume FM Sound AFTER muting //----------------------------------------------------------------------------- /*void FM_OPL_Resume (void) { int i, oldv; int n_channels; // 6 or 9 channels ----------------------------------------------------------- n_channels = (FM_OPL_Rhythm_Mode ? 6 : 9); for (i = 0; i < n_channels; i++) { oldv = vcref[i]; vcref[i] = 0xff; FM_OPL_Set_Voice (i, oldv, vlref[i]); Sound_OPL_Write (0xa0 + i, fref[i] & 0xff); Sound_OPL_Write (0xb0 + i, ((fref[i] >> 8) & 0x1f) | ((FM_OPL_Regs[0x20 + i] & 0x10) << 1)); } // 3 Rythmic channels (if enabled) ------------------------------------------- if (FM_OPL_Rhythm_Mode) { for (i = 6; i < 9; i++) { Sound_OPL_Write (0xa0 + i, fref[i] & 0xff); Sound_OPL_Write (0xb0 + i, (fref[i] >> 8) & 0x1f); } Sound_OPL_Write (0x53, fmVol[FM_OPL_Regs[0x36] & 0x0f]); // Bass drum Sound_OPL_Write (0x51, fmVol[(FM_OPL_Regs[0x37] >> 4) & 0x0f]); // Hi-hat Sound_OPL_Write (0x54, fmVol[FM_OPL_Regs[0x37] & 0x0f]); // Snare Sound_OPL_Write (0x52, fmVol[(FM_OPL_Regs[0x38] >> 4) & 0x0f]); // Tomtom Sound_OPL_Write (0x55, fmVol[FM_OPL_Regs[0x38] & 0x0f]); // Top Cymbal } }*/ //----------------------------------------------------------------------------- // FM_OPL_Regenerate() // Regenerate various data from YM-2413 registers // This is called after a state loading //----------------------------------------------------------------------------- /*void FM_OPL_Regenerate (void) { int i; // Initialize delayed update system fm_write_d = fm_update_d = 0; w_delay = 0; for (i = 0; i < DELAY_BUFFER_MAX; i++) delay_point[i] = 0; // Rewrite all registers for (i = 0; i < YM2413_REGISTERS; i++) { FM_OPL_Write (i, FM_OPL_Regs[i]); } }*/ /*******************************************************************/ /* FM voice set */ /* note : YM3812 register map */ /* */ /* 9 voices map */ /* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | */ /* ---------------------------------------------- */ /* | 00 | 01 | 02 | 08 | 09 | 0a | 10 | 11 | 12 | */ /* | 03 | 04 | 05 | 0b | 0c | 0d | 13 | 14 | 15 | */ /* */ /* 6 voices & 5 rhythm map */ /* | 0 | 1 | 2 | 3 | 4 | 5 || BD | HH | TOM| SD | CYM| */ /* --------------------------------------------------------- */ /* | 00 | 01 | 02 | 08 | 09 | 0a || 10 | 11 | 12 | 14 | 15 | */ /* | 03 | 04 | 05 | 0b | 0c | 0d || 13 | | | | | */ /* */ /* BD=Bass drum, HH=HiHat, TOM=Tomtom, SD=Snare drum */ /* CYM=Cymbal */ /*******************************************************************/ void FM_OPL_Set_Voice (int R, int V, int VL) { int R2; FM_OPL_Patch *patch; R2 = R; R = (R % 3) + ((R / 3) * 0x08); V &= 0xff; patch = &FM_OPL_Patchs[vcref[R2]]; vlref[R2] = VL & 0x3f; if (V != 0xff) { if ((V == 0) || (vcref[R2] != V)) { vcref[R2] = V; patch = &FM_OPL_Patchs[V]; Sound_OPL_Write(0x20 + R, (patch->MS <<5) | (patch->MEV<<4) | patch->MML); Sound_OPL_Write(0x60 + R, (patch->MA <<4) | patch->MD); Sound_OPL_Write(0x80 + R, (patch->MSL<<4) | patch->MR); Sound_OPL_Write(0xe0 + R, patch->MW); Sound_OPL_Write(0x23 + R, (patch->CS <<5) | (patch->CEV<<4) | patch->CML); Sound_OPL_Write(0x63 + R, (patch->CA <<4) | patch->CD); Sound_OPL_Write(0x83 + R, (patch->CSL<<4) | patch->CR); Sound_OPL_Write(0xe3 + R, patch->CW); Sound_OPL_Write(0xc0 + R2, (patch->FB<<1) | patch->CON | 0x30); Sound_OPL_Write(0x40 + R, (0x3f - patch->MTL) | (patch->MKS<<6)); } } Sound_OPL_Write(0x43 + R, (patch->CKS << 6) | vlref[R2]); } /***********************************************/ /* check use user-voice & set */ /***********************************************/ void FM_OPL_Set_User_Voice (void) { int c, lpmax; lpmax = (FM_OPL_Rhythm_Mode) ? 6 : 9; for (c = 0; c < lpmax; c ++) { if (!vcref[c]) FM_OPL_Set_Voice (c, 0, vlref[c]); // if( !vcref[c] ) vcref[c] = 0xff; /* set direct write */ } } //----------------------------------------------------------------------------- // FM_OPL_Update() // Update audio stream with the delayed writes // This is periodically (~1/60th second) called by the sound engine //----------------------------------------------------------------------------- /*void FM_OPL_Update (void) { unsigned int i; unsigned int start, end, stock; if (fm_delay_size) { i = fm_write_d - fm_update_d; fm_write_d++; if (i < fm_delay_size) { delay_point[(fm_write_d+1) % DELAY_BUFFER_MAX] = delay_point[fm_write_d]; return; } start = delay_point[fm_update_d % DELAY_BUFFER_MAX]; fm_update_d++; end = delay_point[fm_update_d % DELAY_BUFFER_MAX]; stock = fm_delay_size; fm_delay_size = 0; // Message( MESSAGE_DEBUG, " now delay write %d %d (%d)[%d]", start, end, end - start, fm_update_d ); // printf( " now delay write %d %d (%d)[%d]\n", start, end, end - start, fm_update_d ); if (end >= start) { for (i = start; i < end; i++) FM_OPL_Write (delay_chip[i % DELAY_STOCK_MAX].reg & 0x3f, delay_chip[i % DELAY_STOCK_MAX].data & 0xff); } #if 0 else { printf(" ??? %d %d (%d)[%d]\n", start, end, end - start, fm_update_d); } #endif fm_delay_size = stock; fm_write_d %= DELAY_BUFFER_MAX; fm_update_d %= DELAY_BUFFER_MAX; delay_point[(fm_write_d + 1) % DELAY_BUFFER_MAX] = delay_point[fm_write_d]; } }*/ //----------------------------------------------------------------------------- // FM_OPL_Write() // Port write to the YM-2413 //----------------------------------------------------------------------------- void FM_OPL_Write (int R, int V) { int Freq, c; int F; int Previous_Register; FM_OPL_Patch *patch = &FM_OPL_Patchs[0]; // FIXME: is the first test necessary ? // Note: the second is necessary, when an FM_OPL_Update() is called by the // sound engine, while the FM emulator has already been switched to another! /*if (FM_OPL_Initialized == FALSE || Sound.FM_Emulator_Current != FM_EMULATOR_YM2413HD) { FM_OPL_Regs[R] = V; return; } if (fm_delay_size) { delay_chip[w_delay].reg = (BYTE)R; delay_chip[w_delay].data = (BYTE)V; w_delay = (w_delay + 1) % DELAY_STOCK_MAX; delay_point[(fm_write_d+1) % DELAY_BUFFER_MAX] = w_delay; // printf( "%d = %d\n", fm_write_d+1, delay_point[(fm_write_d+1)%DELAY_BUFFER_MAX] ); return; }*/ Previous_Register = FM_OPL_Regs[R]; FM_OPL_Regs[R] = V; switch (R) { /**** user voice set ****/ case 0x00: patch->MML = V&0x0f; patch->MS = (V>>5)&0x01; patch->MEV = (V>>4)&0x01; FM_OPL_Set_User_Voice (); return; case 0x01: patch->CML = V&0x0f; patch->CS = (V>>5)&0x01; patch->CEV = (V>>4)&0x01; FM_OPL_Set_User_Voice (); return; case 0x02: patch->MTL = 0x3f - (V&0x3f); patch->MKS = (V>>6)&0x03; FM_OPL_Set_User_Voice (); return; case 0x03: patch->FB = V&0x07; patch->MW = (V>>4)&0x01; patch->CW = (V>>3)&0x01; patch->MKS = (V>>6)&0x03; FM_OPL_Set_User_Voice (); return; case 0x04: patch->MA = (V>>4)&0x0f; patch->MD = V&0x0f; FM_OPL_Set_User_Voice (); return; case 0x05: patch->CA = (V>>4)&0x0f; patch->CD = V&0x0f; FM_OPL_Set_User_Voice (); return; case 0x06: patch->MSL = (V>>4)&0x0f; patch->MR = V&0x0f; FM_OPL_Set_User_Voice (); return; case 0x07: patch->CSL = (V>>4)&0x0f; patch->CR = V&0x0f; FM_OPL_Set_User_Voice (); return; case 0x0e: /**** rhythm select ****/ if (FM_OPL_Rhythm_Mode) { // If it was previously desactived, setup OPL instruments if ((Previous_Register & 0x20) == 0x00) { for (c = 0; c < 3; c++) { Sound_OPL_Write (0xf0 + c, 0x00); Sound_OPL_Write (0xf3 + c, 0x00); Sound_OPL_Write (0xc6 + c, 0x00 | 0x30); } Sound_OPL_Write (0x70, 0xf0); Sound_OPL_Write (0x73, 0xf0); Sound_OPL_Write (0x71, 0xf0); Sound_OPL_Write (0x74, 0xf0); Sound_OPL_Write (0x72, 0xf0); Sound_OPL_Write (0x75, 0xf0); Sound_OPL_Write (0x30, 0x00); Sound_OPL_Write (0x33, 0x01); /* bass */ Sound_OPL_Write (0x31, 0x01); Sound_OPL_Write (0x34, 0x01); /* hi-hat/snare */ Sound_OPL_Write (0x32, 0x01); Sound_OPL_Write (0x35, 0x01); /* tom/cymbal */ if (!(FM_OPL_Regs[0x26] & 0x20)) { Sound_OPL_Write (0x90, 0x07); Sound_OPL_Write (0x93, 0x07); } else { Sound_OPL_Write (0x90, 0x04); Sound_OPL_Write (0x93, 0x04); } if (!(FM_OPL_Regs[0x27] & 0x20)) { Sound_OPL_Write (0x91, 0x07); Sound_OPL_Write (0x94, 0x07); } else { Sound_OPL_Write (0x91, 0x04); Sound_OPL_Write (0x94, 0x04); } if (!(FM_OPL_Regs[0x28] & 0x20)) { Sound_OPL_Write (0x92, 0x06); Sound_OPL_Write (0x95, 0x06); } else { Sound_OPL_Write (0x92, 0x04); Sound_OPL_Write (0x95, 0x04); } Sound_OPL_Write (0x50, 0x0f); Sound_OPL_Write (0x53, fmVol[FM_OPL_Regs[0x36]&0x0f] >> 1); Sound_OPL_Write (0x51, fmVol[(FM_OPL_Regs[0x37]>>4)&0x0f]); Sound_OPL_Write (0x54, fmVol[FM_OPL_Regs[0x37]&0x0f]); Sound_OPL_Write (0x52, fmVol[(FM_OPL_Regs[0x38]>>4)&0x0f]); Sound_OPL_Write (0x55, fmVol[FM_OPL_Regs[0x38]&0x0f]); Sound_OPL_Write (0xa6, fref[6]&0xff); Sound_OPL_Write (0xb6, (fref[6]>>8) & 0x1f); Sound_OPL_Write (0xa7, fref[7]&0xff); Sound_OPL_Write (0xb7, (fref[7]>>8) & 0x1f); Sound_OPL_Write (0xa8, fref[8]&0xff); Sound_OPL_Write (0xb8, (fref[8]>>8) & 0x1f); } } Sound_OPL_Write (0xbd, V); return; } if (R >= 0x10 && R <= 0x28) { /**** Freq. set ****/ F = R & 0x0f; Freq = (((int)FM_OPL_Regs[0x10+F] & 0x00ff) | (((int)FM_OPL_Regs[0x20+F] & 0x01) << 8)) << 1; c = (FM_OPL_Regs[0x20+F] >> 1) & 0x0007; if (FM_OPL_Regs[0x20+F] & 0x10) fref[F] = Freq | (c << 10) | 0x2000; else fref[F] = Freq | (c << 10); Sound_OPL_Write (0xa0+F, fref[F] & 0xff); Sound_OPL_Write (0xb0+F, (fref[F] >> 8) & 0xff); return; } if (R >= 0x30 && R < 0x36) { /**** set voice&volume (ch0-ch5) ****/ FM_OPL_Set_Voice (R & 0x0f, (V >> 4) & 0x0f, fmVol[V & 0x0f]); return; } if (R >= 0x36 && R <= 0x38) { /**** set voice & volume (ch6-8 or rhythm) ****/ if (!FM_OPL_Rhythm_Mode) { FM_OPL_Set_Voice (R & 0x0f, (V >> 4) & 0x0f, fmVol[V & 0x0f]); return; } switch (R & 0x0f) { case 6: Sound_OPL_Write (0x53, fmVol[V&0x0f]>>1); /* bass drum */ break; case 7: Sound_OPL_Write (0x51, fmVol[(V>>4)&0x0f]); /* hi-hat */ Sound_OPL_Write (0x54, fmVol[V&0x0f]); /* snare */ break; case 8: Sound_OPL_Write (0x52, fmVol[(V>>4)&0x0f]); /* tomtom */ Sound_OPL_Write (0x55, fmVol[V&0x0f]); /* top cymbal */ break; } } } //----------------------------------------------------------------------------- //#endif // MEKA_OPL ================================================ FILE: VGMPlay/chips/ym2413hd.h ================================================ /*-----------------------------------*/ /* YM-2413 emulator using OPL */ /* (c) by Hiromitsu Shioya */ /* Modified by Omar Cornut */ /*-----------------------------------*/ //#ifndef __YM2413HD_H__ //#define __YM2413HD_H__ #define YM2413_REGISTERS (64) // 64 registers area #define YM2413_INSTRUMENTS (16) // 16 instruments (0 is user defined) #define YM2413_VOLUME_STEPS (16) // 16 different volume steps #define YM2413_VOLUME_MASK (0x0F) //----------------------------------------------------------------------------- // Instrument Data //----------------------------------------------------------------------------- // FIXME: Currently placed outside of the MEKA_OPL test, as it is being // used by the FM Editor. //----------------------------------------------------------------------------- typedef struct { unsigned char MKS, CKS; /* KSL */ unsigned char MML, CML; /* MULTIPLE */ unsigned char MA, CA; /* ATTACK RATE */ unsigned char MSL, CSL; /* SUSTAIN LEVEL */ unsigned char MS, CS; /* EG */ unsigned char MD, CD; /* DECAY RATE */ unsigned char MR, CR; /* RELEASE RATE */ unsigned char MTL, CTL; /* TOTAL LEVEL */ unsigned char MEV, CEV; /* KSR */ unsigned char MW, CW; /* WAVE FORM */ unsigned char FB, CON; /* FEEDBACK / Connection */ } FM_OPL_Patch; //----------------------------------------------------------------------------- //#ifdef MEKA_OPL //----------------------------------------------------------------------------- // Registers byte FM_OPL_Regs [YM2413_REGISTERS]; #define FM_OPL_Rhythm_Mode (FM_OPL_Regs [0x0E] & 0x20) // Functions int FM_OPL_Init (void *userdata); void FM_OPL_Close (void); //void FM_OPL_Active (void); void FM_OPL_Update (void); void FM_OPL_Set_Voice (int R, int V, int VL); void FM_OPL_Set_User_Voice (void); // Interface (see FMUNIT.C/.H) void FM_OPL_Reset (void); void FM_OPL_Write (int Register, int Value); //void FM_OPL_Mute (void); //void FM_OPL_Resume (void); //void FM_OPL_Regenerate (void); //----------------------------------------------------------------------------- /*#else // A fake set of registers is created as sound/fmunit.c reference it. // FIXME: This sucks. byte FM_OPL_Regs [YM2413_REGISTERS]; #endif*/ //#endif /* !__YM2413HD_H__ */ ================================================ FILE: VGMPlay/chips/ym2612.c ================================================ /***********************************************************/ /* */ /* YM2612.C : YM2612 emulator */ /* */ /* Almost constantes are taken from the MAME core */ /* */ /* This source is a part of Gens project */ /* Written by Stphane Dallongeville (gens@consolemul.com) */ /* Copyright (c) 2002 by Stphane Dallongeville */ /* */ /***********************************************************/ /***********************************************************/ /* */ /* Modified by Maxim, Blargg */ /* - removed non-sound-related functionality */ /* - added high-pass PCM filter */ /* - added per-channel muting control */ /* - made it use a context struct to allow multiple */ /* instances */ /* */ /***********************************************************/ #include // for malloc #include #include #include // for memset() #include "mamedef.h" // for correct INLINE macro #include "ym2612.h" #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wpointer-sign" #endif /******************************************** * Partie dfinition * ********************************************/ #define YM_DEBUG_LEVEL 0 #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) //Just for tests stuff... // //#define COEF_MOD 0.5 //#define MAX_OUT ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD)) #define OUT_BITS (OUTPUT_BITS - 2) #define OUT_SHIFT (MAX_OUT_BITS - OUT_BITS) #define LIMIT_CH_OUT ((int) (((1 << OUT_BITS) * 1.5) - 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 // good rate ? //#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 /******************************************** * Partie variables * ********************************************/ //struct ym2612__ YM2612; int *SIN_TAB[SIN_LENGHT]; // SINUS TABLE (pointer on TL TABLE) int TL_TAB[TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) unsigned int ENV_TAB[2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) //unsigned int ATTACK_TO_DECAY[ENV_LENGHT]; // Conversion from attack to decay phase unsigned int DECAY_TO_ATTACK[ENV_LENGHT]; // Conversion from decay to attack phase unsigned int FINC_TAB[2048]; // Frequency step table 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_ENV_TAB[LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) int LFO_FREQ_TAB[LFO_LENGHT]; // LFO FMS TABLE // int INTER_TAB[MAX_UPDATE_LENGHT]; // Interpolation table int LFO_INC_TAB[8]; // LFO step table void (* const UPDATE_CHAN[8 * 8])(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) = // Update Channel functions pointer table { Update_Chan_Algo0, Update_Chan_Algo1, Update_Chan_Algo2, Update_Chan_Algo3, Update_Chan_Algo4, Update_Chan_Algo5, Update_Chan_Algo6, Update_Chan_Algo7, Update_Chan_Algo0_LFO, Update_Chan_Algo1_LFO, Update_Chan_Algo2_LFO, Update_Chan_Algo3_LFO, Update_Chan_Algo4_LFO, Update_Chan_Algo5_LFO, Update_Chan_Algo6_LFO, Update_Chan_Algo7_LFO, Update_Chan_Algo0_Int, Update_Chan_Algo1_Int, Update_Chan_Algo2_Int, Update_Chan_Algo3_Int, Update_Chan_Algo4_Int, Update_Chan_Algo5_Int, Update_Chan_Algo6_Int, Update_Chan_Algo7_Int, Update_Chan_Algo0_LFO_Int, Update_Chan_Algo1_LFO_Int, Update_Chan_Algo2_LFO_Int, Update_Chan_Algo3_LFO_Int, Update_Chan_Algo4_LFO_Int, Update_Chan_Algo5_LFO_Int, Update_Chan_Algo6_LFO_Int, Update_Chan_Algo7_LFO_Int }; void (* const ENV_NEXT_EVENT[8])(slot_ *SL) = // Next Enveloppe phase functions pointer table { Env_Attack_Next, Env_Decay_Next, Env_Substain_Next, Env_Release_Next, Env_NULL_Next, Env_NULL_Next, Env_NULL_Next, Env_NULL_Next }; const unsigned int DT_DEF_TAB[4 * 32] = { // FD = 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // FD = 1 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, // FD = 2 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, // FD = 3 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 }; const unsigned int FKEY_TAB[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 }; const unsigned int LFO_AMS_TAB[4] = { 31, 4, 1, 0 }; const unsigned int 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 }; int int_cnt; // Interpolation calculation #if YM_DEBUG_LEVEL > 0 // Debug FILE *debug_file = NULL; #endif /* Gens */ //extern unsigned int Sound_Extrapol[312][2]; //extern int Seg_L[882], Seg_R[882]; //extern int VDP_Current_Line; //extern int GYM_Dumping; //extern int YM2612_Enable; //extern int DAC_Enable=1; //int Update_GYM_Dump(char v0, char v1, char v2); int YM2612_Enable; int YM2612_Improv; //int DAC_Enable = 1; int *YM_Buf[2]; int YM_Len = 0; int YM2612_Enable_SSGEG = 1; // enable SSG-EG envelope (causes inacurate sound sometimes - rodrigo) int DAC_Highpass_Enable = 1; // sometimes it creates a terrible noise /* end */ /*********************************************** * fonctions calcul param * ***********************************************/ INLINE void CALC_FINC_SL(slot_ *SL, int finc, int kc) { int ksr; SL->Finc = (finc + SL->DT[kc]) * SL->MUL; ksr = kc >> SL->KSR_S; // keycode attnuation #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "FINC = %d SL->Finc = %d\n", finc, SL->Finc); #endif if (SL->KSR != ksr) // si le KSR a chang alors { // les diffrents 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 YM_DEBUG_LEVEL > 1 fprintf(debug_file, "KSR = %.4X EincA = %.8X EincD = %.8X EincS = %.8X EincR = %.8X\n", ksr, SL->EincA, SL->EincD, SL->EincS, SL->EincR); #endif } } INLINE void CALC_FINC_CH(channel_ *CH) { int finc, kc; finc = FINC_TAB[CH->FNUM[0]] >> (7 - CH->FOCT[0]); kc = CH->KC[0]; CALC_FINC_SL(&CH->SLOT[0], finc, kc); CALC_FINC_SL(&CH->SLOT[1], finc, kc); CALC_FINC_SL(&CH->SLOT[2], finc, kc); CALC_FINC_SL(&CH->SLOT[3], finc, kc); } /*********************************************** * fonctions setting * ***********************************************/ INLINE void KEY_ON(channel_ *CH, int nsl) { slot_ *SL = &(CH->SLOT[nsl]); // on recupre le bon pointeur de slot if(SL->Ecurp == RELEASE) // la touche est-elle relche ? { SL->Fcnt = 0; // Fix Ecco 2 splash sound SL->Ecnt = (DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; SL->ChgEnM = 0xFFFFFFFF; // SL->Ecnt = DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; // SL->Ecnt = 0; SL->Einc = SL->EincA; SL->Ecmp = ENV_DECAY; SL->Ecurp = ATTACK; } } INLINE void KEY_OFF(channel_ *CH, int nsl) { slot_ *SL = &(CH->SLOT[nsl]); // on recupre le bon pointeur de slot if(SL->Ecurp != RELEASE) // la touche est-elle appuye ? { if(SL->Ecnt < ENV_DECAY) // attack phase ? { SL->Ecnt = (ENV_TAB[SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; } SL->Einc = SL->EincR; SL->Ecmp = ENV_END; SL->Ecurp = RELEASE; } } INLINE void CSM_Key_Control(ym2612_ *YM2612) { KEY_ON(&YM2612->CHANNEL[2], 0); KEY_ON(&YM2612->CHANNEL[2], 1); KEY_ON(&YM2612->CHANNEL[2], 2); KEY_ON(&YM2612->CHANNEL[2], 3); } int SLOT_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; slot_ *SL; int nch, nsl; if((nch = Adr & 3) == 3) return 1; nsl = (Adr >> 2) & 3; if(Adr & 0x100) nch += 3; CH = &(YM2612->CHANNEL[nch]); SL = &(CH->SLOT[nsl]); switch(Adr & 0xF0) { case 0x30: if(SL->MUL = (data & 0x0F)) SL->MUL <<= 1; else SL->MUL = 1; SL->DT = DT_TAB[(data >> 4) & 7]; CH->SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] DTMUL = %.2X\n", nch, nsl, data & 0x7F); #endif break; case 0x40: SL->TL = data & 0x7F; // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... YM2612_Special_Update(YM2612); #if((ENV_HBITS - 7) < 0) SL->TLL = SL->TL >> (7 - ENV_HBITS); #else SL->TLL = SL->TL << (ENV_HBITS - 7); #endif #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] TL = %.2X\n", nch, nsl, SL->TL); #endif break; case 0x50: SL->KSR_S = 3 - (data >> 6); CH->SLOT[0].Finc = -1; if(data &= 0x1F) SL->AR = &AR_TAB[data << 1]; else SL->AR = &NULL_RATE[0]; SL->EincA = SL->AR[SL->KSR]; if(SL->Ecurp == ATTACK) SL->Einc = SL->EincA; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AR = %.2X EincA = %.6X\n", nch, nsl, data, SL->EincA); #endif break; case 0x60: if(SL->AMSon = (data & 0x80)) SL->AMS = CH->AMS; else SL->AMS = 31; if(data &= 0x1F) SL->DR = &DR_TAB[data << 1]; else SL->DR = &NULL_RATE[0]; SL->EincD = SL->DR[SL->KSR]; if(SL->Ecurp == DECAY) SL->Einc = SL->EincD; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AMS = %d DR = %.2X EincD = %.6X\n", nch, nsl, SL->AMSon, data, SL->EincD); #endif break; case 0x70: if(data &= 0x1F) SL->SR = &DR_TAB[data << 1]; else SL->SR = &NULL_RATE[0]; SL->EincS = SL->SR[SL->KSR]; if((SL->Ecurp == SUBSTAIN) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincS; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SR = %.2X EincS = %.6X\n", nch, nsl, data, SL->EincS); #endif break; case 0x80: SL->SLL = SL_TAB[data >> 4]; SL->RR = &DR_TAB[((data & 0xF) << 2) + 2]; SL->EincR = SL->RR[SL->KSR]; if((SL->Ecurp == RELEASE) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincR; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SL = %.8X\n", nch, nsl, SL->SLL); fprintf(debug_file, "CHANNEL[%d], SLOT[%d] RR = %.2X EincR = %.2X\n", nch, nsl, ((data & 0xF) << 1) | 2, SL->EincR); #endif break; case 0x90: /* SSG-EG envelope shapes : // // E At Al H // // 1 0 0 0 \\\\ // // 1 0 0 1 \___ // // 1 0 1 0 \/\/ // ___ // 1 0 1 1 \ // // 1 1 0 0 //// // ___ // 1 1 0 1 / // // 1 1 1 0 /\/\ // // 1 1 1 1 /___ // // E = SSG-EG enable // At = Start negate // Al = Altern // H = Hold */ if(YM2612_Enable_SSGEG) { if(data & 0x08) SL->SEG = data & 0x0F; else SL->SEG = 0; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SSG-EG = %.2X\n", nch, nsl, data); #endif } break; } return 0; } int CHANNEL_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; int num; if((num = Adr & 3) == 3) return 1; switch(Adr & 0xFC) { case 0xA0: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); 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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] part1 FNUM = %d KC = %d\n", num, CH->FNUM[0], CH->KC[0]); #endif break; case 0xA4: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); CH->FNUM[0] = (CH->FNUM[0] & 0x0FF) + ((int) (data & 0x07) << 8); CH->FOCT[0] = (data & 0x38) >> 3; CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; CH->SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] part2 FNUM = %d FOCT = %d KC = %d\n", num, CH->FNUM[0], CH->FOCT[0], CH->KC[0]); #endif break; case 0xA8: if(Adr < 0x100) { num++; YM2612_Special_Update(YM2612); 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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[2] part1 FNUM[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].KC[num]); #endif } break; case 0xAC: if(Adr < 0x100) { num++; YM2612_Special_Update(YM2612); YM2612->CHANNEL[2].FNUM[num] = (YM2612->CHANNEL[2].FNUM[num] & 0x0FF) + ((int) (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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[2] part2 FNUM[%d] = %d FOCT[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].FOCT[num], num, YM2612->CHANNEL[2].KC[num]); #endif } break; case 0xB0: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); if(CH->ALGO != (data & 7)) { // Fix VectorMan 2 heli sound (level 1) YM2612_Special_Update(YM2612); 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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] ALGO = %d FB = %d\n", num, CH->ALGO, CH->FB); #endif break; case 0xB4: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); if(data & 0x80) CH->LEFT = 0xFFFFFFFF; else CH->LEFT = 0; if(data & 0x40) CH->RIGHT = 0xFFFFFFFF; else CH->RIGHT = 0; CH->AMS = LFO_AMS_TAB[(data >> 4) & 3]; CH->FMS = LFO_FMS_TAB[data & 7]; if(CH->SLOT[0].AMSon) CH->SLOT[0].AMS = CH->AMS; else CH->SLOT[0].AMS = 31; if(CH->SLOT[1].AMSon) CH->SLOT[1].AMS = CH->AMS; else CH->SLOT[1].AMS = 31; if(CH->SLOT[2].AMSon) CH->SLOT[2].AMS = CH->AMS; else CH->SLOT[2].AMS = 31; if(CH->SLOT[3].AMSon) CH->SLOT[3].AMS = CH->AMS; else CH->SLOT[3].AMS = 31; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "CHANNEL[%d] AMS = %d FMS = %d\n", num, CH->AMS, CH->FMS); #endif break; } return 0; } int YM_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; int nch; switch(Adr) { case 0x22: if(data & 8) { // Cool Spot music 1, LFO modified severals time which // distord the sound, have to check that on a real genesis... YM2612->LFOinc = LFO_INC_TAB[data & 7]; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\nLFO Enable, LFOinc = %.8X %d\n", YM2612->LFOinc, data & 7); #endif } else { YM2612->LFOinc = YM2612->LFOcnt = 0; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\nLFO Disable\n"); #endif } 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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); #endif } 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; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); #endif } break; case 0x26: YM2612->TimerB = data; if(YM2612->TimerBL != (256 - YM2612->TimerB) << (4 + 12)) { YM2612->TimerBcnt = YM2612->TimerBL = (256 - YM2612->TimerB) << (4 + 12); #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer B Set = %.8X\n", YM2612->TimerBcnt); #endif } break; case 0x27: // Paramtre 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); 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 demand? YM2612->Status &= (~data >> 4) & (data >> 2); // Reset Status YM2612->Mode = data; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Mode reg = %.2X\n", data); #endif break; case 0x28: if((nch = data & 3) == 3) return 1; if(data & 4) nch += 3; CH = &(YM2612->CHANNEL[nch]); YM2612_Special_Update(YM2612); if(data & 0x10) KEY_ON(CH, S0); // On appuie sur la touche pour le slot 1 else KEY_OFF(CH, S0); // On relche 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 relche 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 relche 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 relche la touche pour le slot 4 #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "CHANNEL[%d] KEY %.1X\n", nch, ((data & 0xf0) >> 4)); #endif break; case 0x2A: YM2612->DACdata = ((int) data - 0x80) << DAC_SHIFT; // donne du DAC break; case 0x2B: if(YM2612->DAC ^ (data & 0x80)) YM2612_Special_Update(YM2612); YM2612->DAC = data & 0x80; // activation/dsactivation du DAC break; } return 0; } /*********************************************** * fonctions de gnration * ***********************************************/ void Env_NULL_Next(slot_ *SL) { } void Env_Attack_Next(slot_ *SL) { // Verified with Gynoug even in HQ (explode SFX) SL->Ecnt = ENV_DECAY; SL->Einc = SL->EincD; SL->Ecmp = SL->SLL; SL->Ecurp = DECAY; } void Env_Decay_Next(slot_ *SL) { // Verified with Gynoug even in HQ (explode SFX) SL->Ecnt = SL->SLL; SL->Einc = SL->EincS; SL->Ecmp = ENV_END; SL->Ecurp = SUBSTAIN; } void Env_Substain_Next(slot_ *SL) { if(YM2612_Enable_SSGEG) { if(SL->SEG & 8) // SSG envelope type { if(SL->SEG & 1) { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } else { // re KEY ON // SL->Fcnt = 0; // SL->ChgEnM = 0xFFFFFFFF; SL->Ecnt = 0; SL->Einc = SL->EincA; SL->Ecmp = ENV_DECAY; SL->Ecurp = ATTACK; } SL->SEG ^= (SL->SEG & 2) << 1; } else { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } } else { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } } void Env_Release_Next(slot_ *SL) { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } #define GET_CURRENT_PHASE \ YM2612->in0 = CH->SLOT[S0].Fcnt; \ YM2612->in1 = CH->SLOT[S1].Fcnt; \ YM2612->in2 = CH->SLOT[S2].Fcnt; \ YM2612->in3 = CH->SLOT[S3].Fcnt; #define UPDATE_PHASE \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; #define UPDATE_PHASE_LFO \ if(freq_LFO = (CH->FMS * YM2612->LFO_FREQ_UP[i]) >> (LFO_HBITS - 1)) \ { \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc + ((CH->SLOT[S0].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc + ((CH->SLOT[S1].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc + ((CH->SLOT[S2].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc + ((CH->SLOT[S3].Finc * freq_LFO) >> LFO_FMS_LBITS); \ } \ else \ { \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; \ } #define GET_CURRENT_ENV \ if(CH->SLOT[S0].SEG & 4) \ { \ if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ else YM2612->en0 ^= ENV_MASK; \ } \ else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL; \ if(CH->SLOT[S1].SEG & 4) \ { \ if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ else YM2612->en1 ^= ENV_MASK; \ } \ else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL; \ if(CH->SLOT[S2].SEG & 4) \ { \ if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ else YM2612->en2 ^= ENV_MASK; \ } \ else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL; \ if(CH->SLOT[S3].SEG & 4) \ { \ if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ else YM2612->en3 ^= ENV_MASK; \ } \ else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL; #define GET_CURRENT_ENV_LFO \ env_LFO = YM2612->LFO_ENV_UP[i]; \ \ if(CH->SLOT[S0].SEG & 4) \ { \ if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ else YM2612->en0 = (YM2612->en0 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S0].AMS); \ } \ else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL + (env_LFO >> CH->SLOT[S0].AMS); \ if(CH->SLOT[S1].SEG & 4) \ { \ if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ else YM2612->en1 = (YM2612->en1 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S1].AMS); \ } \ else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL + (env_LFO >> CH->SLOT[S1].AMS); \ if(CH->SLOT[S2].SEG & 4) \ { \ if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ else YM2612->en2 = (YM2612->en2 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S2].AMS); \ } \ else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL + (env_LFO >> CH->SLOT[S2].AMS); \ if(CH->SLOT[S3].SEG & 4) \ { \ if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ else YM2612->en3 = (YM2612->en3 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S3].AMS); \ } \ else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL + (env_LFO >> CH->SLOT[S3].AMS); #define UPDATE_ENV \ if((CH->SLOT[S0].Ecnt += CH->SLOT[S0].Einc) >= CH->SLOT[S0].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S0].Ecurp](&(CH->SLOT[S0])); \ if((CH->SLOT[S1].Ecnt += CH->SLOT[S1].Einc) >= CH->SLOT[S1].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S1].Ecurp](&(CH->SLOT[S1])); \ if((CH->SLOT[S2].Ecnt += CH->SLOT[S2].Einc) >= CH->SLOT[S2].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S2].Ecurp](&(CH->SLOT[S2])); \ if((CH->SLOT[S3].Ecnt += CH->SLOT[S3].Einc) >= CH->SLOT[S3].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S3].Ecurp](&(CH->SLOT[S3])); #define DO_LIMIT \ if(CH->OUTd > LIMIT_CH_OUT) CH->OUTd = LIMIT_CH_OUT; \ else if(CH->OUTd < -LIMIT_CH_OUT) CH->OUTd = -LIMIT_CH_OUT; #define DO_FEEDBACK0 \ YM2612->in0 += CH->S0_OUT[0] >> CH->FB; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK \ YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1]) >> CH->FB; \ CH->S0_OUT[1] = CH->S0_OUT[0]; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK2 \ YM2612->in0 += (CH->S0_OUT[0] + (CH->S0_OUT[0] >> 2) + CH->S0_OUT[1]) >> CH->FB; \ CH->S0_OUT[1] = CH->S0_OUT[0] >> 2; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK3 \ YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1] + CH->S0_OUT[2] + CH->S0_OUT[3]) >> CH->FB; \ CH->S0_OUT[3] = CH->S0_OUT[2] >> 1; \ CH->S0_OUT[2] = CH->S0_OUT[1] >> 1; \ CH->S0_OUT[1] = CH->S0_OUT[0] >> 1; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_ALGO_0 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_1 \ DO_FEEDBACK \ YM2612->in2 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_2 \ DO_FEEDBACK \ YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_3 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in3 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_4 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_5 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in2 += CH->S0_OUT[1]; \ YM2612->in3 += CH->S0_OUT[1]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_6 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_7 \ DO_FEEDBACK \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2] + CH->S0_OUT[1]) >> OUT_SHIFT; \ DO_LIMIT #define DO_OUTPUT \ buf[0][i] += CH->OUTd & CH->LEFT; \ buf[1][i] += CH->OUTd & CH->RIGHT; #define DO_OUTPUT_INT0 \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ buf[0][i] += CH->OUTd & CH->LEFT; \ buf[1][i] += CH->OUTd & CH->RIGHT; \ } \ else i--; #define DO_OUTPUT_INT1 \ CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; #define DO_OUTPUT_INT2 \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; \ CH->Old_OUTd = CH->OUTd; #define DO_OUTPUT_INT \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ CH->Old_OUTd = (((int_cnt ^ 0x3FFF) * CH->OUTd) + (int_cnt * CH->Old_OUTd)) >> 14; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; \ CH->Old_OUTd = CH->OUTd; void Update_Chan_Algo0(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_0 DO_OUTPUT } } void Update_Chan_Algo1(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_1 DO_OUTPUT } } void Update_Chan_Algo2(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_2 DO_OUTPUT } } void Update_Chan_Algo3(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_3 DO_OUTPUT } } void Update_Chan_Algo4(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_4 DO_OUTPUT } } void Update_Chan_Algo5(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_5 DO_OUTPUT } } void Update_Chan_Algo6(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_6 DO_OUTPUT } } void Update_Chan_Algo7(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_7 DO_OUTPUT } } void Update_Chan_Algo0_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_0 DO_OUTPUT } } void Update_Chan_Algo1_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_1 DO_OUTPUT } } void Update_Chan_Algo2_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_2 DO_OUTPUT } } void Update_Chan_Algo3_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_3 DO_OUTPUT } } void Update_Chan_Algo4_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_4 DO_OUTPUT } } void Update_Chan_Algo5_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_5 DO_OUTPUT } } void Update_Chan_Algo6_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_6 DO_OUTPUT } } void Update_Chan_Algo7_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_7 DO_OUTPUT } } /****************************************************** * Interpolated output * *****************************************************/ void Update_Chan_Algo0_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_0 DO_OUTPUT_INT } } void Update_Chan_Algo1_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_1 DO_OUTPUT_INT } } void Update_Chan_Algo2_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_2 DO_OUTPUT_INT } } void Update_Chan_Algo3_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_3 DO_OUTPUT_INT } } void Update_Chan_Algo4_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_4 DO_OUTPUT_INT } } void Update_Chan_Algo5_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_5 DO_OUTPUT_INT } } void Update_Chan_Algo6_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_6 DO_OUTPUT_INT } } void Update_Chan_Algo7_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_7 DO_OUTPUT_INT } } void Update_Chan_Algo0_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_0 DO_OUTPUT_INT } } void Update_Chan_Algo1_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_1 DO_OUTPUT_INT } } void Update_Chan_Algo2_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_2 DO_OUTPUT_INT } } void Update_Chan_Algo3_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_3 DO_OUTPUT_INT } } void Update_Chan_Algo4_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_4 DO_OUTPUT_INT } } void Update_Chan_Algo5_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_5 DO_OUTPUT_INT } } void Update_Chan_Algo6_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_6 DO_OUTPUT_INT } } void Update_Chan_Algo7_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_7 DO_OUTPUT_INT } } /*********************************************** * fonctions publiques * ***********************************************/ // Initialisation de l'mulateur YM2612 ym2612_ *YM2612_Init(int Clock, int Rate, int Interpolation) { ym2612_ *YM2612; int i, j; double x; if((Rate == 0) || (Clock == 0)) return NULL; YM2612 = (ym2612_ *)malloc(sizeof(ym2612_)); memset(YM2612, 0, sizeof(ym2612_)); #if YM_DEBUG_LEVEL > 0 if(debug_file == NULL) { debug_file = fopen("ym2612.log", "w"); fprintf(debug_file, "YM2612 logging :\n\n"); } #endif YM2612->Clock = Clock; YM2612->Rate = Rate; // 144 = 12 * (prescale * 2) = 12 * 6 * 2 // prescale set to 6 by default YM2612->Frequence = ((double) YM2612->Clock / (double) YM2612->Rate) / 144.0; YM2612->TimerBase = (int) (YM2612->Frequence * 4096.0); if((Interpolation) && (YM2612->Frequence > 1.0)) { YM2612->Inter_Step = (unsigned int) ((1.0 / YM2612->Frequence) * (double) (0x4000)); YM2612->Inter_Cnt = 0; // We recalculate rate and frequence after interpolation YM2612->Rate = YM2612->Clock / 144; YM2612->Frequence = 1.0; } else { YM2612->Inter_Step = 0x4000; YM2612->Inter_Cnt = 0; } #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "YM2612 frequence = %g rate = %d interp step = %.8X\n\n", YM2612->Frequence, YM2612->Rate, YM2612->Inter_Step); #endif // 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 ?) { TL_TAB[TL_LENGHT + i] = TL_TAB[i] = 0; } else { x = MAX_OUT; // Max output x /= pow(10, (ENV_STEP * i) / 20); // Decibel -> Voltage TL_TAB[i] = (int) x; TL_TAB[TL_LENGHT + i] = -TL_TAB[i]; } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "TL_TAB[%d] = %.8X TL_TAB[%d] = %.8X\n", i, TL_TAB[i], TL_LENGHT + i, TL_TAB[TL_LENGHT + i]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau SIN : // SIN_TAB[x][y] = sin(x) * y; // x = phase and y = volume SIN_TAB[0] = SIN_TAB[SIN_LENGHT / 2] = &TL_TAB[(int)PG_CUT_OFF]; for(i = 1; i <= SIN_LENGHT / 4; i++) { x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus x = 20 * log10(1 / x); // convert to dB j = (int) (x / ENV_STEP); // Get TL range if(j > PG_CUT_OFF) j = (int) PG_CUT_OFF; SIN_TAB[i] = SIN_TAB[(SIN_LENGHT / 2) - i] = &TL_TAB[j]; SIN_TAB[(SIN_LENGHT / 2) + i] = SIN_TAB[SIN_LENGHT - i] = &TL_TAB[TL_LENGHT + j]; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X\n", i, SIN_TAB[i][0], (SIN_LENGHT / 2) - i, SIN_TAB[(SIN_LENGHT / 2) - i][0], (SIN_LENGHT / 2) + i, SIN_TAB[(SIN_LENGHT / 2) + i][0], SIN_LENGHT - i, SIN_TAB[SIN_LENGHT - i][0]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau LFO (LFO wav) : for(i = 0; i < LFO_LENGHT; i++) { 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 LFO_ENV_TAB[i] = (int) x; x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus x *= (double) ((1 << (LFO_HBITS - 1)) - 1); LFO_FREQ_TAB[i] = (int) x; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "LFO[%d] = %.8X\n", i, LFO_ENV_TAB[i]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau Enveloppe : // ENV_TAB[0] -> ENV_TAB[ENV_LENGHT - 1] = attack curve // ENV_TAB[ENV_LENGHT] -> ENV_TAB[2 * ENV_LENGHT - 1] = decay curve for(i = 0; i < ENV_LENGHT; i++) { // Attack curve (x^8 - music level 2 Vectorman 2) x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); x *= ENV_LENGHT; ENV_TAB[i] = (int) x; // Decay curve (just linear) x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); x *= ENV_LENGHT; ENV_TAB[ENV_LENGHT + i] = (int) x; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "ATTACK[%d] = %d DECAY[%d] = %d\n", i, ENV_TAB[i], i, ENV_TAB[ENV_LENGHT + i]); #endif } ENV_TAB[ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state // Tableau pour la conversion Attack -> Decay and Decay -> Attack for(i = 0, j = ENV_LENGHT - 1; i < ENV_LENGHT; i++) { while (j && (ENV_TAB[j] < (unsigned) i)) j--; DECAY_TO_ATTACK[i] = j << ENV_LBITS; } // Tableau pour le Substain Level for(i = 0; i < 15; i++) { x = i * 3; // 3 and not 6 (Mickey Mania first music for test) x /= ENV_STEP; j = (int) x; j <<= ENV_LBITS; SL_TAB[i] = j + ENV_DECAY; } j = ENV_LENGHT - 1; // special case : volume off j <<= ENV_LBITS; SL_TAB[15] = j + ENV_DECAY; // Tableau Frequency Step for(i = 0; i < 2048; i++) { x = (double) (i) * YM2612->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 FINC_TAB[i] = (unsigned int) x; } // Tableaux Attack & Decay Rate for(i = 0; i < 4; i++) { AR_TAB[i] = 0; DR_TAB[i] = 0; } for(i = 0; i < 60; i++) { x = YM2612->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 ENV_TAB AR_TAB[i + 4] = (unsigned int) (x / AR_RATE); DR_TAB[i + 4] = (unsigned int) (x / DR_RATE); } for(i = 64; i < 96; i++) { AR_TAB[i] = AR_TAB[63]; DR_TAB[i] = DR_TAB[63]; NULL_RATE[i - 64] = 0; } // Tableau Detune for(i = 0; i < 4; i++) { for (j = 0; j < 32; j++) { #if((SIN_LBITS + SIN_HBITS - 21) < 0) x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); #else x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); #endif DT_TAB[i + 0][j] = (int) x; DT_TAB[i + 4][j] = (int) -x; } } // Tableau LFO j = (YM2612->Rate * YM2612->Inter_Step) / 0x4000; LFO_INC_TAB[0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); YM2612_Reset(YM2612); return YM2612; } int YM2612_End(ym2612_ *YM2612) { free(YM2612); #if YM_DEBUG_LEVEL > 0 if(debug_file) fclose(debug_file); debug_file = NULL; #endif return 0; } int YM2612_Reset(ym2612_ *YM2612) { int i, j; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\n\nStarting reseting YM2612 ...\n\n"); #endif YM2612->LFOcnt = 0; YM2612->TimerA = 0; YM2612->TimerAL = 0; YM2612->TimerAcnt = 0; YM2612->TimerB = 0; YM2612->TimerBL = 0; YM2612->TimerBcnt = 0; YM2612->DAC = 0; YM2612->DACdata = 0; YM2612->dac_highpass = 0; YM2612->Status = 0; YM2612->OPNAadr = 0; YM2612->OPNBadr = 0; YM2612->Inter_Cnt = 0; for(i = 0; i < 6; i++) { YM2612->CHANNEL[i].Old_OUTd = 0; YM2612->CHANNEL[i].OUTd = 0; YM2612->CHANNEL[i].LEFT = 0xFFFFFFFF; YM2612->CHANNEL[i].RIGHT = 0xFFFFFFFF; YM2612->CHANNEL[i].ALGO = 0;; YM2612->CHANNEL[i].FB = 31; YM2612->CHANNEL[i].FMS = 0; YM2612->CHANNEL[i].AMS = 0; for(j = 0 ;j < 4 ; j++) { YM2612->CHANNEL[i].S0_OUT[j] = 0; YM2612->CHANNEL[i].FNUM[j] = 0; YM2612->CHANNEL[i].FOCT[j] = 0; YM2612->CHANNEL[i].KC[j] = 0; YM2612->CHANNEL[i].SLOT[j].Fcnt = 0; YM2612->CHANNEL[i].SLOT[j].Finc = 0; YM2612->CHANNEL[i].SLOT[j].Ecnt = ENV_END; // Put it at the end of Decay phase... YM2612->CHANNEL[i].SLOT[j].Einc = 0; YM2612->CHANNEL[i].SLOT[j].Ecmp = 0; YM2612->CHANNEL[i].SLOT[j].Ecurp = RELEASE; YM2612->CHANNEL[i].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--) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 1, 0xC0); YM2612_Write(YM2612, 3, 0xC0); } for(i = 0xB2; i >= 0x22; i--) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 1, 0); YM2612_Write(YM2612, 3, 0); } YM2612_Write(YM2612, 0, 0x2A); YM2612_Write(YM2612, 1, 0x80); #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\n\nFinishing reseting YM2612 ...\n\n"); #endif return 0; } int YM2612_Read(ym2612_ *YM2612) { /* static int cnt = 0; if(cnt++ == 50) { cnt = 0; return YM2612->Status; } else return YM2612->Status | 0x80; */ return YM2612->Status; } int YM2612_Write(ym2612_ *YM2612, unsigned char adr, unsigned char data) { int d; data &= 0xFF; adr &= 0x03; switch(adr) { case 0: YM2612->OPNAadr = data; break; case 1: // Trivial optimisation if(YM2612->OPNAadr == 0x2A) { YM2612->DACdata = ((int)data - 0x80) << DAC_SHIFT; return 0; } d = YM2612->OPNAadr & 0xF0; if(d >= 0x30) { if(YM2612->REG[0][YM2612->OPNAadr] == data) return 2; YM2612->REG[0][YM2612->OPNAadr] = data; // if (GYM_Dumping) Update_GYM_Dump(1, YM2612->OPNAadr, data); if(d < 0xA0) // SLOT { SLOT_SET(YM2612, YM2612->OPNAadr, data); } else // CHANNEL { CHANNEL_SET(YM2612, YM2612->OPNAadr, data); } } else // YM2612 { YM2612->REG[0][YM2612->OPNAadr] = data; // if ((GYM_Dumping) && ((YM2612->OPNAadr == 0x22) || (YM2612->OPNAadr == 0x27) || (YM2612->OPNAadr == 0x28))) Update_GYM_Dump(1, YM2612->OPNAadr, data); YM_SET(YM2612, YM2612->OPNAadr, data); } break; case 2: YM2612->OPNBadr = data; break; case 3: d = YM2612->OPNBadr & 0xF0; if(d >= 0x30) { if(YM2612->REG[1][YM2612->OPNBadr] == data) return 2; YM2612->REG[1][YM2612->OPNBadr] = data; // if (GYM_Dumping) Update_GYM_Dump(2, YM2612->OPNBadr, data); if(d < 0xA0) // SLOT { SLOT_SET(YM2612, YM2612->OPNBadr + 0x100, data); } else // CHANNEL { CHANNEL_SET(YM2612, YM2612->OPNBadr + 0x100, data); } } else return 1; break; } return 0; } int YM2612_GetMute(ym2612_ *YM2612) { int i, result = 0; for (i = 0; i < 6; ++i) { result |= YM2612->CHANNEL[i].Mute << i; } result |= YM2612->DAC_Mute << 6; //result &= !(YM2612_Enable_SSGEG); return result; } void YM2612_SetMute(ym2612_ *YM2612, int val) { int i; for (i = 0; i < 6; ++i) { YM2612->CHANNEL[i].Mute = (val >> i) & 1; } YM2612->DAC_Mute = (val >> 6) & 1; //YM2612_Enable_SSGEG = !(val & 1); } void YM2612_SetOptions(int Flags) { DAC_Highpass_Enable = (Flags >> 0) & 0x01; YM2612_Enable_SSGEG = (Flags >> 1) & 0x01; } void YM2612_ClearBuffer(int **buffer, int length) { // the MAME core does this before updating, // but the Gens core does this before mixing int *bufL, *bufR; int i; bufL = buffer[0]; bufR = buffer[1]; for(i = 0; i < length; i++) { bufL[i] = 0x0000; bufR[i] = 0x0000; } } void YM2612_Update(ym2612_ *YM2612, int **buf, int length) { int i, j, algo_type; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nStarting generating sound...\n\n"); #endif // Mise ?jour des pas des compteurs-frquences s'ils ont t?modifis if(YM2612->CHANNEL[0].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[0]); if(YM2612->CHANNEL[1].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[1]); if(YM2612->CHANNEL[2].SLOT[0].Finc == -1) { if(YM2612->Mode & 0x40) { CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); } else { CALC_FINC_CH(&YM2612->CHANNEL[2]); } } if(YM2612->CHANNEL[3].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[3]); if(YM2612->CHANNEL[4].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[4]); if(YM2612->CHANNEL[5].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[5]); /* CALC_FINC_CH(&YM2612->CHANNEL[0]); CALC_FINC_CH(&YM2612->CHANNEL[1]); if(YM2612->Mode & 0x40) { CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); } else { CALC_FINC_CH(&YM2612->CHANNEL[2]); } CALC_FINC_CH(&YM2612->CHANNEL[3]); CALC_FINC_CH(&YM2612->CHANNEL[4]); CALC_FINC_CH(&YM2612->CHANNEL[5]); */ if(YM2612->Inter_Step & 0x04000) algo_type = 0; else algo_type = 16; if(YM2612->LFOinc) { // Precalcul LFO wav for(i = 0; i < length; i++) { j = ((YM2612->LFOcnt += YM2612->LFOinc) >> LFO_LBITS) & LFO_MASK; YM2612->LFO_ENV_UP[i] = LFO_ENV_TAB[j]; YM2612->LFO_FREQ_UP[i] = LFO_FREQ_TAB[j]; #if YM_DEBUG_LEVEL > 3 fprintf(debug_file, "LFO_ENV_UP[%d] = %d LFO_FREQ_UP[%d] = %d\n", i, YM2612->LFO_ENV_UP[i], i, YM2612->LFO_FREQ_UP[i]); #endif } algo_type |= 8; } if (!YM2612->CHANNEL[0].Mute) UPDATE_CHAN[YM2612->CHANNEL[0].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[0]), buf, length); if (!YM2612->CHANNEL[1].Mute) UPDATE_CHAN[YM2612->CHANNEL[1].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[1]), buf, length); if (!YM2612->CHANNEL[2].Mute) UPDATE_CHAN[YM2612->CHANNEL[2].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[2]), buf, length); if (!YM2612->CHANNEL[3].Mute) UPDATE_CHAN[YM2612->CHANNEL[3].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[3]), buf, length); if (!YM2612->CHANNEL[4].Mute) UPDATE_CHAN[YM2612->CHANNEL[4].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[4]), buf, length); if (!YM2612->CHANNEL[5].Mute && !(YM2612->DAC)) UPDATE_CHAN[YM2612->CHANNEL[5].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[5]), buf, length); YM2612->Inter_Cnt = int_cnt; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nFinishing generating sound...\n\n"); #endif } /*int YM2612_Save(ym2612_ *YM2612, unsigned char SAVE[0x200]) { int i; for(i = 0; i < 0x100; i++) { SAVE[0x000 + i] = YM2612->REG[0][i]; SAVE[0x100 + i] = YM2612->REG[1][i]; } return 0; } int YM2612_Restore(ym2612_ *YM2612, unsigned char SAVE[0x200]) { int i; YM2612_Reset(YM2612); for(i = 0; i < 0x100; i++) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 1, SAVE[0x000 + i]); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 3, SAVE[0x100 + i]); } return 0; }*/ /* Gens */ enum { highpass_fract = 15 }; enum { highpass_shift = 9 }; // higher values reduce highpass on DAC void YM2612_DacAndTimers_Update(ym2612_ *YM2612, int **buffer, int length) { int *bufL, *bufR; int i; if(YM2612->DAC && YM2612->DACdata && ! YM2612->DAC_Mute) { bufL = buffer[0]; bufR = buffer[1]; for(i = 0; i < length; i++) { long dac = (YM2612->DACdata << highpass_fract) - YM2612->dac_highpass; if (DAC_Highpass_Enable) // else it's left at 0 and doesn't affect the sound YM2612->dac_highpass += dac >> highpass_shift; dac >>= highpass_fract; bufL[i] += dac & YM2612->CHANNEL[5].LEFT; bufR[i] += dac & YM2612->CHANNEL[5].RIGHT; } } i = YM2612->TimerBase * length; if(YM2612->Mode & 1) // Timer A ON ? { // if((YM2612->TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) if((YM2612->TimerAcnt -= i) <= 0) { YM2612->Status |= (YM2612->Mode & 0x04) >> 2; YM2612->TimerAcnt += YM2612->TimerAL; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Counter A overflow\n"); #endif if(YM2612->Mode & 0x80) CSM_Key_Control(YM2612); } } if(YM2612->Mode & 2) // Timer B ON ? { // if((YM2612->TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) if((YM2612->TimerBcnt -= i) <= 0) { YM2612->Status |= (YM2612->Mode & 0x08) >> 2; YM2612->TimerBcnt += YM2612->TimerBL; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Counter B overflow\n"); #endif } } } /* Gens */ void YM2612_Special_Update(ym2612_ *YM2612) { /* if (YM_Len && YM2612_Enable) { YM2612_Update(YM_Buf, YM_Len); YM_Buf[0] = Seg_L + Sound_Extrapol[VDP_Current_Line + 1][0]; YM_Buf[1] = Seg_R + Sound_Extrapol[VDP_Current_Line + 1][0]; YM_Len = 0; }*/ } /* end */ ================================================ FILE: VGMPlay/chips/ym2612.h ================================================ #ifndef _YM2612_H_ #define _YM2612_H_ // Change it if you need to do long update //#define MAX_UPDATE_LENGHT 4000 #define MAX_UPDATE_LENGHT 0x100 //#define MAX_UPDATE_LENGHT 1 // for in_vgm // Gens always uses 16 bits sound (in 32 bits buffer) and do the convertion later if needed. #define OUTPUT_BITS 15 // OUTPUT_BITS 15 is MAME's volume level #define DAC_SHIFT (OUTPUT_BITS - 9) // DAC_SHIFT makes sure that FM and DAC volume has the same volume // VC++ inline #ifndef INLINE #define INLINE __inline #endif typedef struct slot__ { int *DT; // paramtre detune int MUL; // paramtre "multiple de frquence" 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 premire phase de rgression 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 calcule par rapport la frquence actuelle, elle va influer // sur les diffrents paramtres de l'enveloppe comme l'attaque, le decay ... comme dans la ralit ! int SEG; // Type enveloppe SSG int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) int *DR; // Decay Rate (table pointeur) = Taux pour la rgression (DR[KSR]) int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) int *RR; // Release Rate (table pointeur) = Taux pour le relchement (RR[KSR]) int Fcnt; // Frequency Count = compteur-frquence pour dterminer l'amplitude actuelle (SIN[Finc >> 16]) int Finc; // frequency step = pas d'incrmentation du compteur-frquence // plus le pas est grand, plus la frquence est agu (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'incrmentation du compteur durant la phase d'attaque // cette valeur est gal AR[KSR] int EincD; // Envelope step for Decay = pas d'incrmentation du compteur durant la phase de regression // cette valeur est gal DR[KSR] int EincS; // Envelope step for Sustain = pas d'incrmentation du compteur durant la phase de maintenue // cette valeur est gal SR[KSR] int EincR; // Envelope step for Release = pas d'incrmentation du compteur durant la phase de relchement // cette valeur est gal RR[KSR] int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot l'entre // d'un autre ou carrement la sortie de la voie int INd; // input data of the slot = donnes en entre du slot int ChgEnM; // Change envelop mask. int AMS; // AMS depth level of this SLOT = degr de modulation de l'amplitude par le LFO int AMSon; // AMS enable flag = drapeau d'activation de l'AMS } slot_; typedef struct channel__ { int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) int Old_OUTd; // ancienne sortie de la voie (son brut) int OUTd; // sortie de la voie (son brut) int LEFT; // LEFT enable flag int RIGHT; // RIGHT enable flag int ALGO; // Algorythm = dtermine les connections entre les oprateurs int FB; // shift count of self feed back = degr de "Feed-Back" du SLOT 1 (il est son unique entre) int FMS; // Frquency Modulation Sensitivity of channel = degr de modulation de la frquence sur la voie par le LFO int AMS; // Amplitude Modulation Sensitivity of channel = degr de modulation de l'amplitude sur la voie par le LFO int FNUM[4]; // hauteur frquence de la voie (+ 3 pour le mode spcial) int FOCT[4]; // octave de la voie (+ 3 pour le mode spcial) int KC[4]; // Key Code = valeur fonction de la frquence (voir KSR pour les slots, KSR = KC >> KSR_S) struct slot__ SLOT[4]; // four slot.operators = les 4 slots de la voie int FFlag; // Frequency step recalculation flag int Mute; // Maxim: channel mute flag } channel_; typedef struct ym2612__ { int Clock; // Horloge YM2612 int Rate; // Sample Rate (11025/22050/44100) int TimerBase; // TimerBase calculation int Status; // YM2612 Status (timer overflow) int OPNAadr; // addresse pour l'criture dans l'OPN A (propre l'mulateur) int OPNBadr; // addresse pour l'criture dans l'OPN B (propre l'mulateur) int LFOcnt; // LFO counter = compteur-frquence pour le LFO int LFOinc; // LFO step counter = pas d'incrmentation du compteur-frquence du LFO // plus le pas est grand, plus la frquence est grande int TimerA; // timerA limit = valeur jusqu' laquelle le timer A doit compter int TimerAL; int TimerAcnt; // timerA counter = valeur courante du Timer A int TimerB; // timerB limit = valeur jusqu' laquelle le timer B doit compter int TimerBL; int TimerBcnt; // timerB counter = valeur courante du Timer B int Mode; // Mode actuel des voie 3 et 6 (normal / spcial) int DAC; // DAC enabled flag int DACdata; // DAC data long dac_highpass; double Frequence; // Frquence de base, se calcul par rapport l'horlage et au sample rate unsigned int Inter_Cnt; // Interpolation Counter unsigned int Inter_Step; // Interpolation Step struct channel__ CHANNEL[6]; // Les 6 voies du YM2612 int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif // cela nous rend le dbuggage plus facile int LFO_ENV_UP[MAX_UPDATE_LENGHT]; // Temporary calculated LFO AMS (adjusted for 11.8 dB) * int LFO_FREQ_UP[MAX_UPDATE_LENGHT]; // Temporary calculated LFO FMS * int in0, in1, in2, in3; // current phase calculation * int en0, en1, en2, en3; // current enveloppe calculation * int DAC_Mute; } ym2612_; /* Gens */ //extern int YM2612_Enable; //extern int YM2612_Improv; //extern int DAC_Enable; //extern int *YM_Buf[2]; //extern int YM_Len; /* end */ ym2612_ *YM2612_Init(int clock, int rate, int interpolation); int YM2612_End(ym2612_ *YM2612); int YM2612_Reset(ym2612_ *YM2612); int YM2612_Read(ym2612_ *YM2612); int YM2612_Write(ym2612_ *YM2612, unsigned char adr, unsigned char data); void YM2612_ClearBuffer(int **buffer, int length); void YM2612_Update(ym2612_ *YM2612, int **buf, int length); /*int YM2612_Save(ym2612_ *YM2612, unsigned char SAVE[0x200]); int YM2612_Restore(ym2612_ *YM2612, unsigned char SAVE[0x200]);*/ /* Maxim: muting (bits 0-5 for channels 0-5) */ int YM2612_GetMute(ym2612_ *YM2612); void YM2612_SetMute(ym2612_ *YM2612, int val); void YM2612_SetOptions(int Flags); /* Gens */ void YM2612_DacAndTimers_Update(ym2612_ *YM2612, int **buffer, int length); void YM2612_Special_Update(ym2612_ *YM2612); /* end */ // used for foward... void Update_Chan_Algo0(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo1(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo2(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo3(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo4(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo5(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo6(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo7(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo0_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo1_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo2_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo3_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo4_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo5_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo6_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo7_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo0_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo1_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo2_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo3_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo4_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo5_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo6_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo7_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo0_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo1_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo2_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo3_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo4_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo5_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo6_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); void Update_Chan_Algo7_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); // used for foward... void Env_Attack_Next(slot_ *SL); void Env_Decay_Next(slot_ *SL); void Env_Substain_Next(slot_ *SL); void Env_Release_Next(slot_ *SL); void Env_NULL_Next(slot_ *SL); #endif ================================================ FILE: VGMPlay/chips/ym3438.c ================================================ // // Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // Nuked OPN2(Yamaha YM3438) emulator. // Thanks: // Silicon Pr0n: // Yamaha YM3438 decap and die shot(digshadow). // OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): // OPL2 ROMs. // // version: 1.0.7 // #include #include "ym3438.h" #define OUTPUT_FACTOR 11 #define OUTPUT_FACTOR_F 12 #define FILTER_CUTOFF 0.512331301282628 // 5894Hz single pole IIR low pass #define FILTER_CUTOFF_I (1-FILTER_CUTOFF) enum { eg_num_attack = 0, eg_num_decay = 1, eg_num_sustain = 2, eg_num_release = 3 }; /* logsin table */ static const Bit16u logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; /* exp table */ static const Bit16u exprom[256] = { 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa }; /* Note table */ static const Bit32u fn_note[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 }; /* Envelope generator */ static const Bit32u eg_stephi[4][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 1, 0 } }; static const Bit8u eg_am_shift[4] = { 7, 3, 1, 0 }; /* Phase generator */ static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; static const Bit32u pg_lfo_sh1[8][8] = { { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7, 1, 1 }, { 7, 7, 7, 7, 1, 1, 1, 1 }, { 7, 7, 7, 1, 1, 1, 1, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 } }; static const Bit32u pg_lfo_sh2[8][8] = { { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 2, 2, 2, 2 }, { 7, 7, 7, 2, 2, 2, 7, 7 }, { 7, 7, 2, 2, 7, 7, 2, 2 }, { 7, 7, 2, 7, 7, 7, 2, 7 }, { 7, 7, 7, 2, 7, 7, 2, 1 }, { 7, 7, 7, 2, 7, 7, 2, 1 }, { 7, 7, 7, 2, 7, 7, 2, 1 } }; /* Address decoder */ static const Bit32u op_offset[12] = { 0x000, /* Ch1 OP1/OP2 */ 0x001, /* Ch2 OP1/OP2 */ 0x002, /* Ch3 OP1/OP2 */ 0x100, /* Ch4 OP1/OP2 */ 0x101, /* Ch5 OP1/OP2 */ 0x102, /* Ch6 OP1/OP2 */ 0x004, /* Ch1 OP3/OP4 */ 0x005, /* Ch2 OP3/OP4 */ 0x006, /* Ch3 OP3/OP4 */ 0x104, /* Ch4 OP3/OP4 */ 0x105, /* Ch5 OP3/OP4 */ 0x106 /* Ch6 OP3/OP4 */ }; static const Bit32u ch_offset[6] = { 0x000, /* Ch1 */ 0x001, /* Ch2 */ 0x002, /* Ch3 */ 0x100, /* Ch4 */ 0x101, /* Ch5 */ 0x102 /* Ch6 */ }; /* LFO */ static const Bit32u lfo_cycles[8] = { 108, 77, 71, 67, 62, 44, 8, 5 }; /* FM algorithm */ static const Bit32u fm_algorithm[4][6][8] = { { { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ }, { { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ }, { { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ }, { { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ } }; static Bit32u chip_type = ym3438_type_discrete; static Bit32u use_filter = 0; void OPN2_DoIO(ym3438_t *chip) { /* Write signal check */ chip->write_a_en = (chip->write_a & 0x03) == 0x01; chip->write_d_en = (chip->write_d & 0x03) == 0x01; chip->write_a <<= 1; chip->write_d <<= 1; /* Busy counter */ chip->busy = chip->write_busy; chip->write_busy_cnt += chip->write_busy; chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; chip->write_busy_cnt &= 0x1f; } void OPN2_DoRegWrite(ym3438_t *chip) { Bit32u i; Bit32u slot = chip->slot % 12; Bit32u address; Bit32u channel = chip->channel; /* Update registers */ if (chip->write_fm_data) { /* Slot */ if (op_offset[slot] == (chip->address & 0x107)) { if (chip->address & 0x08) { /* OP2, OP4 */ slot += 12; } address = chip->address & 0xf0; switch (address) { case 0x30: /* DT, MULTI */ chip->multi[slot] = chip->data & 0x0f; if (!chip->multi[slot]) { chip->multi[slot] = 1; } else { chip->multi[slot] <<= 1; } chip->dt[slot] = (chip->data >> 4) & 0x07; break; case 0x40: /* TL */ chip->tl[slot] = chip->data & 0x7f; break; case 0x50: /* KS, AR */ chip->ar[slot] = chip->data & 0x1f; chip->ks[slot] = (chip->data >> 6) & 0x03; break; case 0x60: /* AM, DR */ chip->dr[slot] = chip->data & 0x1f; chip->am[slot] = (chip->data >> 7) & 0x01; break; case 0x70: /* SR */ chip->sr[slot] = chip->data & 0x1f; break; case 0x80: /* SL, RR */ chip->rr[slot] = chip->data & 0x0f; chip->sl[slot] = (chip->data >> 4) & 0x0f; chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; break; case 0x90: /* SSG-EG */ chip->ssg_eg[slot] = chip->data & 0x0f; break; default: break; } } /* Channel */ if (ch_offset[channel] == (chip->address & 0x103)) { address = chip->address & 0xfc; switch (address) { case 0xa0: chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; break; case 0xa4: chip->reg_a4 = chip->data & 0xff; break; case 0xa8: chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; break; case 0xac: chip->reg_ac = chip->data & 0xff; break; case 0xb0: chip->connect[channel] = chip->data & 0x07; chip->fb[channel] = (chip->data >> 3) & 0x07; break; case 0xb4: chip->pms[channel] = chip->data & 0x07; chip->ams[channel] = (chip->data >> 4) & 0x03; chip->pan_l[channel] = (chip->data >> 7) & 0x01; chip->pan_r[channel] = (chip->data >> 6) & 0x01; break; default: break; } } } if (chip->write_a_en || chip->write_d_en) { /* Data */ if (chip->write_a_en) { chip->write_fm_data = 0; } if (chip->write_fm_address && chip->write_d_en) { chip->write_fm_data = 1; } /* Address */ if (chip->write_a_en) { if ((chip->write_data & 0xf0) != 0x00) { /* FM Write */ chip->address = chip->write_data; chip->write_fm_address = 1; } else { /* SSG write */ chip->write_fm_address = 0; } } /* FM Mode */ /* Data */ if (chip->write_d_en && (chip->write_data & 0x100) == 0) { switch (chip->address) { case 0x21: /* LSI test 1 */ for (i = 0; i < 8; i++) { chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; } break; case 0x22: /* LFO control */ if ((chip->write_data >> 3) & 0x01) { chip->lfo_en = 0x7f; } else { chip->lfo_en = 0; } chip->lfo_freq = chip->write_data & 0x07; break; case 0x24: /* Timer A */ chip->timer_a_reg &= 0x03; chip->timer_a_reg |= (chip->write_data & 0xff) << 2; break; case 0x25: chip->timer_a_reg &= 0x3fc; chip->timer_a_reg |= chip->write_data & 0x03; break; case 0x26: /* Timer B */ chip->timer_b_reg = chip->write_data & 0xff; break; case 0x27: /* CSM, Timer control */ chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; chip->mode_csm = chip->mode_ch3 == 2; chip->timer_a_load = chip->write_data & 0x01; chip->timer_a_enable = (chip->write_data >> 2) & 0x01; chip->timer_a_reset = (chip->write_data >> 4) & 0x01; chip->timer_b_load = (chip->write_data >> 1) & 0x01; chip->timer_b_enable = (chip->write_data >> 3) & 0x01; chip->timer_b_reset = (chip->write_data >> 5) & 0x01; break; case 0x28: /* Key on/off */ for (i = 0; i < 4; i++) { chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; } if ((chip->write_data & 0x03) == 0x03) { /* Invalid address */ chip->mode_kon_channel = 0xff; } else { chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; } break; case 0x2a: /* DAC data */ chip->dacdata &= 0x01; chip->dacdata |= (chip->write_data ^ 0x80) << 1; break; case 0x2b: /* DAC enable */ chip->dacen = chip->write_data >> 7; break; case 0x2c: /* LSI test 2 */ for (i = 0; i < 8; i++) { chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; } chip->dacdata &= 0x1fe; chip->dacdata |= chip->mode_test_2c[3]; chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; break; default: break; } } /* Address */ if (chip->write_a_en) { chip->write_fm_mode_a = chip->write_data & 0xff; } } if (chip->write_fm_data) { chip->data = chip->write_data & 0xff; } } void OPN2_PhaseCalcIncrement(ym3438_t *chip) { Bit32u fnum = chip->pg_fnum; Bit32u fnum_h = fnum >> 4; Bit32u fm; Bit32u basefreq; Bit8u lfo = chip->lfo_pm; Bit8u lfo_l = lfo & 0x0f; Bit8u pms = chip->pms[chip->channel]; Bit8u dt = chip->dt[chip->slot]; Bit8u dt_l = dt & 0x03; Bit8u detune = 0; Bit8u block, note; Bit8u sum, sum_h, sum_l; Bit8u kcode = chip->pg_kcode; fnum <<= 1; /* Apply LFO */ if (lfo_l & 0x08) { lfo_l ^= 0x0f; } fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); if (pms > 5) { fm <<= pms - 5; } fm >>= 2; if (lfo & 0x10) { fnum -= fm; } else { fnum += fm; } fnum &= 0xfff; basefreq = (fnum << chip->pg_block) >> 2; /* Apply detune */ if (dt_l) { if (kcode > 0x1c) { kcode = 0x1c; } block = kcode >> 2; note = kcode & 0x03; sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); sum_h = sum >> 1; sum_l = sum & 0x01; detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); } if (dt & 0x04) { basefreq -= detune; } else { basefreq += detune; } basefreq &= 0x1ffff; chip->pg_inc[chip->slot] = (basefreq * chip->multi[chip->slot]) >> 1; chip->pg_inc[chip->slot] &= 0xfffff; } void OPN2_PhaseGenerate(ym3438_t *chip) { Bit32u slot; /* Mask increment */ slot = (chip->slot + 20) % 24; if (chip->pg_reset[slot]) { chip->pg_inc[slot] = 0; } /* Phase step */ slot = (chip->slot + 19) % 24; chip->pg_phase[slot] += chip->pg_inc[slot]; chip->pg_phase[slot] &= 0xfffff; if (chip->pg_reset[slot] || chip->mode_test_21[3]) { chip->pg_phase[slot] = 0; } } void OPN2_EnvelopeSSGEG(ym3438_t *chip) { Bit32u slot = chip->slot; Bit8u direction = 0; chip->eg_ssg_pgrst_latch[slot] = 0; chip->eg_ssg_repeat_latch[slot] = 0; chip->eg_ssg_hold_up_latch[slot] = 0; chip->eg_ssg_inv[slot] = 0; if (chip->ssg_eg[slot] & 0x08) { direction = chip->eg_ssg_dir[slot]; if (chip->eg_level[slot] & 0x200) { /* Reset */ if ((chip->ssg_eg[slot] & 0x03) == 0x00) { chip->eg_ssg_pgrst_latch[slot] = 1; } /* Repeat */ if ((chip->ssg_eg[slot] & 0x01) == 0x00) { chip->eg_ssg_repeat_latch[slot] = 1; } /* Inverse */ if ((chip->ssg_eg[slot] & 0x03) == 0x02) { direction ^= 1; } if ((chip->ssg_eg[slot] & 0x03) == 0x03) { direction = 1; } } /* Hold up */ if (chip->eg_kon_latch[slot] && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) { chip->eg_ssg_hold_up_latch[slot] = 1; } direction &= chip->eg_kon[slot]; chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) & chip->eg_kon[slot]; } chip->eg_ssg_dir[slot] = direction; chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; } void OPN2_EnvelopeADSR(ym3438_t *chip) { Bit32u slot = (chip->slot + 22) % 24; Bit8u nkon = chip->eg_kon_latch[slot]; Bit8u okon = chip->eg_kon[slot]; Bit8u kon_event; Bit8u koff_event; Bit8u eg_off; Bit16s level; Bit16s nextlevel = 0; Bit16s ssg_level; Bit8u nextstate = chip->eg_state[slot]; Bit16s inc = 0; chip->eg_read[0] = chip->eg_read_inc; chip->eg_read_inc = chip->eg_inc > 0; /* Reset phase generator */ chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; /* KeyOn/Off */ kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); koff_event = okon && !nkon; ssg_level = level = (Bit16s)chip->eg_level[slot]; if (chip->eg_ssg_inv[slot]) { /* Inverse */ ssg_level = 512 - level; ssg_level &= 0x3ff; } if (koff_event) { level = ssg_level; } if (chip->eg_ssg_enable[slot]) { eg_off = level >> 9; } else { eg_off = (level & 0x3f0) == 0x3f0; } nextlevel = level; if (kon_event) { nextstate = eg_num_attack; /* Instant attack */ if (chip->eg_ratemax) { nextlevel = 0; } else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) { inc = (~level << chip->eg_inc) >> 5; } } else { switch (chip->eg_state[slot]) { case eg_num_attack: if (level == 0) { nextstate = eg_num_decay; } else if(chip->eg_inc && !chip->eg_ratemax && nkon) { inc = (~level << chip->eg_inc) >> 5; } break; case eg_num_decay: if ((level >> 5) == chip->eg_sl[1]) { nextstate = eg_num_sustain; } else if (!eg_off && chip->eg_inc) { inc = 1 << (chip->eg_inc - 1); if (chip->eg_ssg_enable[slot]) { inc <<= 2; } } break; case eg_num_sustain: case eg_num_release: if (!eg_off && chip->eg_inc) { inc = 1 << (chip->eg_inc - 1); if (chip->eg_ssg_enable[slot]) { inc <<= 2; } } break; default: break; } if (!nkon) { nextstate = eg_num_release; } } if (chip->eg_kon_csm[slot]) { nextlevel |= chip->eg_tl[1] << 3; } /* Envelope off */ if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) { nextstate = eg_num_release; nextlevel = 0x3ff; } nextlevel += inc; chip->eg_kon[slot] = chip->eg_kon_latch[slot]; chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; chip->eg_state[slot] = nextstate; } void OPN2_EnvelopePrepare(ym3438_t *chip) { Bit8u rate; Bit8u sum; Bit8u inc = 0; Bit32u slot = chip->slot; Bit8u rate_sel; /* Prepare increment */ rate = (chip->eg_rate << 1) + chip->eg_ksv; if (rate > 0x3f) { rate = 0x3f; } sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; if (chip->eg_rate != 0 && chip->eg_quotient == 2) { if (rate < 48) { switch (sum) { case 12: inc = 1; break; case 13: inc = (rate >> 1) & 0x01; break; case 14: inc = rate & 0x01; break; default: break; } } else { inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; if (inc > 4) { inc = 4; } } } chip->eg_inc = inc; chip->eg_ratemax = (rate >> 1) == 0x1f; /* Prepare rate & ksv */ rate_sel = chip->eg_state[slot]; if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) { rate_sel = eg_num_attack; } switch (rate_sel) { case eg_num_attack: chip->eg_rate = chip->ar[slot]; break; case eg_num_decay: chip->eg_rate = chip->dr[slot]; break; case eg_num_sustain: chip->eg_rate = chip->sr[slot]; break; case eg_num_release: chip->eg_rate = (chip->rr[slot] << 1) | 0x01; break; default: break; } chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); if (chip->am[slot]) { chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; } else { chip->eg_lfo_am = 0; } /* Delay TL & SL value */ chip->eg_tl[1] = chip->eg_tl[0]; chip->eg_tl[0] = chip->tl[slot]; chip->eg_sl[1] = chip->eg_sl[0]; chip->eg_sl[0] = chip->sl[slot]; } void OPN2_EnvelopeGenerate(ym3438_t *chip) { Bit32u slot = (chip->slot + 23) % 24; Bit16u level; level = chip->eg_level[slot]; if (chip->eg_ssg_inv[slot]) { /* Inverse */ level = 512 - level; } if (chip->mode_test_21[5]) { level = 0; } level &= 0x3ff; /* Apply AM LFO */ level += chip->eg_lfo_am; /* Apply TL */ if (!(chip->mode_csm && chip->channel == 2 + 1)) { level += chip->eg_tl[0] << 3; } if (level > 0x3ff) { level = 0x3ff; } chip->eg_out[slot] = level; } void OPN2_UpdateLFO(ym3438_t *chip) { if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) { chip->lfo_quotient = 0; chip->lfo_cnt++; } else { chip->lfo_quotient += chip->lfo_inc; } chip->lfo_cnt &= chip->lfo_en; } void OPN2_FMPrepare(ym3438_t *chip) { Bit32u slot = (chip->slot + 6) % 24; Bit32u channel = chip->channel; Bit16s mod, mod1, mod2; Bit32u op = slot / 6; Bit8u connect = chip->connect[channel]; Bit32u prevslot = (chip->slot + 18) % 24; /* Calculate modulation */ mod1 = mod2 = 0; if (fm_algorithm[op][0][connect]) { mod2 |= chip->fm_op1[channel][0]; } if (fm_algorithm[op][1][connect]) { mod1 |= chip->fm_op1[channel][1]; } if (fm_algorithm[op][2][connect]) { mod1 |= chip->fm_op2[channel]; } if (fm_algorithm[op][3][connect]) { mod2 |= chip->fm_out[prevslot]; } if (fm_algorithm[op][4][connect]) { mod1 |= chip->fm_out[prevslot]; } mod = mod1 + mod2; if (op == 0) { /* Feedback */ mod = mod >> (10 - chip->fb[channel]); if (!chip->fb[channel]) { mod = 0; } } else { mod >>= 1; } chip->fm_mod[slot] = mod; slot = (chip->slot + 18) % 24; /* OP1 */ if (slot / 6 == 0) { chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; chip->fm_op1[channel][0] = chip->fm_out[slot]; } /* OP2 */ if (slot / 6 == 2) { chip->fm_op2[channel] = chip->fm_out[slot]; } } void OPN2_ChGenerate(ym3438_t *chip) { Bit32u slot = (chip->slot + 18) % 24; Bit32u channel = chip->channel; Bit32u op = slot / 6; Bit32u test_dac = chip->mode_test_2c[5]; Bit16s acc = chip->ch_acc[channel]; Bit16s add = test_dac; Bit16s sum = 0; if (op == 0 && !test_dac) { acc = 0; } if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) { add += chip->fm_out[slot] >> 5; } sum = acc + add; /* Clamp */ if (sum > 255) { sum = 255; } else if(sum < -256) { sum = -256; } if (op == 0 || test_dac) { chip->ch_out[channel] = chip->ch_acc[channel]; } chip->ch_acc[channel] = sum; } void OPN2_ChOutput(ym3438_t *chip) { Bit32u cycles = chip->cycles; Bit32u channel = chip->channel; Bit32u test_dac = chip->mode_test_2c[5]; Bit16s out; Bit16s sign; Bit32u out_en; chip->ch_read = chip->ch_lock; if (chip->slot < 12) { /* Ch 4,5,6 */ channel++; } if ((cycles & 3) == 0) { if (!test_dac) { /* Lock value */ chip->ch_lock = chip->ch_out[channel]; } chip->ch_lock_l = chip->pan_l[channel]; chip->ch_lock_r = chip->pan_r[channel]; } /* Ch 6 */ if (((cycles >> 2) == 1 && chip->dacen) || test_dac) { out = (Bit16s)chip->dacdata; out <<= 7; out >>= 7; } else { out = chip->ch_lock; } chip->mol = 0; chip->mor = 0; if (chip_type == ym3438_type_ym2612) { out_en = ((cycles & 3) == 3) || test_dac; /* YM2612 DAC emulation(not verified) */ sign = out >> 8; if (out >= 0) { out++; sign++; } if (chip->ch_lock_l && out_en) { chip->mol = out; } else { chip->mol = sign; } if (chip->ch_lock_r && out_en) { chip->mor = out; } else { chip->mor = sign; } /* Amplify signal */ chip->mol *= 3; chip->mor *= 3; } else { out_en = ((cycles & 3) != 0) || test_dac; /* Discrete YM3438 seems has the ladder effect too */ if (out >= 0 && chip_type == ym3438_type_discrete) { out++; } if (chip->ch_lock_l && out_en) { chip->mol = out; } if (chip->ch_lock_r && out_en) { chip->mor = out; } } } void OPN2_FMGenerate(ym3438_t *chip) { Bit32u slot = (chip->slot + 19) % 24; /* Calculate phase */ Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; Bit16u quarter; Bit16u level; Bit16s output; if (phase & 0x100) { quarter = (phase ^ 0xff) & 0xff; } else { quarter = phase & 0xff; } level = logsinrom[quarter]; /* Apply envelope */ level += chip->eg_out[slot] << 2; /* Transform */ if (level > 0x1fff) { level = 0x1fff; } output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); if (phase & 0x200) { output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; } else { output = output ^ (chip->mode_test_21[4] << 13); } output <<= 2; output >>= 2; chip->fm_out[slot] = output; } void OPN2_DoTimerA(ym3438_t *chip) { Bit16u time; Bit8u load; load = chip->timer_a_overflow; if (chip->cycles == 2) { /* Lock load value */ load |= (!chip->timer_a_load_lock && chip->timer_a_load); chip->timer_a_load_lock = chip->timer_a_load; if (chip->mode_csm) { /* CSM KeyOn */ chip->mode_kon_csm = load; } else { chip->mode_kon_csm = 0; } } /* Load counter */ if (chip->timer_a_load_latch) { time = chip->timer_a_reg; } else { time = chip->timer_a_cnt; } chip->timer_a_load_latch = load; /* Increase counter */ if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) { time++; } /* Set overflow flag */ if (chip->timer_a_reset) { chip->timer_a_reset = 0; chip->timer_a_overflow_flag = 0; } else { chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; } chip->timer_a_overflow = (time >> 10); chip->timer_a_cnt = time & 0x3ff; } void OPN2_DoTimerB(ym3438_t *chip) { Bit16u time; Bit8u load; load = chip->timer_b_overflow; if (chip->cycles == 2) { /* Lock load value */ load |= (!chip->timer_b_load_lock && chip->timer_b_load); chip->timer_b_load_lock = chip->timer_b_load; } /* Load counter */ if (chip->timer_b_load_latch) { time = chip->timer_b_reg; } else { time = chip->timer_b_cnt; } chip->timer_b_load_latch = load; /* Increase counter */ if (chip->cycles == 1) { chip->timer_b_subcnt++; } if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) { time++; } chip->timer_b_subcnt &= 0x0f; /* Set overflow flag */ if (chip->timer_b_reset) { chip->timer_b_reset = 0; chip->timer_b_overflow_flag = 0; } else { chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; } chip->timer_b_overflow = (time >> 8); chip->timer_b_cnt = time & 0xff; } void OPN2_KeyOn(ym3438_t*chip) { /* Key On */ chip->eg_kon_latch[chip->slot] = chip->mode_kon[chip->slot]; chip->eg_kon_csm[chip->slot] = 0; if (chip->channel == 2 && chip->mode_kon_csm) { /* CSM Key On */ chip->eg_kon_latch[chip->slot] = 1; chip->eg_kon_csm[chip->slot] = 1; } if (chip->cycles == chip->mode_kon_channel) { /* OP1 */ chip->mode_kon[chip->channel] = chip->mode_kon_operator[0]; /* OP2 */ chip->mode_kon[chip->channel + 12] = chip->mode_kon_operator[1]; /* OP3 */ chip->mode_kon[chip->channel + 6] = chip->mode_kon_operator[2]; /* OP4 */ chip->mode_kon[chip->channel + 18] = chip->mode_kon_operator[3]; } } void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) { Bit32u i, rateratio; rateratio = chip->rateratio; memset(chip, 0, sizeof(ym3438_t)); for (i = 0; i < 24; i++) { chip->eg_out[i] = 0x3ff; chip->eg_level[i] = 0x3ff; chip->eg_state[i] = eg_num_release; chip->multi[i] = 1; } for (i = 0; i < 6; i++) { chip->pan_l[i] = 1; chip->pan_r[i] = 1; } if (rate != 0) { chip->rateratio = (Bit32u)((((Bit64u)144 * rate) << RSM_FRAC) / clock); } else { chip->rateratio = rateratio; } } void OPN2_SetChipType(Bit32u type) { use_filter = 0; if(type == ym3438_type_ym2612) use_filter = 1; if(type == ym3438_type_ym2612_u) type = ym3438_type_ym2612; chip_type = type; } void OPN2_Clock(ym3438_t *chip, Bit32s *buffer) { chip->lfo_inc = chip->mode_test_21[1]; chip->pg_read >>= 1; chip->eg_read[1] >>= 1; chip->eg_cycle++; /* Lock envelope generator timer value */ if (chip->cycles == 1 && chip->eg_quotient == 2) { if (chip->eg_cycle_stop) { chip->eg_shift_lock = 0; } else { chip->eg_shift_lock = chip->eg_shift + 1; } chip->eg_timer_low_lock = chip->eg_timer & 0x03; } /* Cycle specific functions */ switch (chip->cycles) { case 0: chip->lfo_pm = chip->lfo_cnt >> 2; if (chip->lfo_cnt & 0x40) { chip->lfo_am = chip->lfo_cnt & 0x3f; } else { chip->lfo_am = chip->lfo_cnt ^ 0x3f; } chip->lfo_am <<= 1; break; case 1: chip->eg_quotient++; chip->eg_quotient %= 3; chip->eg_cycle = 0; chip->eg_cycle_stop = 1; chip->eg_shift = 0; chip->eg_timer_inc |= chip->eg_quotient >> 1; chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; chip->eg_timer_inc = chip->eg_timer >> 12; chip->eg_timer &= 0xfff; break; case 2: chip->pg_read = chip->pg_phase[21] & 0x3ff; chip->eg_read[1] = chip->eg_out[0]; break; case 13: chip->eg_cycle = 0; chip->eg_cycle_stop = 1; chip->eg_shift = 0; chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; chip->eg_timer_inc = chip->eg_timer >> 12; chip->eg_timer &= 0xfff; break; case 23: chip->lfo_inc |= 1; break; } chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) { chip->eg_shift = chip->eg_cycle; chip->eg_cycle_stop = 0; } OPN2_DoIO(chip); OPN2_DoTimerA(chip); OPN2_DoTimerB(chip); OPN2_KeyOn(chip); OPN2_ChOutput(chip); OPN2_ChGenerate(chip); OPN2_FMPrepare(chip); OPN2_FMGenerate(chip); OPN2_PhaseGenerate(chip); OPN2_PhaseCalcIncrement(chip); OPN2_EnvelopeADSR(chip); OPN2_EnvelopeGenerate(chip); OPN2_EnvelopeSSGEG(chip); OPN2_EnvelopePrepare(chip); /* Prepare fnum & block */ if (chip->mode_ch3) { /* Channel 3 special mode */ switch (chip->slot) { case 1: /* OP1 */ chip->pg_fnum = chip->fnum_3ch[1]; chip->pg_block = chip->block_3ch[1]; chip->pg_kcode = chip->kcode_3ch[1]; break; case 7: /* OP3 */ chip->pg_fnum = chip->fnum_3ch[0]; chip->pg_block = chip->block_3ch[0]; chip->pg_kcode = chip->kcode_3ch[0]; break; case 13: /* OP2 */ chip->pg_fnum = chip->fnum_3ch[2]; chip->pg_block = chip->block_3ch[2]; chip->pg_kcode = chip->kcode_3ch[2]; break; case 19: /* OP4 */ default: chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; chip->pg_block = chip->block[(chip->channel + 1) % 6]; chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; break; } } else { chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; chip->pg_block = chip->block[(chip->channel + 1) % 6]; chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; } OPN2_UpdateLFO(chip); OPN2_DoRegWrite(chip); chip->cycles = (chip->cycles + 1) % 24; chip->slot = chip->cycles; chip->channel = chip->cycles % 6; buffer[0] = chip->mol; buffer[1] = chip->mor; } void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) { port &= 3; chip->write_data = ((port << 7) & 0x100) | data; if (port & 1) { /* Data */ chip->write_d |= 1; } else { /* Address */ chip->write_a |= 1; } } void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) { chip->pin_test_in = value & 1; } Bit32u OPN2_ReadTestPin(ym3438_t *chip) { if (!chip->mode_test_2c[7]) { return 0; } return chip->cycles == 23; } Bit32u OPN2_ReadIRQPin(ym3438_t *chip) { return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; } Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) { if ((port & 3) == 0 || chip_type == ym3438_type_asic) { if (chip->mode_test_21[6]) { /* Read test data */ Bit16u testdata = ((chip->pg_read & 0x01) << 15) | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); if (chip->mode_test_2c[4]) { testdata |= chip->ch_read & 0x1ff; } else { testdata |= chip->fm_out[(chip->slot + 18) % 24] & 0x3fff; } if (chip->mode_test_21[7]) { return testdata & 0xff; } else { return testdata >> 8; } } else { return (chip->busy << 7) | (chip->timer_b_overflow_flag << 1) | chip->timer_a_overflow_flag; } } return 0; } void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) { Bit64u time1, time2; Bit32s buffer[2]; Bit64u skip; if (chip->writebuf[chip->writebuf_last].port & 0x04) { OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, chip->writebuf[chip->writebuf_last].data); chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; while (skip--) { OPN2_Clock(chip, buffer); } } chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; chip->writebuf[chip->writebuf_last].data = data; time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; if (time1 < time2) { time1 = time2; } chip->writebuf[chip->writebuf_last].time = time1; chip->writebuf_lasttime = time1; chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; } void OPN2_GenerateResampled(ym3438_t *chip, Bit32s *buf) { Bit32u i; Bit32s buffer[2]; Bit32u mute; while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; chip->samples[0] = chip->samples[1] = 0; for (i = 0; i < 24; i++) { switch (chip->cycles >> 2) { case 0: // Ch 2 mute = chip->mute[1]; break; case 1: // Ch 6, DAC mute = chip->mute[5 + chip->dacen]; break; case 2: // Ch 4 mute = chip->mute[3]; break; case 3: // Ch 1 mute = chip->mute[0]; break; case 4: // Ch 5 mute = chip->mute[4]; break; case 5: // Ch 3 mute = chip->mute[2]; break; default: mute = 0; break; } OPN2_Clock(chip, buffer); if (!mute) { chip->samples[0] += buffer[0]; chip->samples[1] += buffer[1]; } while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) { if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) { break; } chip->writebuf[chip->writebuf_cur].port &= 0x03; OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } if(!use_filter) { chip->samples[0] *= OUTPUT_FACTOR; chip->samples[1] *= OUTPUT_FACTOR; } else { chip->samples[0] = chip->oldsamples[0] + FILTER_CUTOFF_I * (chip->samples[0]*OUTPUT_FACTOR_F - chip->oldsamples[0]); chip->samples[1] = chip->oldsamples[1] + FILTER_CUTOFF_I * (chip->samples[1]*OUTPUT_FACTOR_F - chip->oldsamples[1]); } chip->samplecnt -= chip->rateratio; } buf[0] = (Bit32s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + chip->samples[0] * chip->samplecnt) / chip->rateratio); buf[1] = (Bit32s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + chip->samples[1] * chip->samplecnt) / chip->rateratio); chip->samplecnt += 1 << RSM_FRAC; } void OPN2_GenerateStream(ym3438_t *chip, Bit32s **sndptr, Bit32u numsamples) { Bit32u i; Bit32s *smpl, *smpr; Bit32s buffer[2]; smpl = sndptr[0]; smpr = sndptr[1]; for (i = 0; i < numsamples; i++) { OPN2_GenerateResampled(chip, buffer); *smpl++ = buffer[0]; *smpr++ = buffer[1]; } } void OPN2_SetOptions(Bit8u flags) { switch ((flags >> 3) & 0x03) { case 0x00: // YM2612 default: OPN2_SetChipType(ym3438_type_ym2612); break; case 0x01: // ASIC YM3438 OPN2_SetChipType(ym3438_type_asic); break; case 0x02: // Discrete YM3438 OPN2_SetChipType(ym3438_type_discrete); break; case 0x03: // YM2612 without filter emulation OPN2_SetChipType(ym3438_type_ym2612_u); break; } } void OPN2_SetMute(ym3438_t *chip, Bit32u mute) { Bit32u i; for (i = 0; i < 7; i++) { chip->mute[i] = (mute >> i) & 0x01; } } ================================================ FILE: VGMPlay/chips/ym3438.h ================================================ // // Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // Nuked OPN2(Yamaha YM3438) emulator. // Thanks: // Silicon Pr0n: // Yamaha YM3438 decap and die shot(digshadow). // OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): // OPL2 ROMs. // // version: 1.0.7 // #ifndef YM3438_H #define YM3438_H #define RSM_FRAC 10 #define OPN_WRITEBUF_SIZE 2048 #define OPN_WRITEBUF_DELAY 15 enum { ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */ ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */ ym3438_type_ym2612 = 2, /* YM2612 (MD1, MD2 VA2) */ ym3438_type_ym2612_u = 3 /* YM2612 without lowpass filter */ }; #include "mamedef.h" typedef UINT64 Bit64u; typedef INT64 Bit64s; typedef UINT32 Bit32u; typedef INT32 Bit32s; typedef UINT16 Bit16u; typedef INT16 Bit16s; typedef UINT8 Bit8u; typedef INT8 Bit8s; typedef struct _opn2_writebuf { Bit64u time; Bit8u port; Bit8u data; } opn2_writebuf; typedef struct { Bit32u cycles; Bit32u slot; Bit32u channel; Bit16s mol, mor; /* IO */ Bit16u write_data; Bit8u write_a; Bit8u write_d; Bit8u write_a_en; Bit8u write_d_en; Bit8u write_busy; Bit8u write_busy_cnt; Bit8u write_fm_address; Bit8u write_fm_data; Bit8u write_fm_mode_a; Bit16u address; Bit8u data; Bit8u pin_test_in; Bit8u pin_irq; Bit8u busy; /* LFO */ Bit8u lfo_en; Bit8u lfo_freq; Bit8u lfo_pm; Bit8u lfo_am; Bit8u lfo_cnt; Bit8u lfo_inc; Bit8u lfo_quotient; /* Phase generator */ Bit16u pg_fnum; Bit8u pg_block; Bit8u pg_kcode; Bit32u pg_inc[24]; Bit32u pg_phase[24]; Bit8u pg_reset[24]; Bit32u pg_read; /* Envelope generator */ Bit8u eg_cycle; Bit8u eg_cycle_stop; Bit8u eg_shift; Bit8u eg_shift_lock; Bit8u eg_timer_low_lock; Bit16u eg_timer; Bit8u eg_timer_inc; Bit16u eg_quotient; Bit8u eg_custom_timer; Bit8u eg_rate; Bit8u eg_ksv; Bit8u eg_inc; Bit8u eg_ratemax; Bit8u eg_sl[2]; Bit8u eg_lfo_am; Bit8u eg_tl[2]; Bit8u eg_state[24]; Bit16u eg_level[24]; Bit16u eg_out[24]; Bit8u eg_kon[24]; Bit8u eg_kon_csm[24]; Bit8u eg_kon_latch[24]; Bit8u eg_csm_mode[24]; Bit8u eg_ssg_enable[24]; Bit8u eg_ssg_pgrst_latch[24]; Bit8u eg_ssg_repeat_latch[24]; Bit8u eg_ssg_hold_up_latch[24]; Bit8u eg_ssg_dir[24]; Bit8u eg_ssg_inv[24]; Bit32u eg_read[2]; Bit8u eg_read_inc; /* FM */ Bit16s fm_op1[6][2]; Bit16s fm_op2[6]; Bit16s fm_out[24]; Bit16u fm_mod[24]; /* Channel */ Bit16s ch_acc[6]; Bit16s ch_out[6]; Bit16s ch_lock; Bit8u ch_lock_l; Bit8u ch_lock_r; Bit16s ch_read; /* Timer */ Bit16u timer_a_cnt; Bit16u timer_a_reg; Bit8u timer_a_load_lock; Bit8u timer_a_load; Bit8u timer_a_enable; Bit8u timer_a_reset; Bit8u timer_a_load_latch; Bit8u timer_a_overflow_flag; Bit8u timer_a_overflow; Bit16u timer_b_cnt; Bit8u timer_b_subcnt; Bit16u timer_b_reg; Bit8u timer_b_load_lock; Bit8u timer_b_load; Bit8u timer_b_enable; Bit8u timer_b_reset; Bit8u timer_b_load_latch; Bit8u timer_b_overflow_flag; Bit8u timer_b_overflow; /* Register set */ Bit8u mode_test_21[8]; Bit8u mode_test_2c[8]; Bit8u mode_ch3; Bit8u mode_kon_channel; Bit8u mode_kon_operator[4]; Bit8u mode_kon[24]; Bit8u mode_csm; Bit8u mode_kon_csm; Bit8u dacen; Bit16s dacdata; Bit8u ks[24]; Bit8u ar[24]; Bit8u sr[24]; Bit8u dt[24]; Bit8u multi[24]; Bit8u sl[24]; Bit8u rr[24]; Bit8u dr[24]; Bit8u am[24]; Bit8u tl[24]; Bit8u ssg_eg[24]; Bit16u fnum[6]; Bit8u block[6]; Bit8u kcode[6]; Bit16u fnum_3ch[6]; Bit8u block_3ch[6]; Bit8u kcode_3ch[6]; Bit8u reg_a4; Bit8u reg_ac; Bit8u connect[6]; Bit8u fb[6]; Bit8u pan_l[6], pan_r[6]; Bit8u ams[6]; Bit8u pms[6]; Bit32u mute[7]; Bit32s rateratio; Bit32s samplecnt; Bit32s oldsamples[2]; Bit32s samples[2]; Bit64u writebuf_samplecnt; Bit32u writebuf_cur; Bit32u writebuf_last; Bit64u writebuf_lasttime; opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; } ym3438_t; void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock); void OPN2_SetChipType(Bit32u type); void OPN2_Clock(ym3438_t *chip, Bit32s *buffer); void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); Bit32u OPN2_ReadTestPin(ym3438_t *chip); Bit32u OPN2_ReadIRQPin(ym3438_t *chip); Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_GenerateStream(ym3438_t *chip, Bit32s **sndptr, Bit32u numsamples); void OPN2_SetOptions(Bit8u flags); void OPN2_SetMute(ym3438_t *chip, Bit32u mute); #endif ================================================ FILE: VGMPlay/chips/ymdeltat.c ================================================ /* ** ** File: ymdeltat.c ** ** YAMAHA DELTA-T adpcm sound emulation subroutine ** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) ** ** Base program is YM2610 emulator by Hiromitsu Shioya. ** Written by Tatsuyuki Satoh ** Improvements by Jarek Burczynski (bujar at mame dot net) ** ** ** History: ** ** 03-08-2003 Jarek Burczynski: ** - fixed BRDY flag implementation. ** ** 24-07-2003 Jarek Burczynski, Frits Hilderink: ** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset ** ** 22-07-2003 Jarek Burczynski, Frits Hilderink: ** - fixed external memory support ** ** 15-06-2003 Jarek Burczynski: ** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) ** - implemented support for the Limit address register ** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM ** - implemented external memory access (read/write) via the ADPCM data reg reads/writes ** Thanks go to Frits Hilderink for the example code. ** ** 14-06-2003 Jarek Burczynski: ** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO ** - modified EOS handling ** ** 05-04-2003 Jarek Burczynski: ** - implemented partial support for external/processor memory on sample replay ** ** 01-12-2002 Jarek Burczynski: ** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi ** - renamed/removed some YM_DELTAT struct fields ** ** 28-12-2001 Acho A. Tang ** - added EOS status report on ADPCM playback. ** ** 05-08-2001 Jarek Burczynski: ** - now_step is initialized with 0 at the start of play. ** ** 12-06-2001 Jarek Burczynski: ** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. ** Checked on real YM2610 chip - address register is 24 bits wide. ** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. ** ** TO DO: ** Check size of the address register on the other chips.... ** ** Version 0.72 ** ** sound chips that have this unit: ** YM2608 OPNA ** YM2610/B OPNB ** Y8950 MSX AUDIO ** */ #include "mamedef.h" #include //#include "sndintrf.h" #include "ymdeltat.h" #define YM_DELTAT_DELTA_MAX (24576) #define YM_DELTAT_DELTA_MIN (127) #define YM_DELTAT_DELTA_DEF (127) #define YM_DELTAT_DECODE_RANGE 32768 #define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) #define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) /* Forecast to next Forecast (rate = *8) */ /* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ static const INT32 ym_deltat_decode_tableB1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, }; /* delta to next delta (rate= *64) */ /* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ static const INT32 ym_deltat_decode_tableB2[16] = { 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153 }; #if 0 void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT) { logerror("BRDY_callback reached (flag set) !\n"); /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } #endif UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT) { UINT8 v = 0; /* external memory read */ if ( (DELTAT->portstate & 0xe0)==0x20 ) { /* two dummy reads */ if (DELTAT->memread) { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread--; return 0; } if ( DELTAT->now_addr != (DELTAT->end<<1) ) { v = DELTAT->memory[DELTAT->now_addr>>1]; /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ DELTAT->now_addr+=2; /* two nibbles at a time */ /* reset BRDY bit in status register, which means we are reading the memory now */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); /* setup a timer that will callback us in 10 master clock cycles for Y8950 * in the callback set the BRDY flag to 1 , which means we have another data ready. * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); } } return v; } /* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ static const UINT8 dram_rightshift[4]={3,0,0,0}; /* DELTA-T ADPCM write register */ void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v) { if(r>=0x10) return; DELTAT->reg[r] = v; /* stock data */ switch( r ) { case 0x00: /* START: Accessing *external* memory is started when START bit (D7) is set to "1", so you must set all conditions needed for recording/playback before starting. If you access *CPU-managed* memory, recording/playback starts after read/write of ADPCM data register $08. REC: 0 = ADPCM synthesis (playback) 1 = ADPCM analysis (record) MEMDATA: 0 = processor (*CPU-managed*) memory (means: using register $08) 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) SPOFF: controls output pin that should disable the speaker while ADPCM analysis RESET and REPEAT only work with external memory. some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ /* handle emulation mode */ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) { v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ } DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ { /* set PCM BUSY bit */ DELTAT->PCM_BSY = 1; /* start ADPCM */ DELTAT->now_step = 0; DELTAT->acc = 0; DELTAT->prev_acc = 0; DELTAT->adpcml = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->now_data = 0; if (DELTAT->start > DELTAT->end) logerror("DeltaT-Warning: Start: %06X, End: %06X\n", DELTAT->start, DELTAT->end); } if( DELTAT->portstate&0x20 ) /* do we access external memory? */ { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ /* if yes, then let's check if ADPCM memory is mapped and big enough */ if(DELTAT->memory == 0) { #ifdef _DEBUG logerror("YM Delta-T ADPCM rom not mapped\n"); #endif DELTAT->portstate = 0x00; DELTAT->PCM_BSY = 0; } else { if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */ { #ifdef _DEBUG logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end); #endif DELTAT->end = DELTAT->memory_size - 1; } if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */ { #ifdef _DEBUG logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start); #endif DELTAT->portstate = 0x00; DELTAT->PCM_BSY = 0; } } } else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ { DELTAT->now_addr = 0; } if( DELTAT->portstate&0x01 ) { DELTAT->portstate = 0x00; /* clear PCM BUSY bit (in status register) */ DELTAT->PCM_BSY = 0; /* set BRDY flag */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } break; case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ /* handle emulation mode */ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) { v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't have ROM/RAM memory flag bit. */ } DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03]; if ((DELTAT->control2 & 3) != (v & 3)) { /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ if (DELTAT->DRAMportshift != dram_rightshift[v&3]) { DELTAT->DRAMportshift = dram_rightshift[v&3]; /* final shift value depends on chip type and memory type selected: 8 for YM2610 (ROM only), 5 for ROM for Y8950 and YM2608, 5 for x8bit DRAMs for Y8950 and YM2608, 2 for x1bit DRAMs for Y8950 and YM2608. */ /* refresh addresses */ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); } } DELTAT->control2 = v; break; case 0x02: /* Start Address L */ case 0x03: /* Start Address H */ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/ break; case 0x04: /* Stop Address L */ case 0x05: /* Stop Address H */ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end );*/ break; case 0x06: /* Prescale L (ADPCM and Record frq) */ case 0x07: /* Prescale H */ break; case 0x08: /* ADPCM data */ /* some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ /* external memory write */ if ( (DELTAT->portstate & 0xe0)==0x60 ) { if (DELTAT->memread) { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread = 0; } /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ if ( DELTAT->now_addr != (DELTAT->end<<1) ) { DELTAT->memory[DELTAT->now_addr>>1] = v; DELTAT->now_addr+=2; /* two nibbles at a time */ /* reset BRDY bit in status register, which means we are processing the write */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); /* setup a timer that will callback us in 10 master clock cycles for Y8950 * in the callback set the BRDY flag to 1 , which means we have written the data. * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); } return; } /* ADPCM synthesis from CPU */ if ( (DELTAT->portstate & 0xe0)==0x80 ) { DELTAT->CPU_data = v; /* Reset BRDY bit in status register, which means we are full of data */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); return; } break; case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ case 0x0a: /* DELTA-N H */ DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]); DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) ); /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/ break; case 0x0b: /* Output level control (volume, linear) */ { INT32 oldvol = DELTAT->volume; DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE; /* v * ((1<<16)>>8) >> 15; * thus: v * (1<<8) >> 15; * thus: output_range must be (1 << (15+8)) at least * v * ((1<<23)>>8) >> 15; * v * (1<<15) >> 15; */ /*logerror("DELTAT vol = %2x\n",v&0xff);*/ if( oldvol != 0 ) { DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume); } } break; case 0x0c: /* Limit Address L */ case 0x0d: /* Limit Address H */ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/ break; } } void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode) { DELTAT->now_addr = 0; DELTAT->now_step = 0; DELTAT->step = 0; DELTAT->start = 0; DELTAT->end = 0; DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ DELTAT->volume = 0; DELTAT->pan = &DELTAT->output_pointer[pan]; DELTAT->acc = 0; DELTAT->prev_acc = 0; DELTAT->adpcmd = 127; DELTAT->adpcml = 0; DELTAT->emulation_mode = (UINT8)emulation_mode; DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0; DELTAT->control2 = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3]; /* The flag mask register disables the BRDY after the reset, however ** as soon as the mask is enabled the flag needs to be set. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } /*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs) { int r; // to keep adpcml DELTAT->volume = 0; // update for(r=1;r<16;r++) YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]); DELTAT->reg[0] = regs[0]; // current rom data if (DELTAT->memory) DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) ); } //void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT) void YM_DELTAT_savestate(YM_DELTAT *DELTAT) { #ifdef __STATE_H__ state_save_register_device_item(device, 0, DELTAT->portstate); state_save_register_device_item(device, 0, DELTAT->now_addr); state_save_register_device_item(device, 0, DELTAT->now_step); state_save_register_device_item(device, 0, DELTAT->acc); state_save_register_device_item(device, 0, DELTAT->prev_acc); state_save_register_device_item(device, 0, DELTAT->adpcmd); state_save_register_device_item(device, 0, DELTAT->adpcml); #endif }*/ #define YM_DELTAT_Limit(val,max,min) \ { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \ } INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) { UINT32 step; int data; DELTAT->now_step += DELTAT->step; if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) DELTAT->now_addr = 0; if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ if( DELTAT->portstate&0x10 ){ /* repeat start */ DELTAT->now_addr = DELTAT->start<<1; DELTAT->acc = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->prev_acc = 0; }else{ /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); /* clear PCM BUSY bit (reflected in status register) */ DELTAT->PCM_BSY = 0; DELTAT->portstate = 0; DELTAT->adpcml = 0; DELTAT->prev_acc = 0; return; } } if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; else { DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); data = DELTAT->now_data >> 4; } DELTAT->now_addr++; /* 12-06-2001 JB: */ /* YM2610 address register is 24 bits wide.*/ /* The "+1" is there because we use 1 bit more for nibble calculations.*/ /* WARNING: */ /* Side effect: we should take the size of the mapped ROM into account */ //DELTAT->now_addr &= ( (1<<(24+1))-1); DELTAT->now_addr &= DELTAT->memory_mask; /* store accumulator value */ DELTAT->prev_acc = DELTAT->acc; /* Forecast to next Forecast */ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); /* delta to next delta */ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); /* ElSemi: Fix interpolator. */ /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ }while(--step); } /* ElSemi: Fix interpolator. */ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; /* output for work of output channels (outd[OPNxxxx])*/ *(DELTAT->pan) += DELTAT->adpcml; } INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) { UINT32 step; int data; DELTAT->now_step += DELTAT->step; if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; DELTAT->now_step &= (1<now_addr&1 ) { data = DELTAT->now_data & 0x0f; DELTAT->now_data = DELTAT->CPU_data; /* after we used CPU_data, we set BRDY bit in status register, * which means we are ready to accept another byte of data */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { data = DELTAT->now_data >> 4; } DELTAT->now_addr++; /* store accumulator value */ DELTAT->prev_acc = DELTAT->acc; /* Forecast to next Forecast */ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); /* delta to next delta */ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); }while(--step); } /* ElSemi: Fix interpolator. */ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; /* output for work of output channels (outd[OPNxxxx])*/ *(DELTAT->pan) += DELTAT->adpcml; } /* ADPCM B (Delta-T control type) */ void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT) { /* some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ if ( (DELTAT->portstate & 0xe0)==0xa0 ) { YM_DELTAT_synthesis_from_external_memory(DELTAT); return; } if ( (DELTAT->portstate & 0xe0)==0x80 ) { /* ADPCM synthesis from CPU-managed memory (from reg $08) */ YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */ return; } //todo: ADPCM analysis // if ( (DELTAT->portstate & 0xe0)==0xc0 ) // if ( (DELTAT->portstate & 0xe0)==0xe0 ) return; } void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT) { UINT32 MaskSize; MaskSize = 0x01; while(MaskSize < DELTAT->memory_size) MaskSize <<= 1; DELTAT->memory_mask = (MaskSize << 1) - 1; // it's Mask<<1 because of the nibbles return; } ================================================ FILE: VGMPlay/chips/ymdeltat.h ================================================ #pragma once #define YM_DELTAT_SHIFT (16) #define YM_DELTAT_EMULATION_MODE_NORMAL 0 #define YM_DELTAT_EMULATION_MODE_YM2610 1 typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits); /* DELTA-T (adpcm type B) struct */ typedef struct deltat_adpcm_state { /* AT: rearranged and tigntened structure */ UINT8 *memory; INT32 *output_pointer;/* pointer of output pointers */ INT32 *pan; /* pan : &output_pointer[pan] */ double freqbase; #if 0 double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ #endif UINT32 memory_size; UINT32 memory_mask; int output_range; UINT32 now_addr; /* current address */ UINT32 now_step; /* currect step */ UINT32 step; /* step */ UINT32 start; /* start address */ UINT32 limit; /* limit address */ UINT32 end; /* end address */ UINT32 delta; /* delta scale */ INT32 volume; /* current volume */ INT32 acc; /* shift Measurement value*/ INT32 adpcmd; /* next Forecast */ INT32 adpcml; /* current value */ INT32 prev_acc; /* leveling value */ UINT8 now_data; /* current rom data */ UINT8 CPU_data; /* current data from reg 08 */ UINT8 portstate; /* port status */ UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ UINT8 portshift; /* address bits shift-left: ** 8 for YM2610, ** 5 for Y8950 and YM2608 */ UINT8 DRAMportshift; /* address bits shift-right: ** 0 for ROM and x8bit DRAMs, ** 3 for x1 DRAMs */ UINT8 memread; /* needed for reading/writing external memory */ /* handlers and parameters for the status flags support */ STATUS_CHANGE_HANDLER status_set_handler; STATUS_CHANGE_HANDLER status_reset_handler; /* note that different chips have these flags on different ** bits of the status register */ void * status_change_which_chip; /* this chip id */ UINT8 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */ /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) */ UINT8 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ UINT8 reg[16]; /* adpcm registers */ UINT8 emulation_mode; /* which chip we're emulating */ }YM_DELTAT; /*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/ UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT); void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v); void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode); void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT); /*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs); //void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT); void YM_DELTAT_savestate(YM_DELTAT *DELTAT);*/ void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT); ================================================ FILE: VGMPlay/chips/ymf262.c ================================================ /* ** ** File: ymf262.c - software implementation of YMF262 ** FM sound generator type OPL3 ** ** Copyright Jarek Burczynski ** ** Version 0.2 ** Revision History: 03-03-2003: initial release - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips - thanks to Stiletto for the datasheets Features as listed in 4MF262A6 data sheet: 1. Registers are compatible with YM3812 (OPL2) FM sound source. 2. Up to six sounds can be used as four-operator melody sounds for variety. 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators). 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators). 5. 8 selectable waveforms. 6. 4-channel sound output. 7. YMF262 compabile DAC (YAC512) is available. 8. LFO for vibrato and tremolo effedts. 9. 2 programable timers. 10. Shorter register access time compared with YM3812. 11. 5V single supply silicon gate CMOS process. 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S). differences between OPL2 and OPL3 not documented in Yamaha datahasheets: - sinus table is a little different: the negative part is off by one... - in order to enable selection of four different waveforms on OPL2 one must set bit 5 in register 0x01(test). on OPL3 this bit is ignored and 4-waveform select works *always*. (Don't confuse this with OPL3's 8-waveform select.) - Envelope Generator: all 15 x rates take zero time on OPL3 (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates take zero time) - channel calculations: output of operator 1 is in perfect sync with output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 is always delayed by one sample compared to output of operator 2 differences between OPL2 and OPL3 shown in datasheets: - YMF262 does not support CSM mode */ #include #include "mamedef.h" #include #include // for memset #include // for NULL //#include "sndintrf.h" #include "ymf262.h" /* output final shift */ #if (OPL3_SAMPLE_BITS==16) #define FINAL_SH (0) #define MAXOUT (+32767) #define MINOUT (-32768) #else #define FINAL_SH (8) #define MAXOUT (+127) #define MINOUT (-128) #endif #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (EG timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = a; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = b; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif //#define LOG_CYM_FILE 0 //static FILE * cymfile = NULL; #define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */ typedef struct{ UINT32 ar; /* attack rate: AR<<2 */ UINT32 dr; /* decay rate: DR<<2 */ UINT32 rr; /* release rate:RR<<2 */ UINT8 KSR; /* key scale rate */ UINT8 ksl; /* keyscale level */ UINT8 ksr; /* key scale rate: kcode>>KSR */ UINT8 mul; /* multiple: mul_tab[ML] */ /* Phase Generator */ UINT32 Cnt; /* frequency counter */ UINT32 Incr; /* frequency counter step */ UINT8 FB; /* feedback shift value */ INT32 *connect; /* slot output pointer */ INT32 op1_out[2]; /* slot1 output for feedback */ UINT8 CON; /* connection (algorithm) type */ /* Envelope Generator */ UINT8 eg_type; /* percussive/non-percussive mode */ UINT8 state; /* phase type */ UINT32 TL; /* total level: TL << 2 */ INT32 TLL; /* adjusted now TL */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level: sl_tab[SL] */ UINT32 eg_m_ar; /* (attack state) */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT32 eg_m_dr; /* (decay state) */ UINT8 eg_sh_dr; /* (decay state) */ UINT8 eg_sel_dr; /* (decay state) */ UINT32 eg_m_rr; /* (release state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ /* LFO */ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ /* waveform select */ UINT8 waveform_number; unsigned int wavetable; //unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2 unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2 } OPL3_SLOT; typedef struct{ OPL3_SLOT SLOT[2]; UINT32 block_fnum; /* block+fnum */ UINT32 fc; /* Freq. Increment base */ UINT32 ksl_base; /* KeyScaleLevel Base step */ UINT8 kcode; /* key code (for key scaling) */ /* there are 12 2-operator channels which can be combined in pairs to form six 4-operator channel, they are: 0 and 3, 1 and 4, 2 and 5, 9 and 12, 10 and 13, 11 and 14 */ UINT8 extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */ UINT8 Muted; unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2 } OPL3_CH; /* OPL3 state */ typedef struct { OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */ UINT32 pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ UINT32 pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ UINT8 MuteSpc[5]; /* for the 5 Rhythm Channels */ signed int chanout[18]; /* 18 channels */ signed int phase_modulation; /* phase modulation input (SLOT 2) */ signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ UINT32 fn_tab[1024]; /* fnumber->increment counter */ /* LFO */ UINT32 LFO_AM; INT32 LFO_PM; UINT8 lfo_am_depth; UINT8 lfo_pm_depth_range; UINT32 lfo_am_cnt; UINT32 lfo_am_inc; UINT32 lfo_pm_cnt; UINT32 lfo_pm_inc; UINT32 noise_rng; /* 23 bit noise shift register */ UINT32 noise_p; /* current noise 'phase' */ UINT32 noise_f; /* current noise period */ UINT8 OPL3_mode; /* OPL3 extension enable flag */ UINT8 rhythm; /* Rhythm mode */ int T[2]; /* timer counters */ UINT8 st[2]; /* timer enable */ UINT32 address; /* address register */ UINT8 status; /* status flag */ UINT8 statusmask; /* status mask */ UINT8 nts; /* NTS (note select) */ /* external event callback handlers */ OPL3_TIMERHANDLER timer_handler;/* TIMER handler */ void *TimerParam; /* TIMER parameter */ OPL3_IRQHANDLER IRQHandler; /* IRQ handler */ void *IRQParam; /* IRQ parameter */ OPL3_UPDATEHANDLER UpdateHandler;/* stream update handler */ void *UpdateParam; /* stream update parameter */ UINT8 type; /* chip type */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ //attotime TimerBase; /* Timer base time (==sampling time)*/ } OPL3; /* mapping of register number (offset) to slot number used by the emulator */ static const int slot_array[32]= { 0, 2, 4, 1, 3, 5,-1,-1, 6, 8,10, 7, 9,11,-1,-1, 12,14,16,13,15,17,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1 }; /* key scale level */ /* table is 3dB/octave , DV converts this into 6dB/octave */ /* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ #define DV (0.1875/2.0) static const UINT32 ksl_tab[8*16]= { /* OCT 0 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, /* OCT 1 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, /* OCT 2 */ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, /* OCT 3 */ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, /* OCT 4 */ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, 10.875/DV,11.250/DV,11.625/DV,12.000/DV, /* OCT 5 */ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, 9.000/DV,10.125/DV,10.875/DV,11.625/DV, 12.000/DV,12.750/DV,13.125/DV,13.500/DV, 13.875/DV,14.250/DV,14.625/DV,15.000/DV, /* OCT 6 */ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, 12.000/DV,13.125/DV,13.875/DV,14.625/DV, 15.000/DV,15.750/DV,16.125/DV,16.500/DV, 16.875/DV,17.250/DV,17.625/DV,18.000/DV, /* OCT 7 */ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, 15.000/DV,16.125/DV,16.875/DV,17.625/DV, 18.000/DV,18.750/DV,19.125/DV,19.500/DV, 19.875/DV,20.250/DV,20.625/DV,21.000/DV }; #undef DV /* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 }; /* sustain level table (3dB per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ #define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) static const UINT32 sl_tab[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; #undef SC #define RATE_STEPS (8) static const unsigned char eg_inc[15*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */ /*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ /*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /* note that there is no O(13) in this table - it's directly in the code */ static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), /* rates 00-12 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 13 */ O( 4),O( 5),O( 6),O( 7), /* rate 14 */ O( 8),O( 9),O(10),O(11), /* rate 15 */ O(12),O(12),O(12),O(12), /* 16 dummy rates (same as 15 3) */ O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ /*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ /*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ #define O(a) (a*1) static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-12 */ O(12),O(12),O(12),O(12), O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 16 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), }; #undef O /* multiple table */ #define ML 2 static const UINT8 mul_tab[16]= { /* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML }; #undef ML /* TL_TAB_LEN is calculated as: * (12+1)=13 - sinus amplitude bits (Y axis) * additional 1: to compensate for calculations of negative part of waveform * (if we don't add it then the greatest possible _negative_ value would be -2 * and we really need -1 for waveform #7) * 2 - sinus sign bit (Y axis) * TL_RES_LEN - sinus resolution (X axis) */ #define TL_TAB_LEN (13*2*TL_RES_LEN) static signed int tl_tab[TL_TAB_LEN]; #define ENV_QUIET (TL_TAB_LEN>>4) /* sin waveform table in 'decibel' scale */ /* there are eight waveforms on OPL3 chips */ static unsigned int sin_tab[SIN_LEN * 8]; /* LFO Amplitude Modulation table (verified on real YM3812) 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples Length: 210 elements. Each of the elements has to be repeated exactly 64 times (on 64 consecutive samples). The whole table takes: 64 * 210 = 13440 samples. When AM = 1 data is used directly When AM = 0 data is divided by 4 before being used (losing precision is important) */ #define LFO_AM_TAB_ELEMENTS 210 static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; /* LFO Phase Modulation table (verified on real YM3812) */ static const INT8 lfo_pm_table[8*8*2] = { /* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ 0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ 1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ 4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ 2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ 5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ 6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ /* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ 3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ 7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ }; /* lock level of common table */ static int num_lock = 0; /* work table */ #define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) #define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) #define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) #define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) /*INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) val = min; return val; }*/ /* status set and IRQ handling */ INLINE void OPL3_STATUS_SET(OPL3 *chip,int flag) { /* set status flag masking out disabled IRQs */ chip->status |= (flag & chip->statusmask); if(!(chip->status & 0x80)) { if(chip->status & 0x7f) { /* IRQ on */ chip->status |= 0x80; /* callback user interrupt handler (IRQ is OFF to ON) */ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1); } } } /* status reset and IRQ handling */ INLINE void OPL3_STATUS_RESET(OPL3 *chip,int flag) { /* reset status flag */ chip->status &= ~flag; if(chip->status & 0x80) { if (!(chip->status & 0x7f)) { chip->status &= 0x7f; /* callback user interrupt handler (IRQ is ON to OFF) */ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0); } } } /* IRQ mask set */ INLINE void OPL3_STATUSMASK_SET(OPL3 *chip,int flag) { chip->statusmask = flag; /* IRQ handling check */ OPL3_STATUS_SET(chip,0); OPL3_STATUS_RESET(chip,0); } /* advance LFO to next sample */ INLINE void advance_lfo(OPL3 *chip) { UINT8 tmp; /* LFO */ chip->lfo_am_cnt += chip->lfo_am_inc; if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; if (chip->lfo_am_depth) chip->LFO_AM = tmp; else chip->LFO_AM = tmp>>2; chip->lfo_pm_cnt += chip->lfo_pm_inc; chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range; } /* advance to next sample */ INLINE void advance(OPL3 *chip) { OPL3_CH *CH; OPL3_SLOT *op; int i; chip->eg_timer += chip->eg_timer_add; while (chip->eg_timer >= chip->eg_timer_overflow) { chip->eg_timer -= chip->eg_timer_overflow; chip->eg_cnt++; for (i=0; i<9*2*2; i++) { CH = &chip->P_CH[i/2]; op = &CH->SLOT[i&1]; #if 1 /* Envelope Generator */ switch(op->state) { case EG_ATT: /* attack phase */ // if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) if ( !(chip->eg_cnt & op->eg_m_ar) ) { op->volume += (~op->volume * (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) ) >>3; if (op->volume <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ // if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) if ( !(chip->eg_cnt & op->eg_m_dr) ) { op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; if ( op->volume >= op->sl ) op->state = EG_SUS; } break; case EG_SUS: /* sustain phase */ /* this is important behaviour: one can change percusive/non-percussive modes on the fly and the chip will remain in sustain phase - verified on real YM3812 */ if(op->eg_type) /* non-percussive mode */ { /* do nothing */ } else /* percussive mode */ { /* during sustain phase chip adds Release Rate (in percussive mode) */ // if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) if ( !(chip->eg_cnt & op->eg_m_rr) ) { op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) op->volume = MAX_ATT_INDEX; } /* else do nothing in sustain phase */ } break; case EG_REL: /* release phase */ // if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) if ( !(chip->eg_cnt & op->eg_m_rr) ) { op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; if ( op->volume >= MAX_ATT_INDEX ) { op->volume = MAX_ATT_INDEX; op->state = EG_OFF; } } break; default: break; } #endif } } for (i=0; i<9*2*2; i++) { CH = &chip->P_CH[i/2]; op = &CH->SLOT[i&1]; /* Phase Generator */ if(op->vib) { UINT8 block; unsigned int block_fnum = CH->block_fnum; unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { block_fnum += lfo_fn_table_index_offset; block = (block_fnum&0x1c00) >> 10; op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; } else /* LFO phase modulation = zero */ { op->Cnt += op->Incr; } } else /* LFO phase modulation disabled for this operator */ { op->Cnt += op->Incr; } } /* The Noise Generator of the YM3812 is 23-bit shift register. * Period is equal to 2^23-2 samples. * Register works at sampling frequency of the chip, so output * can change on every sample. * * Output of the register and input to the bit 22 is: * bit0 XOR bit14 XOR bit15 XOR bit22 * * Simply use bit 22 as the noise output. */ chip->noise_p += chip->noise_f; i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ chip->noise_p &= FREQ_MASK; while (i) { /* UINT32 j; j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; chip->noise_rng = (j<<22) | (chip->noise_rng>>1); */ /* Instead of doing all the logic operations above, we use a trick here (and use bit 0 as the noise output). The difference is only that the noise bit changes one step ahead. This doesn't matter since we don't know what is real state of the noise_rng after the reset. */ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; chip->noise_rng >>= 1; i--; } } INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } #define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) /* calculate output of a standard 2 operator channel (or 1st part of a 4-op channel) */ INLINE void chan_calc( OPL3 *chip, OPL3_CH *CH ) { OPL3_SLOT *SLOT; unsigned int env; signed int out; if (CH->Muted) return; chip->phase_modulation = 0; chip->phase_modulation2= 0; /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->FB) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); } *SLOT->connect += SLOT->op1_out[1]; //logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env ); /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); //logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env ); } /* calculate output of a 2nd part of 4-op channel */ INLINE void chan_calc_ext( OPL3 *chip, OPL3_CH *CH ) { OPL3_SLOT *SLOT; unsigned int env; if (CH->Muted) return; chip->phase_modulation = 0; /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env = volume_calc(SLOT); if( env < ENV_QUIET ) *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable ); /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); } /* operators used in the rhythm sounds generation process: Envelope Generator: channel operator register number Bass High Snare Tom Top / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal 6 / 0 12 50 70 90 f0 + 6 / 1 15 53 73 93 f3 + 7 / 0 13 51 71 91 f1 + 7 / 1 16 54 74 94 f4 + 8 / 0 14 52 72 92 f2 + 8 / 1 17 55 75 95 f5 + Phase Generator: channel operator register number Bass High Snare Tom Top / slot number MULTIPLE Drum Hat Drum Tom Cymbal 6 / 0 12 30 + 6 / 1 15 33 + 7 / 0 13 31 + + + 7 / 1 16 34 ----- n o t u s e d ----- 8 / 0 14 32 + 8 / 1 17 35 + + channel operator register number Bass High Snare Tom Top number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal 6 12,15 B6 A6 + 7 13,16 B7 A7 + + + 8 14,17 B8 A8 + + + */ /* calculate rhythm */ INLINE void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise ) { OPL3_SLOT *SLOT; signed int *chanout = chip->chanout; signed int out; unsigned int env; /* Bass Drum (verified on real YM3812): - depends on the channel 6 'connect' register: when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - output sample always is multiplied by 2 */ chip->phase_modulation = 0; /* SLOT 1 */ SLOT = &CH[6].SLOT[SLOT1]; env = volume_calc(SLOT); out = SLOT->op1_out[0] + SLOT->op1_out[1]; SLOT->op1_out[0] = SLOT->op1_out[1]; if (!SLOT->CON) chip->phase_modulation = SLOT->op1_out[0]; //else ignore output of operator 1 SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) { if (!SLOT->FB) out = 0; SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); } /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET && ! chip->MuteSpc[0] ) chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2; /* Phase generation is based on: */ // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) // SD (16) channel 7->slot 1 // TOM (14) channel 8->slot 1 // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) /* Envelope generation based on: */ // HH channel 7->slot1 // SD channel 7->slot2 // TOM channel 8->slot1 // TOP channel 8->slot2 /* The following formulas can be well optimized. I leave them in direct form for now (in case I've missed something). */ /* High Hat (verified on real YM3812) */ env = volume_calc(SLOT7_1); if( env < ENV_QUIET && ! chip->MuteSpc[4] ) { /* high hat phase generation: phase = d0 or 234 (based on frequency only) phase = 34 or 2d0 (based on noise) */ /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0xd0; */ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e ^ bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ if (res2) phase = (0x200|(0xd0>>2)); /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ if (phase&0x200) { if (noise) phase = 0x200|0xd0; } else /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ { if (noise) phase = 0xd0>>2; } chanout[7] += op_calc(phase<wavetable) * 2; } /* Snare Drum (verified on real YM3812) */ env = volume_calc(SLOT7_2); if( env < ENV_QUIET && ! chip->MuteSpc[1] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; /* when bit8 = 0 phase = 0x100; */ /* when bit8 = 1 phase = 0x200; */ UINT32 phase = bit8 ? 0x200 : 0x100; /* Noise bit XOR'es phase by 0x100 */ /* when noisebit = 0 pass the phase from calculation above */ /* when noisebit = 1 phase ^= 0x100; */ /* in other words: phase ^= (noisebit<<8); */ if (noise) phase ^= 0x100; chanout[7] += op_calc(phase<wavetable) * 2; } /* Tom Tom (verified on real YM3812) */ env = volume_calc(SLOT8_1); if( env < ENV_QUIET && ! chip->MuteSpc[2] ) chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; /* Top Cymbal (verified on real YM3812) */ env = volume_calc(SLOT8_2); if( env < ENV_QUIET && ! chip->MuteSpc[3] ) { /* base frequency derived from operator 1 in channel 7 */ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; unsigned char res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0x100; */ /* when res1 = 1 phase = 0x200 | 0x100; */ UINT32 phase = res1 ? 0x300 : 0x100; /* enable gate based on frequency of operator 2 in channel 8 */ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; unsigned char res2 = (bit3e ^ bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | 0x100; */ if (res2) phase = 0x300; chanout[8] += op_calc(phase<wavetable) * 2; } } /* generic table initialize */ static int init_tables(void) { signed int i,x; signed int n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 1; /* 12 bits here (as in real chip) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */ for (i=1; i<13; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */ } #if 0 logerror("tl %04i", x*2); for (i=0; i<13; i++) logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */ logerror("\n"); logerror("tl %04i", x*2); for (i=0; i<13; i++) logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */ logerror("\n"); #endif } for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ } for (i=0; i>1) ]; /* waveform 3: _ _ _ _ */ /* / |_/ |_/ |_/ |_*/ /* abs(output only first quarter of the sinus waveform) */ if (i & (1<<(SIN_BITS-2)) ) sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; else sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; /* waveform 4: */ /* /\ ____/\ ____*/ /* \/ \/ */ /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */ if (i & (1<<(SIN_BITS-1)) ) sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; else sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; /* waveform 5: */ /* /\/\____/\/\____*/ /* */ /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */ if (i & (1<<(SIN_BITS-1)) ) sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; else sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ]; /* waveform 6: ____ ____ */ /* */ /* ____ ____*/ /* output maximum in half the cycle and output minimum on the other half of cycle */ if (i & (1<<(SIN_BITS-1)) ) sin_tab[6*SIN_LEN+i] = 1; /* negative */ else sin_tab[6*SIN_LEN+i] = 0; /* positive */ /* waveform 7: */ /* |\____ |\____ */ /* \| \|*/ /* output sawtooth waveform */ if (i & (1<<(SIN_BITS-1)) ) x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */ else x = i*16; /*positive: from 0 to 8176 */ if (x > TL_TAB_LEN) x = TL_TAB_LEN; /* clip to the allowed range */ sin_tab[7*SIN_LEN+i] = x; //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] ); //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] ); //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] ); //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] ); //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] ); } /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ #ifdef SAVE_SAMPLE sample[0]=fopen("sampsum.pcm","wb"); #endif return 1; } static void OPLCloseTable( void ) { #ifdef SAVE_SAMPLE fclose(sample[0]); #endif } static void OPL3_initalize(OPL3 *chip) { int i; /* frequency base */ chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0; #if 0 chip->rate = (double)chip->clock / (8.0*36); chip->freqbase = 1.0; #endif /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ /* Timer base time */ //chip->TimerBase = attotime_mul(ATTOTIME_IN_HZ(chip->clock), 8*36); /* make fnumber -> increment counter table */ for( i=0 ; i < 1024 ; i++ ) { /* opn phase increment counter = 20bit */ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ #if 0 logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n", i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); #endif } #if 0 for( i=0 ; i < 16 ; i++ ) { logerror("YMF262.C: sl_tab[%i] = %08x\n", i, sl_tab[i] ); } for( i=0 ; i < 8 ; i++ ) { int j; logerror("YMF262.C: ksl_tab[oct=%2i] =",i); for (j=0; j<16; j++) { logerror("%08x ", ksl_tab[i*16+j] ); } logerror("\n"); } #endif /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ /* One entry from LFO_AM_TABLE lasts for 64 samples */ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ /* Noise generator: a step takes 1 sample */ chip->noise_f = (1.0 / 1.0) * (1<freqbase; chip->eg_timer_add = (1<freqbase; chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ } INLINE void FM_KEYON(OPL3_SLOT *SLOT, UINT32 key_set) { if( !SLOT->key ) { /* restart Phase Generator */ SLOT->Cnt = 0; /* phase -> Attack */ SLOT->state = EG_ATT; } SLOT->key |= key_set; } INLINE void FM_KEYOFF(OPL3_SLOT *SLOT, UINT32 key_clr) { if( SLOT->key ) { SLOT->key &= key_clr; if( !SLOT->key ) { /* phase -> Release */ if (SLOT->state>EG_REL) SLOT->state = EG_REL; } } } /* update phase increment counter of operator (also update the EG rates if necessary) */ INLINE void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT) { int ksr; /* (frequency) phase increment counter */ SLOT->Incr = CH->fc * SLOT->mul; ksr = CH->kcode >> SLOT->KSR; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 16+60) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_m_ar = (1<eg_sh_ar)-1; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_m_ar = (1<eg_sh_ar)-1; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_m_dr = (1<eg_sh_dr)-1; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_m_rr = (1<eg_sh_rr)-1; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } } /* set multi,am,vib,EG-TYP,KSR,mul */ INLINE void set_mul(OPL3 *chip,int slot,int v) { OPL3_CH *CH = &chip->P_CH[slot/2]; OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->mul = mul_tab[v&0x0f]; SLOT->KSR = (v&0x10) ? 0 : 2; SLOT->eg_type = (v&0x20); SLOT->vib = (v&0x40); SLOT->AMmask = (v&0x80) ? ~0 : 0; if (chip->OPL3_mode & 1) { int chan_no = slot/2; /* in OPL3 mode */ //DO THIS: //if this is one of the slots of 1st channel forming up a 4-op channel //do normal operation //else normal 2 operator function //OR THIS: //if this is one of the slots of 2nd channel forming up a 4-op channel //update it using channel data of 1st channel of a pair //else normal 2 operator function switch(chan_no) { case 0: case 1: case 2: case 9: case 10: case 11: if (CH->extended) { /* normal */ CALC_FCSLOT(CH,SLOT); } else { /* normal */ CALC_FCSLOT(CH,SLOT); } break; case 3: case 4: case 5: case 12: case 13: case 14: if ((CH-3)->extended) { /* update this SLOT using frequency data for 1st channel of a pair */ CALC_FCSLOT(CH-3,SLOT); } else { /* normal */ CALC_FCSLOT(CH,SLOT); } break; default: /* normal */ CALC_FCSLOT(CH,SLOT); break; } } else { /* in OPL2 mode */ CALC_FCSLOT(CH,SLOT); } } /* set ksl & tl */ INLINE void set_ksl_tl(OPL3 *chip,int slot,int v) { OPL3_CH *CH = &chip->P_CH[slot/2]; OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->ksl = ksl_shift[v >> 6]; SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ if (chip->OPL3_mode & 1) { int chan_no = slot/2; /* in OPL3 mode */ //DO THIS: //if this is one of the slots of 1st channel forming up a 4-op channel //do normal operation //else normal 2 operator function //OR THIS: //if this is one of the slots of 2nd channel forming up a 4-op channel //update it using channel data of 1st channel of a pair //else normal 2 operator function switch(chan_no) { case 0: case 1: case 2: case 9: case 10: case 11: if (CH->extended) { /* normal */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } else { /* normal */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } break; case 3: case 4: case 5: case 12: case 13: case 14: if ((CH-3)->extended) { /* update this SLOT using frequency data for 1st channel of a pair */ SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); } else { /* normal */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } break; default: /* normal */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); break; } } else { /* in OPL2 mode */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } } /* set attack rate & decay rate */ INLINE void set_ar_dr(OPL3 *chip,int slot,int v) { OPL3_CH *CH = &chip->P_CH[slot/2]; OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */ { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_m_ar = (1<eg_sh_ar)-1; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_m_ar = (1<eg_sh_ar)-1; SLOT->eg_sel_ar = 13*RATE_STEPS; } SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; SLOT->eg_m_dr = (1<eg_sh_dr)-1; SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; } /* set sustain level & release rate */ INLINE void set_sl_rr(OPL3 *chip,int slot,int v) { OPL3_CH *CH = &chip->P_CH[slot/2]; OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; SLOT->sl = sl_tab[ v>>4 ]; SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; SLOT->eg_m_rr = (1<eg_sh_rr)-1; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } static void update_channels(OPL3 *chip, OPL3_CH *CH) { /* update channel passed as a parameter and a channel at CH+=3; */ if (CH->extended) { /* we've just switched to combined 4 operator mode */ } else { /* we've just switched to normal 2 operator mode */ } } /* write a value v to register r on OPL chip */ static void OPL3WriteReg(OPL3 *chip, int r, int v) { OPL3_CH *CH; signed int *chanout = chip->chanout; unsigned int ch_offset = 0; int slot; int block_fnum; /*if (LOG_CYM_FILE && (cymfile) && ((r&255)!=0) && (r!=255) ) { if (r>0xff) fputc( (unsigned char)0xff, cymfile );//mark writes to second register set fputc( (unsigned char)r&0xff, cymfile ); fputc( (unsigned char)v, cymfile ); }*/ if(r&0x100) { switch(r) { case 0x101: /* test register */ return; case 0x104: /* 6 channels enable */ { UINT8 prev; CH = &chip->P_CH[0]; /* channel 0 */ prev = CH->extended; CH->extended = (v>>0) & 1; if(prev != CH->extended) update_channels(chip, CH); CH++; /* channel 1 */ prev = CH->extended; CH->extended = (v>>1) & 1; if(prev != CH->extended) update_channels(chip, CH); CH++; /* channel 2 */ prev = CH->extended; CH->extended = (v>>2) & 1; if(prev != CH->extended) update_channels(chip, CH); CH = &chip->P_CH[9]; /* channel 9 */ prev = CH->extended; CH->extended = (v>>3) & 1; if(prev != CH->extended) update_channels(chip, CH); CH++; /* channel 10 */ prev = CH->extended; CH->extended = (v>>4) & 1; if(prev != CH->extended) update_channels(chip, CH); CH++; /* channel 11 */ prev = CH->extended; CH->extended = (v>>5) & 1; if(prev != CH->extended) update_channels(chip, CH); } return; case 0x105: /* OPL3 extensions enable register */ chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */ /* following behaviour was tested on real YMF262, switching OPL3/OPL2 modes on the fly: - does not change the waveform previously selected (unless when ....) - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....) - does not disable channels 9-17 on OPL3->OPL2 switch - does not switch 4 operator channels back to 2 operator channels */ return; default: #ifdef _DEBUG if (r < 0x120) logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v); #endif break; } ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */ } /* adjust bus to 8 bits */ r &= 0xff; v &= 0xff; switch(r&0xe0) { case 0x00: /* 00-1f:control */ switch(r&0x1f) { case 0x01: /* test register */ break; case 0x02: /* Timer 1 */ chip->T[0] = (256-v)*4; break; case 0x03: /* Timer 2 */ chip->T[1] = (256-v)*16; break; case 0x04: /* IRQ clear / mask and Timer enable */ if(v&0x80) { /* IRQ flags clear */ OPL3_STATUS_RESET(chip,0x60); } else { /* set IRQ mask ,timer enable */ UINT8 st1 = v & 1; UINT8 st2 = (v>>1) & 1; /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */ OPL3_STATUS_RESET(chip, v & 0x60); OPL3_STATUSMASK_SET(chip, (~v) & 0x60 ); /* timer 2 */ if(chip->st[1] != st2) { //attotime period = st2 ? attotime_mul(chip->TimerBase, chip->T[1]) : attotime_zero; chip->st[1] = st2; //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period); } /* timer 1 */ if(chip->st[0] != st1) { //attotime period = st1 ? attotime_mul(chip->TimerBase, chip->T[0]) : attotime_zero; chip->st[0] = st1; //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); } } break; case 0x08: /* x,NTS,x,x, x,x,x,x */ chip->nts = v; break; default: #ifdef _DEBUG logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v); #endif break; } break; case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ slot = slot_array[r&0x1f]; if(slot < 0) return; set_mul(chip, slot + ch_offset*2, v); break; case 0x40: slot = slot_array[r&0x1f]; if(slot < 0) return; set_ksl_tl(chip, slot + ch_offset*2, v); break; case 0x60: slot = slot_array[r&0x1f]; if(slot < 0) return; set_ar_dr(chip, slot + ch_offset*2, v); break; case 0x80: slot = slot_array[r&0x1f]; if(slot < 0) return; set_sl_rr(chip, slot + ch_offset*2, v); break; case 0xa0: if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ { if (ch_offset != 0) /* 0xbd register is present in set #1 only */ return; chip->lfo_am_depth = v & 0x80; chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0; chip->rhythm = v&0x3f; if(chip->rhythm&0x20) { /* BD key on/off */ if(v&0x10) { FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2); FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2); } else { FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); } /* HH key on/off */ if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2); else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); /* SD key on/off */ if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2); else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); /* TOM key on/off */ if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2); else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY key on/off */ if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2); else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); } else { /* BD key off */ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); /* HH key off */ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); /* SD key off */ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); /* TOM key off */ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); /* TOP-CY off */ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); } return; } /* keyon,block,fnum */ if( (r&0x0f) > 8) return; CH = &chip->P_CH[(r&0x0f) + ch_offset]; if(!(r&0x10)) { /* a0-a8 */ block_fnum = (CH->block_fnum&0x1f00) | v; } else { /* b0-b8 */ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); if (chip->OPL3_mode & 1) { int chan_no = (r&0x0f) + ch_offset; /* in OPL3 mode */ //DO THIS: //if this is 1st channel forming up a 4-op channel //ALSO keyon/off slots of 2nd channel forming up 4-op channel //else normal 2 operator function keyon/off //OR THIS: //if this is 2nd channel forming up 4-op channel just do nothing //else normal 2 operator function keyon/off switch(chan_no) { case 0: case 1: case 2: case 9: case 10: case 11: if (CH->extended) { //if this is 1st channel forming up a 4-op channel //ALSO keyon/off slots of 2nd channel forming up 4-op channel if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); FM_KEYON (&(CH+3)->SLOT[SLOT1], 1); FM_KEYON (&(CH+3)->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1); FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1); } } else { //else normal 2 operator function keyon/off if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); } } break; case 3: case 4: case 5: case 12: case 13: case 14: if ((CH-3)->extended) { //if this is 2nd channel forming up 4-op channel just do nothing } else { //else normal 2 operator function keyon/off if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); } } break; default: if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); } break; } } else { if(v&0x20) { FM_KEYON (&CH->SLOT[SLOT1], 1); FM_KEYON (&CH->SLOT[SLOT2], 1); } else { FM_KEYOFF(&CH->SLOT[SLOT1],~1); FM_KEYOFF(&CH->SLOT[SLOT2],~1); } } } /* update */ if(CH->block_fnum != block_fnum) { UINT8 block = block_fnum >> 10; CH->block_fnum = block_fnum; CH->ksl_base = ksl_tab[block_fnum>>6]; CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ CH->kcode = (CH->block_fnum&0x1c00)>>9; /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ if (chip->nts&0x40) CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ else CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ if (chip->OPL3_mode & 1) { int chan_no = (r&0x0f) + ch_offset; /* in OPL3 mode */ //DO THIS: //if this is 1st channel forming up a 4-op channel //ALSO update slots of 2nd channel forming up 4-op channel //else normal 2 operator function keyon/off //OR THIS: //if this is 2nd channel forming up 4-op channel just do nothing //else normal 2 operator function keyon/off switch(chan_no) { case 0: case 1: case 2: case 9: case 10: case 11: if (CH->extended) { //if this is 1st channel forming up a 4-op channel //ALSO update slots of 2nd channel forming up 4-op channel /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl); (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl); /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]); CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]); } else { //else normal 2 operator function /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); } break; case 3: case 4: case 5: case 12: case 13: case 14: if ((CH-3)->extended) { //if this is 2nd channel forming up 4-op channel just do nothing } else { //else normal 2 operator function /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); } break; default: /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); break; } } else { /* in OPL2 mode */ /* refresh Total Level in both SLOTs of this channel */ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); /* refresh frequency counter in both SLOTs of this channel */ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); } } break; case 0xc0: /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */ if( (r&0xf) > 8) return; CH = &chip->P_CH[(r&0xf) + ch_offset]; if( chip->OPL3_mode & 1 ) { int base = ((r&0xf) + ch_offset) * 4; /* OPL3 mode */ chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */ chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */ chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */ chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */ } else { int base = ((r&0xf) + ch_offset) * 4; /* OPL2 mode - always enabled */ chip->pan[ base ] = ~0; /* ch.A */ chip->pan[ base +1 ] = ~0; /* ch.B */ chip->pan[ base +2 ] = ~0; /* ch.C */ chip->pan[ base +3 ] = ~0; /* ch.D */ } chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; CH->SLOT[SLOT1].CON = v&1; if( chip->OPL3_mode & 1 ) { int chan_no = (r&0x0f) + ch_offset; switch(chan_no) { case 0: case 1: case 2: case 9: case 10: case 11: if (CH->extended) { UINT8 conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0); switch(conn) { case 0: /* 1 -> 2 -> 3 -> 4 - out */ CH->SLOT[SLOT1].connect = &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chip->phase_modulation2; (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; break; case 1: /* 1 -> 2 -\ 3 -> 4 -+- out */ CH->SLOT[SLOT1].connect = &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; break; case 2: /* 1 -----------\ 2 -> 3 -> 4 -+- out */ CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; CH->SLOT[SLOT2].connect = &chip->phase_modulation2; (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; break; case 3: /* 1 ------\ 2 -> 3 -+- out 4 ------/ */ CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; CH->SLOT[SLOT2].connect = &chip->phase_modulation2; (CH+3)->SLOT[SLOT1].connect = &chanout[ chan_no + 3 ]; (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; break; } } else { /* 2 operators mode */ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; } break; case 3: case 4: case 5: case 12: case 13: case 14: if ((CH-3)->extended) { UINT8 conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0); switch(conn) { case 0: /* 1 -> 2 -> 3 -> 4 - out */ (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation; (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; CH->SLOT[SLOT1].connect = &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; break; case 1: /* 1 -> 2 -\ 3 -> 4 -+- out */ (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation; (CH-3)->SLOT[SLOT2].connect = &chanout[ chan_no - 3 ]; CH->SLOT[SLOT1].connect = &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; break; case 2: /* 1 -----------\ 2 -> 3 -> 4 -+- out */ (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ]; (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; CH->SLOT[SLOT1].connect = &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; break; case 3: /* 1 ------\ 2 -> 3 -+- out 4 ------/ */ (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ]; (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; break; } } else { /* 2 operators mode */ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; } break; default: /* 2 operators mode */ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; break; } } else { /* OPL2 mode - always 2 operators mode */ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; } break; case 0xe0: /* waveform select */ slot = slot_array[r&0x1f]; if(slot < 0) return; slot += ch_offset*2; CH = &chip->P_CH[slot/2]; /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */ v &= 7; CH->SLOT[slot&1].waveform_number = v; /* ... but select only waveforms 0-3 in OPL2 mode */ if( !(chip->OPL3_mode & 1) ) { v &= 3; /* we're in OPL2 mode */ } CH->SLOT[slot&1].wavetable = v * SIN_LEN; break; } } /*static TIMER_CALLBACK( cymfile_callback ) { if (cymfile) { fputc( (unsigned char)0, cymfile ); } }*/ /* lock/unlock for common table */ static int OPL3_LockTable() { num_lock++; if(num_lock>1) return 0; /* first time */ if( !init_tables() ) { num_lock--; return -1; } /*if (LOG_CYM_FILE) { cymfile = fopen("ymf262_.cym","wb"); if (cymfile) timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer else logerror("Could not create ymf262_.cym file\n"); }*/ return 0; } static void OPL3_UnLockTable(void) { if(num_lock) num_lock--; if(num_lock) return; /* last time */ OPLCloseTable(); /*if (LOG_CYM_FILE) fclose (cymfile); cymfile = NULL;*/ } static void OPL3ResetChip(OPL3 *chip) { int c,s; chip->eg_timer = 0; chip->eg_cnt = 0; chip->noise_rng = 1; /* noise shift register */ chip->nts = 0; /* note split */ OPL3_STATUS_RESET(chip,0x60); /* reset with register write */ OPL3WriteReg(chip,0x01,0); /* test register */ OPL3WriteReg(chip,0x02,0); /* Timer1 */ OPL3WriteReg(chip,0x03,0); /* Timer2 */ OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */ //FIX IT registers 101, 104 and 105 //FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) for(c = 0xff ; c >= 0x20 ; c-- ) OPL3WriteReg(chip,c,0); //FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) for(c = 0x1ff ; c >= 0x120 ; c-- ) OPL3WriteReg(chip,c,0); /* reset operator parameters */ for( c = 0 ; c < 9*2 ; c++ ) { OPL3_CH *CH = &chip->P_CH[c]; for(s = 0 ; s < 2 ; s++ ) { CH->SLOT[s].state = EG_OFF; CH->SLOT[s].volume = MAX_ATT_INDEX; } } } /* Create one of virtual YMF262 */ /* 'clock' is chip clock in Hz */ /* 'rate' is sampling rate */ static OPL3 *OPL3Create(int clock, int rate, int type) { OPL3 *chip; if (OPL3_LockTable() == -1) return NULL; /* allocate memory block */ chip = (OPL3 *)malloc(sizeof(OPL3)); if (chip==NULL) return NULL; /* clear */ memset(chip, 0, sizeof(OPL3)); chip->type = type; chip->clock = clock; chip->rate = rate; /* init global tables */ OPL3_initalize(chip); /* reset chip */ OPL3ResetChip(chip); return chip; } /* Destroy one of virtual YMF262 */ static void OPL3Destroy(OPL3 *chip) { OPL3_UnLockTable(); free(chip); } /* Optional handlers */ static void OPL3SetTimerHandler(OPL3 *chip,OPL3_TIMERHANDLER timer_handler,void *param) { chip->timer_handler = timer_handler; chip->TimerParam = param; } static void OPL3SetIRQHandler(OPL3 *chip,OPL3_IRQHANDLER IRQHandler,void *param) { chip->IRQHandler = IRQHandler; chip->IRQParam = param; } static void OPL3SetUpdateHandler(OPL3 *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param) { chip->UpdateHandler = UpdateHandler; chip->UpdateParam = param; } /* YMF262 I/O interface */ static int OPL3Write(OPL3 *chip, int a, int v) { /* data bus is 8 bits */ v &= 0xff; switch(a&3) { case 0: /* address port 0 (register set #1) */ chip->address = v; break; case 1: /* data port - ignore A1 */ case 3: /* data port - ignore A1 */ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam/*,0*/); OPL3WriteReg(chip,chip->address,v); break; case 2: /* address port 1 (register set #2) */ /* verified on real YMF262: in OPL3 mode: address line A1 is stored during *address* write and ignored during *data* write. in OPL2 mode: register set#2 writes go to register set#1 (ignoring A1) verified on registers from set#2: 0x01, 0x04, 0x20-0xef The only exception is register 0x05. */ if( chip->OPL3_mode & 1 ) { /* OPL3 mode */ chip->address = v | 0x100; } else { /* in OPL2 mode the only accessible in set #2 is register 0x05 */ if( v==5 ) chip->address = v | 0x100; else chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */ } break; } return chip->status>>7; } static unsigned char OPL3Read(OPL3 *chip,int a) { if( a==0 ) { /* status port */ return chip->status; } return 0x00; /* verified on real YMF262 */ } static int OPL3TimerOver(OPL3 *chip,int c) { if( c ) { /* Timer B */ OPL3_STATUS_SET(chip,0x20); } else { /* Timer A */ OPL3_STATUS_SET(chip,0x40); } /* reload timer */ //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,attotime_mul(chip->TimerBase, chip->T[c])); return chip->status>>7; } void * ymf262_init(int clock, int rate) { return OPL3Create(clock,rate,OPL3_TYPE_YMF262); } void ymf262_shutdown(void *chip) { OPL3Destroy((OPL3 *)chip); } void ymf262_reset_chip(void *chip) { OPL3ResetChip((OPL3 *)chip); } int ymf262_write(void *chip, int a, int v) { return OPL3Write((OPL3 *)chip, a, v); } unsigned char ymf262_read(void *chip, int a) { /* Note on status register: */ /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */ /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */ /* which can be used to identify the chip */ /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */ return OPL3Read((OPL3 *)chip, a); } int ymf262_timer_over(void *chip, int c) { return OPL3TimerOver((OPL3 *)chip, c); } void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, void *param) { OPL3SetTimerHandler((OPL3 *)chip, timer_handler, param); } void ymf262_set_irq_handler(void *chip,OPL3_IRQHANDLER IRQHandler,void *param) { OPL3SetIRQHandler((OPL3 *)chip, IRQHandler, param); } void ymf262_set_update_handler(void *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param) { OPL3SetUpdateHandler((OPL3 *)chip, UpdateHandler, param); } void ymf262_set_mutemask(void *chip, UINT32 MuteMask) { OPL3 *opl3 = (OPL3 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 18; CurChn ++) opl3->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 5; CurChn ++) opl3->MuteSpc[CurChn] = (MuteMask >> (CurChn + 18)) & 0x01; return; } /* ** Generate samples for one of the YMF262's ** ** 'which' is the virtual YMF262 number ** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D ** 'length' is the number of samples that should be generated */ void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length) { OPL3 *chip = (OPL3 *)_chip; UINT8 rhythm = chip->rhythm&0x20; OPL3SAMPLE *ch_a = buffers[0]; OPL3SAMPLE *ch_b = buffers[1]; //OPL3SAMPLE *ch_c = buffers[2]; //OPL3SAMPLE *ch_d = buffers[3]; int i; int chn; for( i=0; i < length ; i++ ) { int a,b,c,d; advance_lfo(chip); /* clear channel outputs */ memset(chip->chanout, 0, sizeof(signed int) * 18); #if 1 /* register set #1 */ chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */ if (chip->P_CH[0].extended) chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */ else chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */ chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */ if (chip->P_CH[1].extended) chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */ else chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */ chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */ if (chip->P_CH[2].extended) chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */ else chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */ if(!rhythm) { chan_calc(chip, &chip->P_CH[6]); chan_calc(chip, &chip->P_CH[7]); chan_calc(chip, &chip->P_CH[8]); } else /* Rhythm part */ { chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); } /* register set #2 */ chan_calc(chip, &chip->P_CH[ 9]); if (chip->P_CH[9].extended) chan_calc_ext(chip, &chip->P_CH[12]); else chan_calc(chip, &chip->P_CH[12]); chan_calc(chip, &chip->P_CH[10]); if (chip->P_CH[10].extended) chan_calc_ext(chip, &chip->P_CH[13]); else chan_calc(chip, &chip->P_CH[13]); chan_calc(chip, &chip->P_CH[11]); if (chip->P_CH[11].extended) chan_calc_ext(chip, &chip->P_CH[14]); else chan_calc(chip, &chip->P_CH[14]); /* channels 15,16,17 are fixed 2-operator channels only */ chan_calc(chip, &chip->P_CH[15]); chan_calc(chip, &chip->P_CH[16]); chan_calc(chip, &chip->P_CH[17]); #endif /* accumulator register set #1 */ a = chip->chanout[0] & chip->pan[0]; b = chip->chanout[0] & chip->pan[1]; c = chip->chanout[0] & chip->pan[2]; d = chip->chanout[0] & chip->pan[3]; #if 1 a += chip->chanout[1] & chip->pan[4]; b += chip->chanout[1] & chip->pan[5]; c += chip->chanout[1] & chip->pan[6]; d += chip->chanout[1] & chip->pan[7]; a += chip->chanout[2] & chip->pan[8]; b += chip->chanout[2] & chip->pan[9]; c += chip->chanout[2] & chip->pan[10]; d += chip->chanout[2] & chip->pan[11]; a += chip->chanout[3] & chip->pan[12]; b += chip->chanout[3] & chip->pan[13]; c += chip->chanout[3] & chip->pan[14]; d += chip->chanout[3] & chip->pan[15]; a += chip->chanout[4] & chip->pan[16]; b += chip->chanout[4] & chip->pan[17]; c += chip->chanout[4] & chip->pan[18]; d += chip->chanout[4] & chip->pan[19]; a += chip->chanout[5] & chip->pan[20]; b += chip->chanout[5] & chip->pan[21]; c += chip->chanout[5] & chip->pan[22]; d += chip->chanout[5] & chip->pan[23]; a += chip->chanout[6] & chip->pan[24]; b += chip->chanout[6] & chip->pan[25]; c += chip->chanout[6] & chip->pan[26]; d += chip->chanout[6] & chip->pan[27]; a += chip->chanout[7] & chip->pan[28]; b += chip->chanout[7] & chip->pan[29]; c += chip->chanout[7] & chip->pan[30]; d += chip->chanout[7] & chip->pan[31]; a += chip->chanout[8] & chip->pan[32]; b += chip->chanout[8] & chip->pan[33]; c += chip->chanout[8] & chip->pan[34]; d += chip->chanout[8] & chip->pan[35]; /* accumulator register set #2 */ a += chip->chanout[9] & chip->pan[36]; b += chip->chanout[9] & chip->pan[37]; c += chip->chanout[9] & chip->pan[38]; d += chip->chanout[9] & chip->pan[39]; a += chip->chanout[10] & chip->pan[40]; b += chip->chanout[10] & chip->pan[41]; c += chip->chanout[10] & chip->pan[42]; d += chip->chanout[10] & chip->pan[43]; a += chip->chanout[11] & chip->pan[44]; b += chip->chanout[11] & chip->pan[45]; c += chip->chanout[11] & chip->pan[46]; d += chip->chanout[11] & chip->pan[47]; a += chip->chanout[12] & chip->pan[48]; b += chip->chanout[12] & chip->pan[49]; c += chip->chanout[12] & chip->pan[50]; d += chip->chanout[12] & chip->pan[51]; a += chip->chanout[13] & chip->pan[52]; b += chip->chanout[13] & chip->pan[53]; c += chip->chanout[13] & chip->pan[54]; d += chip->chanout[13] & chip->pan[55]; a += chip->chanout[14] & chip->pan[56]; b += chip->chanout[14] & chip->pan[57]; c += chip->chanout[14] & chip->pan[58]; d += chip->chanout[14] & chip->pan[59]; a += chip->chanout[15] & chip->pan[60]; b += chip->chanout[15] & chip->pan[61]; c += chip->chanout[15] & chip->pan[62]; d += chip->chanout[15] & chip->pan[63]; a += chip->chanout[16] & chip->pan[64]; b += chip->chanout[16] & chip->pan[65]; c += chip->chanout[16] & chip->pan[66]; d += chip->chanout[16] & chip->pan[67]; a += chip->chanout[17] & chip->pan[68]; b += chip->chanout[17] & chip->pan[69]; c += chip->chanout[17] & chip->pan[70]; d += chip->chanout[17] & chip->pan[71]; #endif a >>= FINAL_SH; b >>= FINAL_SH; c >>= FINAL_SH; d >>= FINAL_SH; /* limit check */ //a = limit( a , MAXOUT, MINOUT ); //b = limit( b , MAXOUT, MINOUT ); //c = limit( c , MAXOUT, MINOUT ); //d = limit( d , MAXOUT, MINOUT ); #ifdef SAVE_SAMPLE if (which==0) { SAVE_ALL_CHANNELS } #endif /* store to sound buffer */ ch_a[i] = a+c; ch_b[i] = b+d; //ch_c[i] = c; //ch_d[i] = d; advance(chip); } } ================================================ FILE: VGMPlay/chips/ymf262.h ================================================ #pragma once //#include "attotime.h" /* select number of output bits: 8 or 16 */ #define OPL3_SAMPLE_BITS 16 /* compiler dependence */ //#ifndef __OSDCOMM_H__ //#define __OSDCOMM_H__ /*typedef unsigned char UINT8; // unsigned 8bit typedef unsigned short UINT16; // unsigned 16bit typedef unsigned int UINT32; // unsigned 32bit typedef signed char INT8; // signed 8bit typedef signed short INT16; // signed 16bit typedef signed int INT32; // signed 32bit*/ //#endif typedef stream_sample_t OPL3SAMPLE; /* #if (OPL3_SAMPLE_BITS==16) typedef INT16 OPL3SAMPLE; #endif #if (OPL3_SAMPLE_BITS==8) typedef INT8 OPL3SAMPLE; #endif */ //typedef void (*OPL3_TIMERHANDLER)(void *param,int timer,attotime period); typedef void (*OPL3_TIMERHANDLER)(void *param,int timer,int period); typedef void (*OPL3_IRQHANDLER)(void *param,int irq); typedef void (*OPL3_UPDATEHANDLER)(void *param/*,int min_interval_us*/); void *ymf262_init(int clock, int rate); void ymf262_shutdown(void *chip); void ymf262_reset_chip(void *chip); int ymf262_write(void *chip, int a, int v); unsigned char ymf262_read(void *chip, int a); int ymf262_timer_over(void *chip, int c); void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length); void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, void *param); void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, void *param); void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, void *param); void ymf262_set_emu_core(UINT8 Emulator); void ymf262_set_mutemask(void *chip, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/ymf271.c ================================================ /* Yamaha YMF271-F "OPX" emulator v0.1 By R. Belmont. Based in part on YMF278B emulator by R. Belmont and O. Galibert. 12June04 update by Toshiaki Nijiura Copyright R. Belmont. This software is dual-licensed: it may be used in MAME and properly licensed MAME derivatives under the terms of the MAME license. For use outside of MAME and properly licensed derivatives, it is available under the terms of the GNU Lesser General Public License (LGPL), version 2.1. You may read the LGPL at http://www.gnu.org/licenses/lgpl.html TODO: - A/L bit (alternate loop) - EN and EXT Out bits - Src B and Src NOTE bits - statusreg Busy and End bits - timer register 0x11 - ch2/ch3 (4 speakers) - PFM (FM using external PCM waveform) - detune (should be same as on other Yamaha chips) - Acc On bit (some sound effects in viprp1?). The documentation says "determines if slot output is accumulated(1), or output directly(0)" - Is memory handling 100% correct? At the moment, seibuspi.c is the only hardware currently emulated that uses external handlers. */ #include #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include "../stdbool.h" #include "ymf271.h" //#define DEVCB_NULL { DEVCB_TYPE_NULL } //#define DEVCB_NULL DEVCB_TYPE_NULL //#define DEVCB_TYPE_NULL (0) #define VERBOSE (1) #define STD_CLOCK (16934400) #define MAXOUT (+32767) #define MINOUT (-32768) #define SIN_BITS 10 #define SIN_LEN (1<> 4; return (entry & 0x0f) << 7; if(ex) } return (ma | 16) << (ex+6); else else { return ma << 7; int shift = 6 + (entry >> 4); } return (0x10 + (entry & 0x0f)) << shift; } lfo_freq = 44100 / lfo_period } */ static const double LFO_frequency_table[256] = { 0.00066, 0.00068, 0.00070, 0.00073, 0.00075, 0.00078, 0.00081, 0.00084, 0.00088, 0.00091, 0.00096, 0.00100, 0.00105, 0.00111, 0.00117, 0.00124, 0.00131, 0.00136, 0.00140, 0.00145, 0.00150, 0.00156, 0.00162, 0.00168, 0.00175, 0.00183, 0.00191, 0.00200, 0.00210, 0.00221, 0.00234, 0.00247, 0.00263, 0.00271, 0.00280, 0.00290, 0.00300, 0.00312, 0.00324, 0.00336, 0.00350, 0.00366, 0.00382, 0.00401, 0.00421, 0.00443, 0.00467, 0.00495, 0.00526, 0.00543, 0.00561, 0.00580, 0.00601, 0.00623, 0.00647, 0.00673, 0.00701, 0.00731, 0.00765, 0.00801, 0.00841, 0.00885, 0.00935, 0.00990, 0.01051, 0.01085, 0.01122, 0.01160, 0.01202, 0.01246, 0.01294, 0.01346, 0.01402, 0.01463, 0.01529, 0.01602, 0.01682, 0.01771, 0.01869, 0.01979, 0.02103, 0.02171, 0.02243, 0.02320, 0.02403, 0.02492, 0.02588, 0.02692, 0.02804, 0.02926, 0.03059, 0.03204, 0.03365, 0.03542, 0.03738, 0.03958, 0.04206, 0.04341, 0.04486, 0.04641, 0.04807, 0.04985, 0.05176, 0.05383, 0.05608, 0.05851, 0.06117, 0.06409, 0.06729, 0.07083, 0.07477, 0.07917, 0.08411, 0.08683, 0.08972, 0.09282, 0.09613, 0.09969, 0.10353, 0.10767, 0.11215, 0.11703, 0.12235, 0.12817, 0.13458, 0.14167, 0.14954, 0.15833, 0.16823, 0.17365, 0.17944, 0.18563, 0.19226, 0.19938, 0.20705, 0.21533, 0.22430, 0.23406, 0.24470, 0.25635, 0.26917, 0.28333, 0.29907, 0.31666, 0.33646, 0.34731, 0.35889, 0.37126, 0.38452, 0.39876, 0.41410, 0.43066, 0.44861, 0.46811, 0.48939, 0.51270, 0.53833, 0.56666, 0.59814, 0.63333, 0.67291, 0.69462, 0.71777, 0.74252, 0.76904, 0.79753, 0.82820, 0.86133, 0.89722, 0.93623, 0.97878, 1.02539, 1.07666, 1.13333, 1.19629, 1.26666, 1.34583, 1.38924, 1.43555, 1.48505, 1.53809, 1.59509, 1.65640, 1.72266, 1.79443, 1.87245, 1.95756, 2.05078, 2.15332, 2.26665, 2.39258, 2.53332, 2.69165, 2.77848, 2.87109, 2.97010, 3.07617, 3.19010, 3.31280, 3.44531, 3.58887, 3.74490, 3.91513, 4.10156, 4.30664, 4.53331, 4.78516, 5.06664, 5.38330, 5.55696, 5.74219, 5.94019, 6.15234, 6.38021, 6.62560, 6.89062, 7.17773, 7.48981, 7.83026, 8.20312, 8.61328, 9.06661, 9.57031, 10.13327, 10.76660, 11.11391, 11.48438, 11.88039, 12.30469, 12.76042, 13.25120, 13.78125, 14.35547, 14.97962, 15.66051, 16.40625, 17.22656, 18.13322, 19.14062, 20.26654, 21.53320, 22.96875, 24.60938, 26.50240, 28.71094, 31.32102, 34.45312, 38.28125, 43.06641, 49.21875, 57.42188, 68.90625, 86.13281, 114.84375, 172.26562, 344.53125 }; static const int RKS_Table[32][8] = { { 0, 0, 0, 0, 0, 2, 4, 8 }, { 0, 0, 0, 0, 1, 3, 5, 9 }, { 0, 0, 0, 1, 2, 4, 6, 10 }, { 0, 0, 0, 1, 3, 5, 7, 11 }, { 0, 0, 1, 2, 4, 6, 8, 12 }, { 0, 0, 1, 2, 5, 7, 9, 13 }, { 0, 0, 1, 3, 6, 8, 10, 14 }, { 0, 0, 1, 3, 7, 9, 11, 15 }, { 0, 1, 2, 4, 8, 10, 12, 16 }, { 0, 1, 2, 4, 9, 11, 13, 17 }, { 0, 1, 2, 5, 10, 12, 14, 18 }, { 0, 1, 2, 5, 11, 13, 15, 19 }, { 0, 1, 3, 6, 12, 14, 16, 20 }, { 0, 1, 3, 6, 13, 15, 17, 21 }, { 0, 1, 3, 7, 14, 16, 18, 22 }, { 0, 1, 3, 7, 15, 17, 19, 23 }, { 0, 2, 4, 8, 16, 18, 20, 24 }, { 0, 2, 4, 8, 17, 19, 21, 25 }, { 0, 2, 4, 9, 18, 20, 22, 26 }, { 0, 2, 4, 9, 19, 21, 23, 27 }, { 0, 2, 5, 10, 20, 22, 24, 28 }, { 0, 2, 5, 10, 21, 23, 25, 29 }, { 0, 2, 5, 11, 22, 24, 26, 30 }, { 0, 2, 5, 11, 23, 25, 27, 31 }, { 0, 3, 6, 12, 24, 26, 28, 31 }, { 0, 3, 6, 12, 25, 27, 29, 31 }, { 0, 3, 6, 13, 26, 28, 30, 31 }, { 0, 3, 6, 13, 27, 29, 31, 31 }, { 0, 3, 7, 14, 28, 30, 31, 31 }, { 0, 3, 7, 14, 29, 31, 31, 31 }, { 0, 3, 7, 15, 30, 31, 31, 31 }, { 0, 3, 7, 15, 31, 31, 31, 31 }, }; static const double multiple_table[16] = { 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; static const double pow_table[16] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0.5, 1, 2, 4, 8, 16, 32, 64 }; static const double fs_frequency[4] = { 1.0/1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0 }; static const double channel_attenuation_table[16] = { 0.0, 2.5, 6.0, 8.5, 12.0, 14.5, 18.1, 20.6, 24.1, 26.6, 30.1, 32.6, 36.1, 96.1, 96.1, 96.1 }; static const int modulation_level[8] = { 16, 8, 4, 2, 1, 32, 64, 128 }; // feedback_level * 16 static const int feedback_level[8] = { 0, 1, 2, 4, 8, 16, 32, 64 }; // slot mapping assists static const int fm_tab[16] = { 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1 }; static const int pcm_tab[16] = { 0, 4, 8, -1, 12, 16, 20, -1, 24, 28, 32, -1, 36, 40, 44, -1 }; typedef struct { UINT8 ext_en; UINT8 ext_out; UINT8 lfoFreq; UINT8 lfowave; UINT8 pms, ams; UINT8 detune; UINT8 multiple; UINT8 tl; UINT8 keyscale; UINT8 ar; UINT8 decay1rate, decay2rate; UINT8 decay1lvl; UINT8 relrate; UINT8 block; UINT8 fns_hi; UINT32 fns; UINT8 feedback; UINT8 waveform; UINT8 accon; UINT8 algorithm; UINT8 ch0_level, ch1_level, ch2_level, ch3_level; UINT32 startaddr; UINT32 loopaddr; UINT32 endaddr; UINT8 altloop; UINT8 fs; UINT8 srcnote, srcb; UINT32 step; UINT64 stepptr; UINT8 active; UINT8 bits; // envelope generator INT32 volume; INT32 env_state; INT32 env_attack_step; // volume increase step in attack state INT32 env_decay1_step; INT32 env_decay2_step; INT32 env_release_step; INT64 feedback_modulation0; INT64 feedback_modulation1; INT32 lfo_phase, lfo_step; INT32 lfo_amplitude; double lfo_phasemod; } YMF271Slot; typedef struct { UINT8 sync, pfm; UINT8 Muted; } YMF271Group; typedef struct { // lookup tables INT16 *lut_waves[8]; double *lut_plfo[4][8]; int *lut_alfo[4]; double lut_ar[64]; double lut_dc[64]; double lut_lfo[256]; int lut_attenuation[16]; int lut_total_level[128]; int lut_env_volume[256]; YMF271Slot slots[48]; YMF271Group groups[12]; UINT8 regs_main[0x10]; UINT32 timerA, timerB; UINT32 timerAVal, timerBVal; UINT32 irqstate; UINT8 status; UINT8 enable; UINT32 ext_address; UINT8 ext_rw; UINT8 ext_readlatch; UINT8 *mem_base; UINT32 mem_size; UINT32 clock; //emu_timer *timA, *timB; //sound_stream * stream; INT32 *mix_buffer; //const device_config *device; //devcb_resolved_read8 ext_mem_read; //devcb_resolved_write8 ext_mem_write; //void (*irq_callback)(const device_config *, int); //void (*irq_callback)(int); } YMF271Chip; #define MAX_CHIPS 0x10 static YMF271Chip YMF271Data[MAX_CHIPS]; /*INLINE YMF271Chip *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YMF271); return (YMF271Chip *)device->token; }*/ static UINT8 ymf271_read_memory(YMF271Chip *chip, UINT32 offset); INLINE void calculate_step(YMF271Slot *slot) { double st; if (slot->waveform == 7) { // external waveform (PCM) st = (double)(2 * (slot->fns | 2048)) * pow_table[slot->block] * fs_frequency[slot->fs]; st = st * multiple_table[slot->multiple]; // LFO phase modulation st *= slot->lfo_phasemod; st /= (double)(524288/65536); // pre-multiply with 65536 slot->step = (UINT32)st; } else { // internal waveform (FM) st = (double)(2 * slot->fns) * pow_table[slot->block]; st = st * multiple_table[slot->multiple] * (double)(SIN_LEN); // LFO phase modulation st *= slot->lfo_phasemod; st /= (double)(536870912/65536); // pre-multiply with 65536 slot->step = (UINT32)st; } } INLINE bool check_envelope_end(YMF271Slot *slot) { if (slot->volume <= 0) { slot->active = 0; slot->volume = 0; return true; } return false; } static void update_envelope(YMF271Slot *slot) { switch (slot->env_state) { case ENV_ATTACK: { slot->volume += slot->env_attack_step; if (slot->volume >= (255 << ENV_VOLUME_SHIFT)) { slot->volume = (255 << ENV_VOLUME_SHIFT); slot->env_state = ENV_DECAY1; } break; } case ENV_DECAY1: { int decay_level = 255 - (slot->decay1lvl << 4); slot->volume -= slot->env_decay1_step; if (!check_envelope_end(slot) && (slot->volume >> ENV_VOLUME_SHIFT) <= decay_level) { slot->env_state = ENV_DECAY2; } break; } case ENV_DECAY2: { slot->volume -= slot->env_decay2_step; check_envelope_end(slot); break; } case ENV_RELEASE: { slot->volume -= slot->env_release_step; check_envelope_end(slot); break; } } } INLINE int get_keyscaled_rate(int rate, int keycode, int keyscale) { int newrate = rate + RKS_Table[keycode][keyscale]; if (newrate > 63) { newrate = 63; } if (newrate < 0) { newrate = 0; } return newrate; } INLINE int get_internal_keycode(int block, int fns) { int n43; if (fns < 0x780) { n43 = 0; } else if (fns < 0x900) { n43 = 1; } else if (fns < 0xa80) { n43 = 2; } else { n43 = 3; } return ((block & 7) * 4) + n43; } INLINE int get_external_keycode(int block, int fns) { int n43; if (fns < 0x100) { n43 = 0; } else if (fns < 0x300) { n43 = 1; } else if (fns < 0x500) { n43 = 2; } else { n43 = 3; } return ((block & 7) * 4) + n43; } static void init_envelope(YMF271Chip *chip, YMF271Slot *slot) { int keycode, rate; int decay_level = 255 - (slot->decay1lvl << 4); if (slot->waveform != 7) { keycode = get_internal_keycode(slot->block, slot->fns); } else { keycode = get_external_keycode(slot->block, slot->fns & 0x7ff); /* keycode = (keycode + slot->srcb * 4 + slot->srcnote) / 2; */ // not sure } // init attack state rate = get_keyscaled_rate(slot->ar * 2, keycode, slot->keyscale); slot->env_attack_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_ar[rate]) * 65536.0); // init decay1 state rate = get_keyscaled_rate(slot->decay1rate * 2, keycode, slot->keyscale); slot->env_decay1_step = (rate < 4) ? 0 : (int)(((double)(255-decay_level) / chip->lut_dc[rate]) * 65536.0); // init decay2 state rate = get_keyscaled_rate(slot->decay2rate * 2, keycode, slot->keyscale); slot->env_decay2_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_dc[rate]) * 65536.0); // init release state //rate = get_keyscaled_rate(slot->relrate * 4, keycode, slot->keyscale); // improved rate as tested by GTheGuardian and kirishima rate = get_keyscaled_rate(slot->relrate * 1.75, keycode, slot->keyscale); slot->env_release_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_ar[rate]) * 65536.0); slot->volume = (255-160) << ENV_VOLUME_SHIFT; // -60db slot->env_state = ENV_ATTACK; } static void init_lfo(YMF271Chip *chip, YMF271Slot *slot) { slot->lfo_phase = 0; slot->lfo_amplitude = 0; slot->lfo_phasemod = 0; slot->lfo_step = (int)((((double)LFO_LENGTH * chip->lut_lfo[slot->lfoFreq]) / 44100.0) * 256.0); } INLINE void update_lfo(YMF271Chip *chip, YMF271Slot *slot) { slot->lfo_phase += slot->lfo_step; slot->lfo_amplitude = chip->lut_alfo[slot->lfowave][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)]; slot->lfo_phasemod = chip->lut_plfo[slot->lfowave][slot->pms][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)]; calculate_step(slot); } INLINE int calculate_slot_volume(YMF271Chip *chip, YMF271Slot *slot) { // Note: Actually everyone of these stores only INT32 (16.16 fixed point), // but the calculations need INT64. INT32 volume; INT64 env_volume; INT64 lfo_volume = 65536; switch (slot->ams) { case 0: lfo_volume = 65536; break; // 0dB case 1: lfo_volume = 65536 - ((slot->lfo_amplitude * 33124) >> 16); break; // 5.90625dB case 2: lfo_volume = 65536 - ((slot->lfo_amplitude * 16742) >> 16); break; // 11.8125dB case 3: lfo_volume = 65536 - ((slot->lfo_amplitude * 4277) >> 16); break; // 23.625dB } env_volume = (chip->lut_env_volume[255 - (slot->volume >> ENV_VOLUME_SHIFT)] * lfo_volume) >> 16; volume = (env_volume * chip->lut_total_level[slot->tl]) >> 16; return volume; } static void update_pcm(YMF271Chip *chip, int slotnum, INT32 *mixp, int length) { int i; INT64 final_volume; INT16 sample; INT64 ch0_vol, ch1_vol; //, ch2_vol, ch3_vol; YMF271Slot *slot = &chip->slots[slotnum]; if (!slot->active) { return; } #ifdef _DEBUG if (slot->waveform != 7) { logerror("Waveform %d in update_pcm !!!\n", slot->waveform); } #endif for (i = 0; i < length; i++) { // loop if ((slot->stepptr>>16) > slot->endaddr) { slot->stepptr = slot->stepptr - ((UINT64)slot->endaddr<<16) + ((UINT64)slot->loopaddr<<16); if ((slot->stepptr>>16) > slot->endaddr) { // overflow slot->stepptr &= 0xffff; slot->stepptr |= ((UINT64)slot->loopaddr<<16); if ((slot->stepptr>>16) > slot->endaddr) { // still overflow? (triggers in rdft2, rarely) slot->stepptr &= 0xffff; slot->stepptr |= ((UINT64)slot->endaddr<<16); } } } if (slot->bits == 8) { // 8bit sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>16))<<8; } else { // 12bit if (slot->stepptr & 0x10000) sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 2)<<8 | ((ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 1) << 4) & 0xf0); else sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3)<<8 | (ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 1) & 0xf0); } update_envelope(slot); update_lfo(chip, slot); final_volume = calculate_slot_volume(chip, slot); ch0_vol = (final_volume * chip->lut_attenuation[slot->ch0_level]) >> 16; ch1_vol = (final_volume * chip->lut_attenuation[slot->ch1_level]) >> 16; // ch2_vol = (final_volume * chip->lut_attenuation[slot->ch2_level]) >> 16; // ch3_vol = (final_volume * chip->lut_attenuation[slot->ch3_level]) >> 16; if (ch0_vol > 65536) ch0_vol = 65536; if (ch1_vol > 65536) ch1_vol = 65536; *mixp++ += (sample * ch0_vol) >> 16; *mixp++ += (sample * ch1_vol) >> 16; // go to next step slot->stepptr += slot->step; } } // calculates the output of one FM operator static INT64 calculate_op(YMF271Chip *chip, int slotnum, INT64 inp) { YMF271Slot *slot = &chip->slots[slotnum]; INT64 env, slot_output, slot_input = 0; update_envelope(slot); update_lfo(chip, slot); env = calculate_slot_volume(chip, slot); if (inp == OP_INPUT_FEEDBACK) { // from own feedback slot_input = (slot->feedback_modulation0 + slot->feedback_modulation1) / 2; slot->feedback_modulation0 = slot->feedback_modulation1; } else if (inp != OP_INPUT_NONE) { // from previous slot output slot_input = ((inp << (SIN_BITS-2)) * modulation_level[slot->feedback]); } slot_output = chip->lut_waves[slot->waveform][((slot->stepptr + slot_input) >> 16) & SIN_MASK]; slot_output = (slot_output * env) >> 16; slot->stepptr += slot->step; return slot_output; } static void set_feedback(YMF271Chip *chip, int slotnum, INT64 inp) { YMF271Slot *slot = &chip->slots[slotnum]; slot->feedback_modulation1 = (((inp << (SIN_BITS-2)) * feedback_level[slot->feedback]) / 16); } //static STREAM_UPDATE( ymf271_update ) void ymf271_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { int i, j; int op; INT32 *mixp; //YMF271Chip *chip = (YMF271Chip *)param; YMF271Chip *chip = &YMF271Data[ChipID]; memset(chip->mix_buffer, 0, sizeof(chip->mix_buffer[0])*samples*2); for (j = 0; j < 12; j++) { YMF271Group *slot_group = &chip->groups[j]; mixp = &chip->mix_buffer[0]; if (slot_group->Muted) continue; #ifdef _DEBUG if (slot_group->pfm && slot_group->sync != 3) { logerror("ymf271 Group %d: PFM, Sync = %d, Waveform Slot1 = %d, Slot2 = %d, Slot3 = %d, Slot4 = %d\n", j, slot_group->sync, chip->slots[j+0].waveform, chip->slots[j+12].waveform, chip->slots[j+24].waveform, chip->slots[j+36].waveform); } #endif switch (slot_group->sync) { // 4 operator FM case 0: { int slot1 = j + (0*12); int slot2 = j + (1*12); int slot3 = j + (2*12); int slot4 = j + (3*12); //mixp = chip->mix_buffer; if (chip->slots[slot1].active) { for (i = 0; i < samples; i++) { INT64 output1 = 0, output2 = 0, output3 = 0, output4 = 0; INT64 phase_mod1 = 0, phase_mod2 = 0, phase_mod3 = 0; switch (chip->slots[slot1].algorithm) { // <--------| // +--[S1]--|--+--[S3]--+--[S2]--+--[S4]--> case 0: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, phase_mod1); phase_mod2 = calculate_op(chip, slot2, phase_mod3); output4 = calculate_op(chip, slot4, phase_mod2); break; // <-----------------| // +--[S1]--+--[S3]--|--+--[S2]--+--[S4]--> case 1: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); phase_mod2 = calculate_op(chip, slot2, phase_mod3); output4 = calculate_op(chip, slot4, phase_mod2); break; // <--------| // +--[S1]--| // | // --[S3]--+--[S2]--+--[S4]--> case 2: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); phase_mod2 = calculate_op(chip, slot2, (phase_mod1 + phase_mod3) / 1); output4 = calculate_op(chip, slot4, phase_mod2); break; // <--------| // +--[S1]--| // | // --[S3]--+--[S2]--+--[S4]--> case 3: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); phase_mod2 = calculate_op(chip, slot2, phase_mod3); output4 = calculate_op(chip, slot4, (phase_mod1 + phase_mod2) / 1); break; // --[S2]--| // <--------| | // +--[S1]--|--+--[S3]--+--[S4]--> case 4: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, phase_mod1); phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); break; // --[S2]-----| // <-----------------| | // +--[S1]--+--[S3]--|--+--[S4]--> case 5: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); break; // --[S2]-----+--[S4]--| // | // <--------| | // +--[S1]--|--+--[S3]--+--> case 6: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output3 = calculate_op(chip, slot3, phase_mod1); phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, phase_mod2); break; // --[S2]--+--[S4]-----| // | // <-----------------| | // +--[S1]--+--[S3]--|--+--> case 7: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); output3 = phase_mod3; phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, phase_mod2); break; // --[S3]--+--[S2]--+--[S4]--| // | // <--------| | // +--[S1]--|-----------------+--> case 8: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); phase_mod2 = calculate_op(chip, slot2, phase_mod3); output4 = calculate_op(chip, slot4, phase_mod2); break; // <--------| // +--[S1]--| // | // --[S3]--| | // --[S2]--+--[S4]--+--> case 9: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); break; // --[S4]--| // --[S2]--| // <--------| | // +--[S1]--|--+--[S3]--+--> case 10: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output3 = calculate_op(chip, slot3, phase_mod1); output2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, OP_INPUT_NONE); break; // --[S4]-----| // --[S2]-----| // <-----------------| | // +--[S1]--+--[S3]--|--+--> case 11: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); output3 = phase_mod3; output2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, OP_INPUT_NONE); break; // |--+--[S4]--| // <--------| |--+--[S3]--| // +--[S1]--|--|--+--[S2]--+--> case 12: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output3 = calculate_op(chip, slot3, phase_mod1); output2 = calculate_op(chip, slot2, phase_mod1); output4 = calculate_op(chip, slot4, phase_mod1); break; // --[S3]--+--[S2]--| // | // --[S4]-----------| // <--------| | // +--[S1]--|--------+--> case 13: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); output2 = calculate_op(chip, slot2, phase_mod3); output4 = calculate_op(chip, slot4, OP_INPUT_NONE); break; // --[S2]-----+--[S4]--| // | // <--------| +--[S3]--| // +--[S1]--|--|--------+--> case 14: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, phase_mod1); phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, phase_mod2); break; // --[S4]-----| // --[S2]-----| // --[S3]-----| // <--------| | // +--[S1]--|--+--> case 15: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, OP_INPUT_NONE); output2 = calculate_op(chip, slot2, OP_INPUT_NONE); output4 = calculate_op(chip, slot4, OP_INPUT_NONE); break; } *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + (output2 * chip->lut_attenuation[chip->slots[slot2].ch0_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level]) + (output4 * chip->lut_attenuation[chip->slots[slot4].ch0_level])) >> 16; *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + (output2 * chip->lut_attenuation[chip->slots[slot2].ch1_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level]) + (output4 * chip->lut_attenuation[chip->slots[slot4].ch1_level])) >> 16; } } break; } // 2x 2 operator FM case 1: { for (op = 0; op < 2; op++) { int slot1 = j + ((op + 0) * 12); int slot3 = j + ((op + 2) * 12); mixp = chip->mix_buffer; if (chip->slots[slot1].active) { for (i = 0; i < samples; i++) { INT64 output1 = 0, output3 = 0; INT64 phase_mod1, phase_mod3 = 0; switch (chip->slots[slot1].algorithm & 3) { // <--------| // +--[S1]--|--+--[S3]--> case 0: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output3 = calculate_op(chip, slot3, phase_mod1); break; // <-----------------| // +--[S1]--+--[S3]--|--> case 1: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); output3 = phase_mod3; break; // --[S3]-----| // <--------| | // +--[S1]--|--+--> case 2: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, OP_INPUT_NONE); break; // // <--------| +--[S3]--| // +--[S1]--|--|--------+--> case 3: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, phase_mod1); break; } *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level])) >> 16; *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level])) >> 16; } } } break; } // 3 operator FM + PCM case 2: { int slot1 = j + (0*12); int slot2 = j + (1*12); int slot3 = j + (2*12); //mixp = chip->mix_buffer; if (chip->slots[slot1].active) { for (i = 0; i < samples; i++) { INT64 output1 = 0, output2 = 0, output3 = 0; INT64 phase_mod1 = 0, phase_mod3 = 0; switch (chip->slots[slot1].algorithm & 7) { // <--------| // +--[S1]--|--+--[S3]--+--[S2]--> case 0: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, phase_mod1); output2 = calculate_op(chip, slot2, phase_mod3); break; // <-----------------| // +--[S1]--+--[S3]--|--+--[S2]--> case 1: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); output2 = calculate_op(chip, slot2, phase_mod3); break; // --[S3]-----| // <--------| | // +--[S1]--|--+--[S2]--> case 2: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); output2 = calculate_op(chip, slot2, (phase_mod1 + phase_mod3) / 1); break; // --[S3]--+--[S2]--| // <--------| | // +--[S1]--|--------+--> case 3: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); output2 = calculate_op(chip, slot2, phase_mod3); break; // --[S2]--| // <--------| | // +--[S1]--|--+--[S3]--+--> case 4: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output3 = calculate_op(chip, slot3, phase_mod1); output2 = calculate_op(chip, slot2, OP_INPUT_NONE); break; // --[S2]--| // <-----------------| | // +--[S1]--+--[S3]--|--+--> case 5: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); phase_mod3 = calculate_op(chip, slot3, phase_mod1); set_feedback(chip, slot1, phase_mod3); output3 = phase_mod3; output2 = calculate_op(chip, slot2, OP_INPUT_NONE); break; // --[S2]-----| // --[S3]-----| // <--------| | // +--[S1]--|--+--> case 6: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, OP_INPUT_NONE); output2 = calculate_op(chip, slot2, OP_INPUT_NONE); break; // --[S2]--| // <--------| +--[S3]--| // +--[S1]--|--|--------+--> case 7: phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); set_feedback(chip, slot1, phase_mod1); output1 = phase_mod1; output3 = calculate_op(chip, slot3, phase_mod1); output2 = calculate_op(chip, slot2, OP_INPUT_NONE); break; } *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + (output2 * chip->lut_attenuation[chip->slots[slot2].ch0_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level])) >> 16; *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + (output2 * chip->lut_attenuation[chip->slots[slot2].ch1_level]) + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level])) >> 16; } } mixp = chip->mix_buffer; update_pcm(chip, j + (3*12), mixp, samples); break; } // PCM case 3: { update_pcm(chip, j + (0*12), mixp, samples); update_pcm(chip, j + (1*12), mixp, samples); update_pcm(chip, j + (2*12), mixp, samples); update_pcm(chip, j + (3*12), mixp, samples); break; } } } mixp = chip->mix_buffer; for (i = 0; i < samples; i++) { outputs[0][i] = (*mixp++)>>2; outputs[1][i] = (*mixp++)>>2; } } static void write_register(YMF271Chip *chip, int slotnum, int reg, UINT8 data) { YMF271Slot *slot = &chip->slots[slotnum]; switch (reg) { case 0x0: slot->ext_en = (data & 0x80) ? 1 : 0; slot->ext_out = (data>>3)&0xf; if (data & 1) { // key on slot->step = 0; slot->stepptr = 0; slot->active = 1; calculate_step(slot); init_envelope(chip, slot); init_lfo(chip, slot); slot->feedback_modulation0 = 0; slot->feedback_modulation1 = 0; } else { if (slot->active) { slot->env_state = ENV_RELEASE; } } break; case 0x1: slot->lfoFreq = data; break; case 0x2: slot->lfowave = data & 3; slot->pms = (data >> 3) & 0x7; slot->ams = (data >> 6) & 0x3; break; case 0x3: slot->multiple = data & 0xf; slot->detune = (data >> 4) & 0x7; break; case 0x4: slot->tl = data & 0x7f; break; case 0x5: slot->ar = data & 0x1f; slot->keyscale = (data >> 5) & 0x7; break; case 0x6: slot->decay1rate = data & 0x1f; break; case 0x7: slot->decay2rate = data & 0x1f; break; case 0x8: slot->relrate = data & 0xf; slot->decay1lvl = (data >> 4) & 0xf; break; case 0x9: // write frequency and block here slot->fns = (slot->fns_hi << 8 & 0x0f00) | data; slot->block = slot->fns_hi >> 4 & 0xf; break; case 0xa: slot->fns_hi = data; break; case 0xb: slot->waveform = data & 0x7; slot->feedback = (data >> 4) & 0x7; slot->accon = (data & 0x80) ? 1 : 0; break; case 0xc: slot->algorithm = data & 0xf; break; case 0xd: slot->ch0_level = data >> 4; slot->ch1_level = data & 0xf; break; case 0xe: slot->ch2_level = data >> 4; slot->ch3_level = data & 0xf; break; default: break; } } static void ymf271_write_fm(YMF271Chip *chip, int bank, UINT8 address, UINT8 data) { int groupnum = fm_tab[address & 0xf]; int reg = (address >> 4) & 0xf; int sync_reg; int sync_mode; if (groupnum == -1) { logerror("ymf271_write_fm invalid group %02X %02X\n", address, data); return; } // check if the register is a synchronized register sync_reg = 0; switch (reg) { case 0: case 9: case 10: case 12: case 13: case 14: sync_reg = 1; break; default: break; } // check if the slot is key on slot for synchronizing sync_mode = 0; switch (chip->groups[groupnum].sync) { // 4 slot mode case 0: if (bank == 0) sync_mode = 1; break; // 2x 2 slot mode case 1: if (bank == 0 || bank == 1) sync_mode = 1; break; // 3 slot + 1 slot mode case 2: if (bank == 0) sync_mode = 1; break; default: break; } // key-on slot & synced register if (sync_mode && sync_reg) { switch (chip->groups[groupnum].sync) { // 4 slot mode case 0: write_register(chip, (12 * 0) + groupnum, reg, data); write_register(chip, (12 * 1) + groupnum, reg, data); write_register(chip, (12 * 2) + groupnum, reg, data); write_register(chip, (12 * 3) + groupnum, reg, data); break; // 2x 2 slot mode case 1: if (bank == 0) { // Slot 1 - Slot 3 write_register(chip, (12 * 0) + groupnum, reg, data); write_register(chip, (12 * 2) + groupnum, reg, data); } else { // Slot 2 - Slot 4 write_register(chip, (12 * 1) + groupnum, reg, data); write_register(chip, (12 * 3) + groupnum, reg, data); } break; // 3 slot + 1 slot mode (1 slot is handled normally) case 2: write_register(chip, (12 * 0) + groupnum, reg, data); write_register(chip, (12 * 1) + groupnum, reg, data); write_register(chip, (12 * 2) + groupnum, reg, data); break; } } else { // write register normally write_register(chip, (12 * bank) + groupnum, reg, data); } } static void ymf271_write_pcm(YMF271Chip *chip, UINT8 address, UINT8 data) { int slotnum = pcm_tab[address & 0xf]; YMF271Slot *slot; if (slotnum == -1) { logerror("ymf271_write_pcm invalid slot %02X %02X\n", address, data); return; } slot = &chip->slots[slotnum]; switch ((address >> 4) & 0xf) { case 0x0: slot->startaddr &= ~0xff; slot->startaddr |= data; break; case 0x1: slot->startaddr &= ~0xff00; slot->startaddr |= data<<8; break; case 0x2: slot->startaddr &= ~0xff0000; slot->startaddr |= (data & 0x7f)<<16; slot->altloop = (data & 0x80) ? 1 : 0; //if (slot->altloop) // popmessage("ymf271 A/L, contact MAMEdev"); break; case 0x3: slot->endaddr &= ~0xff; slot->endaddr |= data; break; case 0x4: slot->endaddr &= ~0xff00; slot->endaddr |= data<<8; break; case 0x5: slot->endaddr &= ~0xff0000; slot->endaddr |= (data & 0x7f)<<16; break; case 0x6: slot->loopaddr &= ~0xff; slot->loopaddr |= data; break; case 0x7: slot->loopaddr &= ~0xff00; slot->loopaddr |= data<<8; break; case 0x8: slot->loopaddr &= ~0xff0000; slot->loopaddr |= (data & 0x7f)<<16; break; case 0x9: slot->fs = data & 0x3; slot->bits = (data & 0x4) ? 12 : 8; slot->srcnote = (data >> 3) & 0x3; slot->srcb = (data >> 5) & 0x7; break; default: break; } } /*static TIMER_CALLBACK( ymf271_timer_a_tick ) { YMF271Chip *chip = (YMF271Chip *)ptr; chip->status |= 1; if (chip->enable & 4) { chip->irqstate |= 1; if (chip->irq_callback) chip->irq_callback(chip->device, 1); } } static TIMER_CALLBACK( ymf271_timer_b_tick ) { YMF271Chip *chip = (YMF271Chip *)ptr; chip->status |= 2; if (chip->enable & 8) { chip->irqstate |= 2; if (chip->irq_callback) chip->irq_callback(chip->device, 1); } }*/ static UINT8 ymf271_read_memory(YMF271Chip *chip, UINT32 offset) { /*if (m_ext_read_handler.isnull()) { if (offset < chip->mem_size) return chip->mem_base[offset]; // 8MB chip limit (shouldn't happen) else if (offset > 0x7fffff) return chip->mem_base[offset & 0x7fffff]; else return 0; } else return m_ext_read_handler(offset);*/ offset &= 0x7FFFFF; if (offset < chip->mem_size) return chip->mem_base[offset]; else return 0; } static void ymf271_write_timer(YMF271Chip *chip, UINT8 address, UINT8 data) { if ((address & 0xf0) == 0) { int groupnum = fm_tab[address & 0xf]; YMF271Group *group; if (groupnum == -1) { logerror("ymf271_write_timer invalid group %02X %02X\n", address, data); return; } group = &chip->groups[groupnum]; group->sync = data & 0x3; group->pfm = data >> 7; } else { switch (address) { case 0x10: chip->timerA = data; break; case 0x11: // According to Yamaha's documentation, this sets timer A upper 2 bits // (it says timer A is 10 bits). But, PCB audio recordings proves // otherwise: it doesn't affect timer A frequency. (see ms32.c tetrisp) // Does this register have another function regarding timer A/B? break; case 0x12: chip->timerB = data; break; case 0x13: // timer A load if (~chip->enable & data & 1) { //attotime period = attotime::from_hz(chip->clock) * (384 * 4 * (256 - chip->timerA)); //chip->timA->adjust((data & 1) ? period : attotime::never, 0); } // timer B load if (~chip->enable & data & 2) { //attotime period = attotime::from_hz(chip->clock) * (384 * 16 * (256 - chip->timerB)); //chip->timB->adjust((data & 2) ? period : attotime::never, 0); } // timer A reset if (data & 0x10) { chip->irqstate &= ~1; chip->status &= ~1; //if (!chip->irq_handler.isnull() && ~chip->irqstate & 2) // chip->irq_handler(0); } // timer B reset if (data & 0x20) { chip->irqstate &= ~2; chip->status &= ~2; //if (!chip->irq_handler.isnull() && ~chip->irqstate & 1) // chip->irq_handler(0); } chip->enable = data; break; case 0x14: chip->ext_address &= ~0xff; chip->ext_address |= data; break; case 0x15: chip->ext_address &= ~0xff00; chip->ext_address |= data << 8; break; case 0x16: chip->ext_address &= ~0xff0000; chip->ext_address |= (data & 0x7f) << 16; chip->ext_rw = (data & 0x80) ? 1 : 0; break; case 0x17: chip->ext_address = (chip->ext_address + 1) & 0x7fffff; //if (!chip->ext_rw && !chip->ext_write_handler.isnull()) // chip->ext_write_handler(chip->ext_address, data); break; } } } //WRITE8_DEVICE_HANDLER( ymf271_w ) void ymf271_w(UINT8 ChipID, offs_t offset, UINT8 data) { //YMF271Chip *chip = get_safe_token(device); YMF271Chip *chip = &YMF271Data[ChipID]; chip->regs_main[offset & 0xf] = data; switch (offset & 0xf) { case 0x0: case 0x2: case 0x4: case 0x6: case 0x8: case 0xc: // address regs break; case 0x1: ymf271_write_fm(chip, 0, chip->regs_main[0x0], data); break; case 0x3: ymf271_write_fm(chip, 1, chip->regs_main[0x2], data); break; case 0x5: ymf271_write_fm(chip, 2, chip->regs_main[0x4], data); break; case 0x7: ymf271_write_fm(chip, 3, chip->regs_main[0x6], data); break; case 0x9: ymf271_write_pcm(chip, chip->regs_main[0x8], data); break; case 0xd: ymf271_write_timer(chip, chip->regs_main[0xc], data); break; default: break; } } //READ8_DEVICE_HANDLER( ymf271_r ) UINT8 ymf271_r(UINT8 ChipID, offs_t offset) { //YMF271Chip *chip = get_safe_token(device); YMF271Chip *chip = &YMF271Data[ChipID]; switch (offset & 0xf) { case 0x0: return chip->status; case 0x1: // statusreg 2 return 0; case 0x2: { UINT8 ret; if (!chip->ext_rw) return 0xff; ret = chip->ext_readlatch; chip->ext_address = (chip->ext_address + 1) & 0x7fffff; chip->ext_readlatch = ymf271_read_memory(chip, chip->ext_address); return ret; } default: break; } return 0xff; } static void init_tables(YMF271Chip *chip) { int i,j; double clock_correction; for (i = 0; i < 8; i++) chip->lut_waves[i] = (INT16*)malloc(sizeof(INT16) * SIN_LEN); for (i = 0; i < 4*8; i++) chip->lut_plfo[i>>3][i&7] = (double*)malloc(sizeof(double) * LFO_LENGTH); for (i = 0; i < 4; i++) chip->lut_alfo[i] = (int*)malloc(sizeof(int) * LFO_LENGTH); for (i=0; i < SIN_LEN; i++) { double m = sin( ((i*2)+1) * M_PI / SIN_LEN ); double m2 = sin( ((i*4)+1) * M_PI / SIN_LEN ); // Waveform 0: sin(wt) (0 <= wt <= 2PI) chip->lut_waves[0][i] = (INT16)(m * MAXOUT); // Waveform 1: sin?(wt) (0 <= wt <= PI) -sin?(wt) (PI <= wt <= 2PI) chip->lut_waves[1][i] = (i < (SIN_LEN/2)) ? (INT16)((m * m) * MAXOUT) : (INT16)((m * m) * MINOUT); // Waveform 2: sin(wt) (0 <= wt <= PI) -sin(wt) (PI <= wt <= 2PI) chip->lut_waves[2][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : (INT16)(-m * MAXOUT); // Waveform 3: sin(wt) (0 <= wt <= PI) 0 chip->lut_waves[3][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : 0; // Waveform 4: sin(2wt) (0 <= wt <= PI) 0 chip->lut_waves[4][i] = (i < (SIN_LEN/2)) ? (INT16)(m2 * MAXOUT) : 0; // Waveform 5: |sin(2wt)| (0 <= wt <= PI) 0 chip->lut_waves[5][i] = (i < (SIN_LEN/2)) ? (INT16)(fabs(m2) * MAXOUT) : 0; // Waveform 6: 1 (0 <= wt <= 2PI) chip->lut_waves[6][i] = (INT16)(1 * MAXOUT); chip->lut_waves[7][i] = 0; } for (i = 0; i < LFO_LENGTH; i++) { int tri_wave; double ftri_wave, fsaw_wave; double plfo[4]; // LFO phase modulation plfo[0] = 0; fsaw_wave = ((i % (LFO_LENGTH/2)) * PLFO_MAX) / (double)((LFO_LENGTH/2)-1); plfo[1] = (i < (LFO_LENGTH/2)) ? fsaw_wave : fsaw_wave - PLFO_MAX; plfo[2] = (i < (LFO_LENGTH/2)) ? PLFO_MAX : PLFO_MIN; ftri_wave = ((i % (LFO_LENGTH/4)) * PLFO_MAX) / (double)(LFO_LENGTH/4); switch (i / (LFO_LENGTH/4)) { case 0: plfo[3] = ftri_wave; break; case 1: plfo[3] = PLFO_MAX - ftri_wave; break; case 2: plfo[3] = 0 - ftri_wave; break; case 3: plfo[3] = 0 - (PLFO_MAX - ftri_wave); break; default: plfo[3] = 0; /*assert(0);*/ break; } for (j = 0; j < 4; j++) { chip->lut_plfo[j][0][i] = pow(2.0, 0.0); chip->lut_plfo[j][1][i] = pow(2.0, (3.378 * plfo[j]) / 1200.0); chip->lut_plfo[j][2][i] = pow(2.0, (5.0646 * plfo[j]) / 1200.0); chip->lut_plfo[j][3][i] = pow(2.0, (6.7495 * plfo[j]) / 1200.0); chip->lut_plfo[j][4][i] = pow(2.0, (10.1143 * plfo[j]) / 1200.0); chip->lut_plfo[j][5][i] = pow(2.0, (20.1699 * plfo[j]) / 1200.0); chip->lut_plfo[j][6][i] = pow(2.0, (40.1076 * plfo[j]) / 1200.0); chip->lut_plfo[j][7][i] = pow(2.0, (79.307 * plfo[j]) / 1200.0); } // LFO amplitude modulation chip->lut_alfo[0][i] = 0; chip->lut_alfo[1][i] = ALFO_MAX - ((i * ALFO_MAX) / LFO_LENGTH); chip->lut_alfo[2][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX : ALFO_MIN; tri_wave = ((i % (LFO_LENGTH/2)) * ALFO_MAX) / (LFO_LENGTH/2); chip->lut_alfo[3][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX-tri_wave : tri_wave; } for (i = 0; i < 256; i++) { chip->lut_env_volume[i] = (int)(65536.0 / pow(10.0, ((double)i / (256.0 / 96.0)) / 20.0)); } for (i = 0; i < 16; i++) { chip->lut_attenuation[i] = (int)(65536.0 / pow(10.0, channel_attenuation_table[i] / 20.0)); } for (i = 0; i < 128; i++) { double db = 0.75 * (double)i; chip->lut_total_level[i] = (int)(65536.0 / pow(10.0, db / 20.0)); } // timing may use a non-standard XTAL clock_correction = (double)(STD_CLOCK) / (double)(chip->clock); for (i = 0; i < 256; i++) { chip->lut_lfo[i] = LFO_frequency_table[i] * clock_correction; } for (i = 0; i < 64; i++) { // attack/release rate in number of samples chip->lut_ar[i] = (ARTime[i] * clock_correction * 44100.0) / 1000.0; } for (i = 0; i < 64; i++) { // decay rate in number of samples chip->lut_dc[i] = (DCTime[i] * clock_correction * 44100.0) / 1000.0; } } /*static void init_state(YMF271Chip *chip, const device_config *device) { int i; for (i = 0; i < ARRAY_LENGTH(chip->slots); i++) { state_save_register_device_item(device, i, chip->slots[i].ext_out); state_save_register_device_item(device, i, chip->slots[i].lfoFreq); state_save_register_device_item(device, i, chip->slots[i].pms); state_save_register_device_item(device, i, chip->slots[i].ams); state_save_register_device_item(device, i, chip->slots[i].detune); state_save_register_device_item(device, i, chip->slots[i].multiple); state_save_register_device_item(device, i, chip->slots[i].tl); state_save_register_device_item(device, i, chip->slots[i].keyscale); state_save_register_device_item(device, i, chip->slots[i].ar); state_save_register_device_item(device, i, chip->slots[i].decay1rate); state_save_register_device_item(device, i, chip->slots[i].decay2rate); state_save_register_device_item(device, i, chip->slots[i].decay1lvl); state_save_register_device_item(device, i, chip->slots[i].relrate); state_save_register_device_item(device, i, chip->slots[i].fns); state_save_register_device_item(device, i, chip->slots[i].block); state_save_register_device_item(device, i, chip->slots[i].feedback); state_save_register_device_item(device, i, chip->slots[i].waveform); state_save_register_device_item(device, i, chip->slots[i].accon); state_save_register_device_item(device, i, chip->slots[i].algorithm); state_save_register_device_item(device, i, chip->slots[i].ch0_level); state_save_register_device_item(device, i, chip->slots[i].ch1_level); state_save_register_device_item(device, i, chip->slots[i].ch2_level); state_save_register_device_item(device, i, chip->slots[i].ch3_level); state_save_register_device_item(device, i, chip->slots[i].startaddr); state_save_register_device_item(device, i, chip->slots[i].loopaddr); state_save_register_device_item(device, i, chip->slots[i].endaddr); state_save_register_device_item(device, i, chip->slots[i].fs); state_save_register_device_item(device, i, chip->slots[i].srcnote); state_save_register_device_item(device, i, chip->slots[i].srcb); state_save_register_device_item(device, i, chip->slots[i].step); state_save_register_device_item(device, i, chip->slots[i].stepptr); state_save_register_device_item(device, i, chip->slots[i].active); state_save_register_device_item(device, i, chip->slots[i].bits); state_save_register_device_item(device, i, chip->slots[i].volume); state_save_register_device_item(device, i, chip->slots[i].env_state); state_save_register_device_item(device, i, chip->slots[i].env_attack_step); state_save_register_device_item(device, i, chip->slots[i].env_decay1_step); state_save_register_device_item(device, i, chip->slots[i].env_decay2_step); state_save_register_device_item(device, i, chip->slots[i].env_release_step); state_save_register_device_item(device, i, chip->slots[i].feedback_modulation0); state_save_register_device_item(device, i, chip->slots[i].feedback_modulation1); state_save_register_device_item(device, i, chip->slots[i].lfo_phase); state_save_register_device_item(device, i, chip->slots[i].lfo_step); state_save_register_device_item(device, i, chip->slots[i].lfo_amplitude); } for (i = 0; i < sizeof(chip->groups) / sizeof(chip->groups[0]); i++) { state_save_register_device_item(device, i, chip->groups[i].sync); state_save_register_device_item(device, i, chip->groups[i].pfm); } state_save_register_device_item(device, 0, chip->timerA); state_save_register_device_item(device, 0, chip->timerB); state_save_register_device_item(device, 0, chip->timerAVal); state_save_register_device_item(device, 0, chip->timerBVal); state_save_register_device_item(device, 0, chip->irqstate); state_save_register_device_item(device, 0, chip->status); state_save_register_device_item(device, 0, chip->enable); state_save_register_device_item(device, 0, chip->reg0); state_save_register_device_item(device, 0, chip->reg1); state_save_register_device_item(device, 0, chip->reg2); state_save_register_device_item(device, 0, chip->reg3); state_save_register_device_item(device, 0, chip->pcmreg); state_save_register_device_item(device, 0, chip->timerreg); state_save_register_device_item(device, 0, chip->ext_address); state_save_register_device_item(device, 0, chip->ext_read); }*/ //static DEVICE_START( ymf271 ) int device_start_ymf271(UINT8 ChipID, int clock) { //static const ymf271_interface defintrf = { DEVCB_NULL }; //const ymf271_interface *intf; int i; //YMF271Chip *chip = get_safe_token(device); YMF271Chip *chip; if (ChipID >= MAX_CHIPS) return 0; chip = &YMF271Data[ChipID]; //chip->device = device; chip->clock = clock; //intf = (device->static_config != NULL) ? (const ymf271_interface *)device->static_config : &defintrf; chip->mem_size = 0x00; chip->mem_base = NULL; init_tables(chip); //init_state(chip); //chip->stream = stream_create(device, 0, 2, device->clock/384, chip, ymf271_update); //chip->mix_buffer = auto_alloc_array(machine, INT32, 44100*2); chip->mix_buffer = (INT32*)malloc(44100*2 * sizeof(INT32)); for (i = 0; i < 12; i ++) chip->groups[i].Muted = 0x00; return clock/384; } //static DEVICE_STOP( ymf271 ) void device_stop_ymf271(UINT8 ChipID) { int i; YMF271Chip *chip = &YMF271Data[ChipID]; free(chip->mem_base); chip->mem_base = NULL; for (i=0; i < 8; i++) { free(chip->lut_waves[i]); chip->lut_waves[i] = NULL; } for (i = 0; i < 4*8; i++) { free(chip->lut_plfo[i>>3][i&7]); chip->lut_plfo[i>>3][i&7] = NULL; } for (i = 0; i < 4; i++) { free(chip->lut_alfo[i]); chip->lut_alfo[i] = NULL; } free(chip->mix_buffer); chip->mix_buffer = NULL; return; } //static DEVICE_RESET( ymf271 ) void device_reset_ymf271(UINT8 ChipID) { int i; //YMF271Chip *chip = get_safe_token(device); YMF271Chip *chip = &YMF271Data[ChipID]; for (i = 0; i < 48; i++) { chip->slots[i].active = 0; chip->slots[i].volume = 0; } // reset timers and IRQ //chip->timA->reset(); //chip->timB->reset(); chip->irqstate = 0; chip->status = 0; chip->enable = 0; //if (!chip->irq_handler.isnull()) // chip->irq_handler(0); } void ymf271_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YMF271Chip *chip = &YMF271Data[ChipID]; if (chip->mem_size != ROMSize) { chip->mem_base = (UINT8*)realloc(chip->mem_base, ROMSize); chip->mem_size = ROMSize; memset(chip->mem_base, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->mem_base + DataStart, ROMData, DataLength); return; } void ymf271_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { YMF271Chip *chip = &YMF271Data[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 12; CurChn ++) chip->groups[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ymf271 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(YMF271Chip); break; // --- the following bits of info are returned as pointers to data or functions case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymf271 ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ymf271 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YMF271"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/ymf271.h ================================================ #pragma once //#include "devcb.h" //typedef struct _ymf271_interface ymf271_interface; //struct _ymf271_interface //{ // //devcb_read8 ext_read; /* external memory read */ // //devcb_write8 ext_write; /* external memory write */ // //void (*irq_callback)(const device_config *device, int state); /* irq callback */ // void (*irq_callback)(int state); /* irq callback */ //}; /*READ8_DEVICE_HANDLER( ymf271_r ); WRITE8_DEVICE_HANDLER( ymf271_w ); DEVICE_GET_INFO( ymf271 ); #define SOUND_YMF271 DEVICE_GET_INFO_NAME( ymf271 )*/ void ymf271_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ymf271(UINT8 ChipID, int clock); void device_stop_ymf271(UINT8 ChipID); void device_reset_ymf271(UINT8 ChipID); UINT8 ymf271_r(UINT8 ChipID, offs_t offset); void ymf271_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf271_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ymf271_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/chips/ymf278b.c ================================================ /* YMF278B FM + Wave table Synthesizer (OPL4) Timer and PCM YMF278B. The FM is shared with the ymf262. This chip roughly splits the difference between the Sega 315-5560 MultiPCM (Multi32, Model 1/2) and YMF 292-F SCSP (later Model 2, STV, Saturn, Model 3). Features as listed in LSI-4MF2782 data sheet: FM Synthesis (same as YMF262) 1. Sound generation mode Two-operater mode Generates eighteen voices or fifteen voices plus five rhythm sounds simultaneously Four-operator mode Generates six voices in four-operator mode plus six voices in two-operator mode simultaneously, or generates six voices in four-operator mode plus three voices in two-operator mode plus five rhythm sounds simultaneously 2. Eight selectable waveforms 3. Stereo output Wave Table Synthesis 1. Generates twenty-four voices simultaneously 2. 44.1kHz sampling rate for output sound data 3. Selectable from 8-bit, 12-bit and 16-bit word lengths for wave data 4. Stereo output (16-stage panpot for each voice) Wave Data 1. Accepts 32M bit external memory at maximum 2. Up to 512 wave tables 3. External ROM or SRAM can be connected. With SRAM connected, the CPU can download wave data 4. Outputs chip select signals for 1Mbit, 4Mbit, 8Mbit or 16Mbit memory 5. Can be directly connected to the Yamaha YRW801 (Wave data ROM) Features of YRW801 as listed in LSI 4RW801A2 Built-in wave data of tones which comply with GM system Level 1 Melody tone ....... 128 tones Percussion tone ... 47 tones 16Mbit capacity (2,097,152word x 8) By R. Belmont and O. Galibert. Copyright R. Belmont and O. Galibert. This software is dual-licensed: it may be used in MAME and properly licensed MAME derivatives under the terms of the MAME license. For use outside of MAME and properly licensed derivatives, it is available under the terms of the GNU Lesser General Public License (LGPL), version 2.1. You may read the LGPL at http://www.gnu.org/licenses/lgpl.html Changelog: Sep. 8, 2002 - fixed ymf278b_compute_rate when OCT is negative (RB) Dec. 11, 2002 - added ability to set non-standard clock rates (RB) fixed envelope target for release (fixes missing instruments in hotdebut). Thanks to Team Japump! for MP3s from a real PCB. fixed crash if MAME is run with no sound. June 4, 2003 - Changed to dual-license with LGPL for use in OpenMSX. OpenMSX contributed a bugfix where looped samples were not being addressed properly, causing pitch fluctuation. August 15, 2010 - Backport to MAME-style C from OpenMSX */ #include #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" //#include "cpuintrf.h" #include #include // for memset #include // for NULL #include #include #include "ymf262.h" #include "ymf278b.h" typedef struct { UINT32 startaddr; UINT32 loopaddr; UINT32 endaddr; UINT32 step; /* fixed-point frequency step */ UINT32 stepptr; /* fixed-point pointer into the sample */ UINT16 pos; INT16 sample1, sample2; INT32 env_vol; INT32 lfo_cnt; INT32 lfo_step; INT32 lfo_max; INT16 wave; /* wavetable number */ INT16 FN; /* f-number */ INT8 OCT; /* octave */ INT8 PRVB; /* pseudo-reverb */ INT8 LD; /* level direct */ INT8 TL; /* total level */ INT8 pan; /* panpot */ INT8 lfo; /* LFO */ INT8 vib; /* vibrato */ INT8 AM; /* AM level */ INT8 AR; INT8 D1R; INT32 DL; INT8 D2R; INT8 RC; /* rate correction */ INT8 RR; INT8 bits; /* width of the samples */ INT8 active; /* slot keyed on */ INT8 state; INT8 lfo_active; UINT8 Muted; } YMF278BSlot; typedef struct { YMF278BSlot slots[24]; UINT32 eg_cnt; /* Global envelope generator counter. */ INT8 wavetblhdr; INT8 memmode; INT32 memadr; UINT8 exp; INT32 fm_l, fm_r; INT32 pcm_l, pcm_r; //UINT8 timer_a_count, timer_b_count, enable, current_irq; //emu_timer *timer_a, *timer_b; //int irq_line; UINT8 port_A, port_B, port_C; //void (*irq_callback)(const device_config *, int); void (*irq_callback)(int); //const device_config *device; UINT32 ROMSize; UINT8 *rom; UINT32 RAMSize; UINT8 *ram; int clock; INT32 volume[256*4]; // precalculated attenuation values with some marging for enveloppe and pan levels UINT8 regs[256]; void *fmchip; UINT8 FMEnabled; // that saves a whole lot of CPU //sound_stream * stream; } YMF278BChip; extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x10 static YMF278BChip YMF278BData[MAX_CHIPS]; static UINT32 ROMFileSize = 0x00; static UINT8* ROMFile = NULL; char* FindFile(const char* FileName); // from VGMPlay_Intf.h/VGMPlay.c #define EG_SH 16 // 16.16 fixed point (EG timing) #define EG_TIMER_OVERFLOW (1 << EG_SH) // envelope output entries #define ENV_BITS 11 // -VB #define ENV_LEN (1 << ENV_BITS) #define ENV_STEP (128.0 / ENV_LEN) #define MAX_ATT_INDEX 511 #define MIN_ATT_INDEX 0 // Envelope Generator phases #define EG_ATT 4 #define EG_DEC 3 #define EG_SUS 2 #define EG_REL 1 #define EG_OFF 0 #define EG_REV 5 // pseudo reverb #define EG_DMP 6 // damp // Pan values, units are -3dB, i.e. 8. const INT32 pan_left[16] = { 0, 8, 16, 24, 32, 40, 48, 256, 256, 0, 0, 0, 0, 0, 0, 0 }; const INT32 pan_right[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 256, 256, 48, 40, 32, 24, 16, 8 }; // Mixing levels, units are -3dB, and add some marging to avoid clipping const INT32 mix_level[8] = { 8, 16, 24, 32, 40, 48, 56, 256+8 }; // decay level table (3dB per step) // 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB) #define SC(db) (db * (2.0 / ENV_STEP)) const UINT32 dl_tab[16] = { SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7), SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31) }; #undef SC #define RATE_STEPS 8 const UINT8 eg_inc[15 * RATE_STEPS] = { //cycle:0 1 2 3 4 5 6 7 0, 1, 0, 1, 0, 1, 0, 1, // 0 rates 00..12 0 (increment by 0 or 1) 0, 1, 0, 1, 1, 1, 0, 1, // 1 rates 00..12 1 0, 1, 1, 1, 0, 1, 1, 1, // 2 rates 00..12 2 0, 1, 1, 1, 1, 1, 1, 1, // 3 rates 00..12 3 1, 1, 1, 1, 1, 1, 1, 1, // 4 rate 13 0 (increment by 1) 1, 1, 1, 2, 1, 1, 1, 2, // 5 rate 13 1 1, 2, 1, 2, 1, 2, 1, 2, // 6 rate 13 2 1, 2, 2, 2, 1, 2, 2, 2, // 7 rate 13 3 2, 2, 2, 2, 2, 2, 2, 2, // 8 rate 14 0 (increment by 2) 2, 2, 2, 4, 2, 2, 2, 4, // 9 rate 14 1 2, 4, 2, 4, 2, 4, 2, 4, // 10 rate 14 2 2, 4, 4, 4, 2, 4, 4, 4, // 11 rate 14 3 4, 4, 4, 4, 4, 4, 4, 4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay 8, 8, 8, 8, 8, 8, 8, 8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) 0, 0, 0, 0, 0, 0, 0, 0, // 14 infinity rates for attack and decay(s) }; #define O(a) (a * RATE_STEPS) const UINT8 eg_rate_select[64] = { O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 4),O( 5),O( 6),O( 7), O( 8),O( 9),O(10),O(11), O(12),O(12),O(12),O(12), }; #undef O // rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 // mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 #define O(a) (a) const UINT8 eg_rate_shift[64] = { O(12),O(12),O(12),O(12), O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), }; #undef O // number of steps to take in quarter of lfo frequency // TODO check if frequency matches real chip #define O(a) ((EG_TIMER_OVERFLOW / a) / 6) const INT32 lfo_period[8] = { O(0.168), O(2.019), O(3.196), O(4.206), O(5.215), O(5.888), O(6.224), O(7.066) }; #undef O #define O(a) (a * 65536) const INT32 vib_depth[8] = { O(0), O(3.378), O(5.065), O(6.750), O(10.114), O(20.170), O(40.106), O(79.307) }; #undef O #define SC(db) (INT32)(db * (2.0 / ENV_STEP)) const INT32 am_depth[8] = { SC(0), SC(1.781), SC(2.906), SC(3.656), SC(4.406), SC(5.906), SC(7.406), SC(11.91) }; #undef SC void ymf278b_slot_reset(YMF278BSlot* slot) { slot->wave = slot->FN = slot->OCT = slot->PRVB = slot->LD = slot->TL = slot->pan = slot->lfo = slot->vib = slot->AM = 0; slot->AR = slot->D1R = slot->DL = slot->D2R = slot->RC = slot->RR = 0; slot->step = slot->stepptr = 0; slot->bits = slot->startaddr = slot->loopaddr = slot->endaddr = 0; slot->env_vol = MAX_ATT_INDEX; slot->lfo_active = 0; slot->lfo_cnt = slot->lfo_step = 0; slot->lfo_max = lfo_period[0]; slot->state = EG_OFF; slot->active = 0; // not strictly needed, but avoid UMR on savestate slot->pos = slot->sample1 = slot->sample2 = 0; } INLINE int ymf278b_slot_compute_rate(YMF278BSlot* slot, int val) { int res; int oct; if (val == 0) return 0; else if (val == 15) return 63; if (slot->RC != 15) { oct = slot->OCT; if (oct & 8) { oct |= -8; } res = (oct + slot->RC); if (res < 0) res = 0; else if (res > 15) res = 15; res = res * 2 + ((slot->FN & 0x200) ? 1 : 0) + val * 4; } else { res = val * 4; } if (res < 0) res = 0; else if (res > 63) res = 63; return res; } INLINE int ymf278b_slot_compute_vib(YMF278BSlot* slot) { return (((slot->lfo_step << 8) / slot->lfo_max) * vib_depth[slot->vib]) >> 24; } INLINE int ymf278b_slot_compute_am(YMF278BSlot* slot) { if (slot->lfo_active && slot->AM) return (((slot->lfo_step << 8) / slot->lfo_max) * am_depth[slot->AM]) >> 12; else return 0; } INLINE void ymf278b_slot_set_lfo(YMF278BSlot* slot, int newlfo) { slot->lfo_step = (((slot->lfo_step << 8) / slot->lfo_max) * newlfo) >> 8; slot->lfo_cnt = (((slot->lfo_cnt << 8) / slot->lfo_max) * newlfo) >> 8; slot->lfo = newlfo; slot->lfo_max = lfo_period[slot->lfo]; } INLINE void ymf278b_advance(YMF278BChip* chip) { YMF278BSlot* op; int i; UINT8 rate; UINT8 shift; UINT8 select; chip->eg_cnt ++; for (i = 0; i < 24; i ++) { op = &chip->slots[i]; if (op->lfo_active) { op->lfo_cnt ++; if (op->lfo_cnt < op->lfo_max) { op->lfo_step ++; } else if (op->lfo_cnt < (op->lfo_max * 3)) { op->lfo_step --; } else { op->lfo_step ++; if (op->lfo_cnt == (op->lfo_max * 4)) op->lfo_cnt = 0; } } // Envelope Generator switch(op->state) { case EG_ATT: // attack phase rate = ymf278b_slot_compute_rate(op, op->AR); if (rate < 4) break; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += (~op->env_vol * eg_inc[select + ((chip->eg_cnt >> shift) & 7)]) >> 4; // -VB if (op->env_vol <= MIN_ATT_INDEX) { op->env_vol = MIN_ATT_INDEX; if (op->DL) op->state = EG_DEC; else op->state = EG_SUS; } } break; case EG_DEC: // decay phase rate = ymf278b_slot_compute_rate(op, op->D1R); if (rate < 4) break; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; if ((op->env_vol > dl_tab[6]) && op->PRVB) op->state = EG_REV; else { if (op->env_vol >= op->DL) op->state = EG_SUS; } } break; case EG_SUS: // sustain phase rate = ymf278b_slot_compute_rate(op, op->D2R); if (rate < 4) break; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; if ((op->env_vol > dl_tab[6]) && op->PRVB) op->state = EG_REV; else { if (op->env_vol >= MAX_ATT_INDEX) { op->env_vol = MAX_ATT_INDEX; op->active = 0; } } } break; case EG_REL: // release phase rate = ymf278b_slot_compute_rate(op, op->RR); if (rate < 4) break; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; if ((op->env_vol > dl_tab[6]) && op->PRVB) op->state = EG_REV; else { if (op->env_vol >= MAX_ATT_INDEX) { op->env_vol = MAX_ATT_INDEX; op->active = 0; } } } break; case EG_REV: // pseudo reverb // TODO improve env_vol update rate = ymf278b_slot_compute_rate(op, 5); //if (rate < 4) // break; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; if (op->env_vol >= MAX_ATT_INDEX) { op->env_vol = MAX_ATT_INDEX; op->active = 0; } } break; case EG_DMP: // damping // TODO improve env_vol update, damp is just fastest decay now rate = 56; shift = eg_rate_shift[rate]; if (! (chip->eg_cnt & ((1 << shift) - 1))) { select = eg_rate_select[rate]; op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; if (op->env_vol >= MAX_ATT_INDEX) { op->env_vol = MAX_ATT_INDEX; op->active = 0; } } break; case EG_OFF: // nothing break; default: #ifdef _DEBUG //logerror(...); #endif break; } } } INLINE UINT8 ymf278b_readMem(YMF278BChip* chip, offs_t address) { if (address < chip->ROMSize) return chip->rom[address&0x3fffff]; else if (address < chip->ROMSize + chip->RAMSize) return chip->ram[address - (chip->ROMSize&0x3fffff)]; else return 255; // TODO check } INLINE UINT8* ymf278b_readMemAddr(YMF278BChip* chip, offs_t address) { if (address < chip->ROMSize) return &chip->rom[address&0x3fffff]; else if (address < chip->ROMSize + chip->RAMSize) return &chip->ram[address - (chip->ROMSize&0x3fffff)]; else return NULL; // TODO check } INLINE void ymf278b_writeMem(YMF278BChip* chip, offs_t address, UINT8 value) { if (address < chip->ROMSize) return; // can't write to ROM else if (address < chip->ROMSize + chip->RAMSize) chip->ram[address - chip->ROMSize] = value; else return; // can't write to unmapped memory return; } INLINE INT16 ymf278b_getSample(YMF278BChip* chip, YMF278BSlot* op) { INT16 sample; UINT32 addr; UINT8* addrp; switch (op->bits) { case 0: // 8 bit sample = ymf278b_readMem(chip, op->startaddr + op->pos) << 8; break; case 1: // 12 bit addr = op->startaddr + ((op->pos / 2) * 3); addrp = ymf278b_readMemAddr(chip, addr); if (op->pos & 1) sample = (addrp[2] << 8) | ((addrp[1] << 4) & 0xF0); else sample = (addrp[0] << 8) | (addrp[1] & 0xF0); break; case 2: // 16 bit addr = op->startaddr + (op->pos * 2); addrp = ymf278b_readMemAddr(chip, addr); sample = (addrp[0] << 8) | addrp[1]; break; default: // TODO unspecified sample = 0; break; } return sample; } int ymf278b_anyActive(YMF278BChip* chip) { int i; for (i = 0; i < 24; i ++) { if (chip->slots[i].active) return 1; } return 0; } void ymf278b_pcm_update(UINT8 ChipID, stream_sample_t** outputs, int samples) { YMF278BChip* chip = &YMF278BData[ChipID]; int i; unsigned int j; INT32 vl; INT32 vr; if (chip->FMEnabled) { /* memset is done by ymf262_update */ ymf262_update_one(chip->fmchip, outputs, samples); // apply FM mixing level vl = mix_level[chip->fm_l] - 8; vl = chip->volume[vl]; vr = mix_level[chip->fm_r] - 8; vr = chip->volume[vr]; // make FM softer by 3 db vl = (vl * 0x16A) >> 8; vr = (vr * 0x16A) >> 8; for (j = 0; j < samples; j ++) { outputs[0][j] = (outputs[0][j] * vl) >> 15; outputs[1][j] = (outputs[1][j] * vr) >> 15; } } else { memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); } if (! ymf278b_anyActive(chip)) { // TODO update internal state, even if muted // TODO also mute individual channels return; } vl = mix_level[chip->pcm_l]; vr = mix_level[chip->pcm_r]; for (j = 0; j < samples; j ++) { for (i = 0; i < 24; i ++) { YMF278BSlot* sl; INT16 sample; int vol; int volLeft; int volRight; sl = &chip->slots[i]; if (! sl->active || sl->Muted) { //outputs[0][j] += 0; //outputs[1][j] += 0; continue; } sample = (sl->sample1 * (0x10000 - sl->stepptr) + sl->sample2 * sl->stepptr) >> 16; vol = sl->TL + (sl->env_vol >> 2) + ymf278b_slot_compute_am(sl); volLeft = vol + pan_left [sl->pan] + vl; volRight = vol + pan_right[sl->pan] + vr; // TODO prob doesn't happen in real chip //volLeft = std::max(0, volLeft); //volRight = std::max(0, volRight); volLeft &= 0x3FF; // catch negative Volume values in a hardware-like way volRight &= 0x3FF; // (anything beyond 0x100 results in *0) outputs[0][j] += (sample * chip->volume[volLeft] ) >> 17; outputs[1][j] += (sample * chip->volume[volRight]) >> 17; if (sl->lfo_active && sl->vib) { int oct; unsigned int step; oct = sl->OCT; if (oct & 8) oct |= -8; oct += 5; step = (sl->FN | 1024) + ymf278b_slot_compute_vib(sl); if (oct >= 0) step <<= oct; else step >>= -oct; sl->stepptr += step; } else sl->stepptr += sl->step; if (sl->stepptr >= 0x10000) { sl->sample1 = sl->sample2; sl->sample2 = ymf278b_getSample(chip, sl); sl->pos += (sl->stepptr >> 16); sl->stepptr &= 0xFFFF; if (sl->pos > sl->endaddr) sl->pos = sl->pos - sl->endaddr + sl->loopaddr - 1; } } ymf278b_advance(chip); } } INLINE void ymf278b_keyOnHelper(YMF278BChip* chip, YMF278BSlot* slot) { int oct; unsigned int step; slot->active = 1; oct = slot->OCT; if (oct & 8) oct |= -8; oct += 5; step = slot->FN | 1024; if (oct >= 0) step <<= oct; else step >>= -oct; slot->step = step; slot->state = EG_ATT; slot->stepptr = 0; slot->pos = 0; slot->sample1 = ymf278b_getSample(chip, slot); slot->pos = 1; slot->sample2 = ymf278b_getSample(chip, slot); } static void ymf278b_A_w(YMF278BChip *chip, UINT8 reg, UINT8 data) { switch(reg) { case 0x02: //chip->timer_a_count = data; //ymf278b_timer_a_reset(chip); break; case 0x03: //chip->timer_b_count = data; //ymf278b_timer_b_reset(chip); break; case 0x04: /*if(data & 0x80) chip->current_irq = 0; else { UINT8 old_enable = chip->enable; chip->enable = data; chip->current_irq &= ~data; if((old_enable ^ data) & 1) ymf278b_timer_a_reset(chip); if((old_enable ^ data) & 2) ymf278b_timer_b_reset(chip); } ymf278b_irq_check(chip);*/ break; default: //#ifdef _DEBUG // logerror("YMF278B: Port A write %02x, %02x\n", reg, data); //#endif ymf262_write(chip->fmchip, 1, data); if ((reg & 0xF0) == 0xB0 && (data & 0x20)) // Key On set chip->FMEnabled = 0x01; else if (reg == 0xBD && (data & 0x1F)) // one of the Rhythm bits set chip->FMEnabled = 0x01; break; } } static void ymf278b_B_w(YMF278BChip *chip, UINT8 reg, UINT8 data) { switch(reg) { case 0x05: // OPL3/OPL4 Enable // Bit 1 enables OPL4 WaveTable Synth chip->exp = data; ymf262_write(chip->fmchip, 3, data & ~0x02); break; default: ymf262_write(chip->fmchip, 3, data); if ((reg & 0xF0) == 0xB0 && (data & 0x20)) chip->FMEnabled = 0x01; break; } //#ifdef _DEBUG // logerror("YMF278B: Port B write %02x, %02x\n", reg, data); //#endif } void ymf278b_C_w(YMF278BChip* chip, UINT8 reg, UINT8 data) { // Handle slot registers specifically if (reg >= 0x08 && reg <= 0xF7) { int snum = (reg - 8) % 24; YMF278BSlot* slot = &chip->slots[snum]; int base; UINT8* buf; int oct; unsigned int step; switch((reg - 8) / 24) { case 0: //loadTime = time + LOAD_DELAY; slot->wave = (slot->wave & 0x100) | data; base = (slot->wave < 384 || ! chip->wavetblhdr) ? (slot->wave * 12) : (chip->wavetblhdr * 0x80000 + ((slot->wave - 384) * 12)); buf = ymf278b_readMemAddr(chip, base); slot->bits = (buf[0] & 0xC0) >> 6; ymf278b_slot_set_lfo(slot, (buf[7] >> 3) & 7); slot->vib = buf[7] & 7; slot->AR = buf[8] >> 4; slot->D1R = buf[8] & 0xF; slot->DL = dl_tab[buf[9] >> 4]; slot->D2R = buf[9] & 0xF; slot->RC = buf[10] >> 4; slot->RR = buf[10] & 0xF; slot->AM = buf[11] & 7; slot->startaddr = buf[2] | (buf[1] << 8) | ((buf[0] & 0x3F) << 16); slot->loopaddr = buf[4] + (buf[3] << 8); slot->endaddr = ((buf[6] + (buf[5] << 8)) ^ 0xFFFF); if (chip->regs[reg + 4] & 0x080) ymf278b_keyOnHelper(chip, slot); break; case 1: slot->wave = (slot->wave & 0xFF) | ((data & 0x1) << 8); slot->FN = (slot->FN & 0x380) | (data >> 1); oct = slot->OCT; if (oct & 8) oct |= -8; oct += 5; step = slot->FN | 1024; if (oct >= 0) step <<= oct; else step >>= -oct; slot->step = step; break; case 2: slot->FN = (slot->FN & 0x07F) | ((data & 0x07) << 7); slot->PRVB = ((data & 0x08) >> 3); slot->OCT = ((data & 0xF0) >> 4); oct = slot->OCT; if (oct & 8) oct |= -8; oct += 5; step = slot->FN | 1024; if (oct >= 0) step <<= oct; else step >>= -oct; slot->step = step; break; case 3: slot->TL = data >> 1; slot->LD = data & 0x1; // TODO if (slot->LD) { // directly change volume } else { // interpolate volume } break; case 4: if (data & 0x10) { // output to DO1 pin: // this pin is not used in moonsound // we emulate this by muting the sound slot->pan = 8; // both left/right -inf dB } else slot->pan = data & 0x0F; if (data & 0x020) { // LFO reset slot->lfo_active = 0; slot->lfo_cnt = 0; slot->lfo_max = lfo_period[slot->vib]; slot->lfo_step = 0; } else { // LFO activate slot->lfo_active = 1; } switch (data >> 6) { case 0: // tone off, no damp if (slot->active && (slot->state != EG_REV)) slot->state = EG_REL; break; case 2: // tone on, no damp if (! (chip->regs[reg] & 0x080)) ymf278b_keyOnHelper(chip, slot); break; case 1: // tone off, damp case 3: // tone on, damp slot->state = EG_DMP; break; } break; case 5: slot->vib = data & 0x7; ymf278b_slot_set_lfo(slot, (data >> 3) & 0x7); break; case 6: slot->AR = data >> 4; slot->D1R = data & 0xF; break; case 7: slot->DL = dl_tab[data >> 4]; slot->D2R = data & 0xF; break; case 8: slot->RC = data >> 4; slot->RR = data & 0xF; break; case 9: slot->AM = data & 0x7; break; } } else { // All non-slot registers switch (reg) { case 0x00: // TEST case 0x01: break; case 0x02: chip->wavetblhdr = (data >> 2) & 0x7; chip->memmode = data & 1; break; case 0x03: chip->memadr = (chip->memadr & 0x00FFFF) | (data << 16); break; case 0x04: chip->memadr = (chip->memadr & 0xFF00FF) | (data << 8); break; case 0x05: chip->memadr = (chip->memadr & 0xFFFF00) | data; break; case 0x06: // memory data //busyTime = time + MEM_WRITE_DELAY; ymf278b_writeMem(chip, chip->memadr, data); chip->memadr = (chip->memadr + 1) & 0xFFFFFF; break; case 0xF8: // TODO use these chip->fm_l = data & 0x7; chip->fm_r = (data >> 3) & 0x7; break; case 0xF9: chip->pcm_l = data & 0x7; chip->pcm_r = (data >> 3) & 0x7; break; } } chip->regs[reg] = data; } UINT8 ymf278b_readReg(YMF278BChip* chip, UINT8 reg) { // no need to call updateStream(time) UINT8 result; switch(reg) { case 2: // 3 upper bits are device ID result = (chip->regs[2] & 0x1F) | 0x20; break; case 6: // Memory Data Register //busyTime = time + MEM_READ_DELAY; result = ymf278b_readMem(chip, chip->memadr); chip->memadr = (chip->memadr + 1) & 0xFFFFFF; break; default: result = chip->regs[reg]; break; } return result; } UINT8 ymf278b_peekReg(YMF278BChip* chip, UINT8 reg) { UINT8 result; switch(reg) { case 2: // 3 upper bits are device ID result = (chip->regs[2] & 0x1F) | 0x20; break; case 6: // Memory Data Register result = ymf278b_readMem(chip, chip->memadr); break; default: result = chip->regs[reg]; break; } return result; } UINT8 ymf278b_readStatus(YMF278BChip* chip) { UINT8 result = 0; //if (time < busyTime) // result |= 0x01; //if (time < loadTime) // result |= 0x02; return result; } //WRITE8_DEVICE_HANDLER( ymf278b_w ) void ymf278b_w(UINT8 ChipID, offs_t offset, UINT8 data) { //YMF278BChip *chip = get_safe_token(device); YMF278BChip *chip = &YMF278BData[ChipID]; switch (offset) { case 0: chip->port_A = data; ymf262_write(chip->fmchip, offset, data); break; case 1: ymf278b_A_w(chip, chip->port_A, data); break; case 2: chip->port_B = data; ymf262_write(chip->fmchip, offset, data); break; case 3: ymf278b_B_w(chip, chip->port_B, data); break; case 4: chip->port_C = data; break; case 5: // PCM regs are only accessible if NEW2 is set if (~chip->exp & 2) break; ymf278b_C_w(chip, chip->port_C, data); break; default: #ifdef _DEBUG logerror("YMF278B: unexpected write at offset %X to ymf278b = %02X\n", offset, data); #endif break; } } void ymf278b_clearRam(YMF278BChip* chip) { memset(chip->ram, 0, chip->RAMSize); } static void ymf278b_load_rom(YMF278BChip *chip) { const char* ROM_FILENAME = "yrw801.rom"; char* FileName; FILE* hFile; size_t RetVal; if (! ROMFileSize) { ROMFileSize = 0x00200000; ROMFile = (UINT8*)malloc(ROMFileSize); memset(ROMFile, 0xFF, ROMFileSize); FileName = FindFile(ROM_FILENAME); if (FileName != NULL) { hFile = fopen(FileName, "rb"); free(FileName); } else { hFile = NULL; } if (hFile != NULL) { RetVal = fread(ROMFile, 0x01, ROMFileSize, hFile); fclose(hFile); if (RetVal != ROMFileSize) fprintf(stderr, "Error while reading OPL4 Sample ROM (%s)!\n", ROM_FILENAME); } else { fprintf(stderr, "Warning! OPL4 Sample ROM (%s) not found!\n", ROM_FILENAME); } } chip->ROMSize = ROMFileSize; chip->rom = (UINT8*)malloc(chip->ROMSize); memcpy(chip->rom, ROMFile, chip->ROMSize); return; } static int ymf278b_init(YMF278BChip *chip, int clock, void (*cb)(int)) { int rate; rate = clock / 768; //if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || // CHIP_SAMPLING_MODE == 0x02) // rate = CHIP_SAMPLE_RATE; chip->fmchip = ymf262_init(clock * 8 / 19, rate); chip->FMEnabled = 0x00; chip->rom = NULL; chip->irq_callback = cb; //chip->timer_a = timer_alloc(device->machine, ymf278b_timer_a_tick, chip); //chip->timer_b = timer_alloc(device->machine, ymf278b_timer_b_tick, chip); chip->clock = clock; ymf278b_load_rom(chip); chip->RAMSize = 0x00080000; chip->ram = (UINT8*)malloc(chip->RAMSize); ymf278b_clearRam(chip); return rate; } //static DEVICE_START( ymf278b ) int device_start_ymf278b(UINT8 ChipID, int clock) { static const ymf278b_interface defintrf = { 0 }; const ymf278b_interface *intf; int i; YMF278BChip *chip; int rate; if (ChipID >= MAX_CHIPS) return 0; chip = &YMF278BData[ChipID]; //chip->device = device; //intf = (device->static_config != NULL) ? (const ymf278b_interface *)device->static_config : &defintrf; intf = &defintrf; rate = ymf278b_init(chip, clock, intf->irq_callback); //chip->stream = stream_create(device, 0, 2, device->clock/768, chip, ymf278b_pcm_update); chip->memadr = 0; // avoid UMR // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB for (i = 0; i < 256; i ++) { int vol_mul = 0x20 - (i & 0x0F); // 0x10 values per 6 db int vol_shift = 5 + (i >> 4); // approximation: -6 dB == divide by two (shift right) chip->volume[i] = (0x8000 * vol_mul) >> vol_shift; } for (i = 256; i < 256 * 4; i ++) chip->volume[i] = 0; for (i = 0; i < 24; i ++) chip->slots[i].Muted = 0x00;; return rate; } //static DEVICE_STOP( ymf278 ) void device_stop_ymf278b(UINT8 ChipID) { YMF278BChip* chip = &YMF278BData[ChipID]; ymf262_shutdown(chip->fmchip); free(chip->rom); chip->rom = NULL; free(chip->ram); chip->ram = NULL; return; } void device_reset_ymf278b(UINT8 ChipID) { YMF278BChip* chip = &YMF278BData[ChipID]; int i; ymf262_reset_chip(chip->fmchip); chip->FMEnabled = 0x00; chip->eg_cnt = 0; for (i = 0; i < 24; i ++) ymf278b_slot_reset(&chip->slots[i]); for (i = 255; i >= 0; i --) // reverse order to avoid UMR ymf278b_C_w(chip, i, 0); chip->wavetblhdr = chip->memmode = chip->memadr = 0; chip->fm_l = chip->fm_r = 3; chip->pcm_l = chip->pcm_r = 0; //busyTime = time; //loadTime = time; } void ymf278b_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YMF278BChip *chip = &YMF278BData[ChipID]; if (chip->ROMSize != ROMSize) { chip->rom = (UINT8*)realloc(chip->rom, ROMSize); chip->ROMSize = ROMSize; memset(chip->rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->rom + DataStart, ROMData, DataLength); return; } void ymf278b_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { YMF278BChip *chip = &YMF278BData[ChipID]; if (DataStart >= chip->RAMSize) return; if (DataStart + DataLength > chip->RAMSize) DataLength = chip->RAMSize - DataStart; memcpy(chip->ram + DataStart, RAMData, DataLength); return; } void ymf278b_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskWT) { YMF278BChip *chip = &YMF278BData[ChipID]; UINT8 CurChn; ymf262_set_mutemask(chip->fmchip, MuteMaskFM); for (CurChn = 0; CurChn < 24; CurChn ++) chip->slots[CurChn].Muted = (MuteMaskWT >> CurChn) & 0x01; return; } ================================================ FILE: VGMPlay/chips/ymf278b.h ================================================ #pragma once #define YMF278B_STD_CLOCK (33868800) /* standard clock for OPL4 */ typedef struct _ymf278b_interface ymf278b_interface; struct _ymf278b_interface { //void (*irq_callback)(const device_config *device, int state); /* irq callback */ void (*irq_callback)(int state); /* irq callback */ }; /*READ8_DEVICE_HANDLER( ymf278b_r ); WRITE8_DEVICE_HANDLER( ymf278b_w ); DEVICE_GET_INFO( ymf278b ); #define SOUND_YMF278B DEVICE_GET_INFO_NAME( ymf278b )*/ void ymf278b_pcm_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ymf278b(UINT8 ChipID, int clock); void device_stop_ymf278b(UINT8 ChipID); void device_reset_ymf278b(UINT8 ChipID); UINT8 ymf278b_r(UINT8 ChipID, offs_t offset); void ymf278b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymf278b_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ymf278b_write_ram(UINT8 ChipID, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); void ymf278b_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskWT); ================================================ FILE: VGMPlay/chips/ymz280b.c ================================================ /* Yamaha YMZ280B driver by Aaron Giles YMZ280B 8-Channel PCMD8 PCM/ADPCM Decoder Features as listed in LSI-4MZ280B3 data sheet: Voice data stored in external memory can be played back simultaneously for up to eight voices Voice data format can be selected from 4-bit ADPCM, 8-bit PCM and 16-bit PCM Control of voice data external memory Up to 16M bytes of ROM or SRAM (x 8 bits, access time 150ms max) can be connected Continuous access is possible Loop playback between selective addresses is possible Voice data playback frequency control 4-bit ADPCM ................ 0.172 to 44.1kHz in 256 steps 8-bit PCM, 16-bit PCM ...... 0.172 to 88.2kHz in 512 steps 256 steps total level and 16 steps panpot can be set Voice signal is output in stereo 16-bit 2's complement MSB-first format TODO: - Is memory handling 100% correct? At the moment, Konami firebeat.c is the only hardware currently emulated that uses external handlers. It also happens to be the only one using 16-bit PCM. Some other drivers (eg. bishi.c, bfm_sc4/5.c) also use ROM readback. */ #include #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #ifdef _DEBUG #include #endif #include #include // for memset #include // for NULL #include "ymz280b.h" static void update_irq_state_timer_common(void *param, int voicenum); //unsigned char DISABLE_YMZ_FIX = 0x00; #define MAX_SAMPLE_CHUNK 0x10000 #define MAKE_WAVS 0 #define MAKE_WAVS_CH 0 #define FRAC_BITS 14 #define FRAC_ONE (1 << FRAC_BITS) #define FRAC_MASK (FRAC_ONE - 1) #define INTERNAL_BUFFER_SIZE (1 << 15) //#define INTERNAL_SAMPLE_RATE (chip->master_clock * 2.0) #define INTERNAL_SAMPLE_RATE chip->rate #if MAKE_WAVS #include "wavwrite.h" #endif #if MAKE_WAVS_CH #include FILE* hWavFile[8]; signed short int* wavmem[8]; #endif /* struct describing a single playing ADPCM voice */ struct YMZ280BVoice { UINT8 playing; /* 1 if we are actively playing */ UINT8 keyon; /* 1 if the key is on */ UINT8 looping; /* 1 if looping is enabled */ UINT8 mode; /* current playback mode */ UINT16 fnum; /* frequency */ UINT8 level; /* output level */ UINT8 pan; /* panning */ UINT32 start; /* start address, in nibbles */ UINT32 stop; /* stop address, in nibbles */ UINT32 loop_start; /* loop start address, in nibbles */ UINT32 loop_end; /* loop end address, in nibbles */ UINT32 position; /* current position, in nibbles */ INT32 signal; /* current ADPCM signal */ INT32 step; /* current ADPCM step */ INT32 loop_signal; /* signal at loop start */ INT32 loop_step; /* step at loop start */ UINT32 loop_count; /* number of loops so far */ INT32 output_left; /* output volume (left) */ INT32 output_right; /* output volume (right) */ INT32 output_step; /* step value for frequency conversion */ INT32 output_pos; /* current fractional position */ INT16 last_sample; /* last sample output */ INT16 curr_sample; /* current sample target */ UINT8 irq_schedule; /* 1 if the IRQ state is updated by timer */ UINT8 Muted; /* used for muting */ }; typedef struct _ymz280b_state ymz280b_state; struct _ymz280b_state { //sound_stream * stream; /* which stream are we using */ UINT8 *region_base; /* pointer to the base of the region */ UINT32 region_size; UINT8 current_register; /* currently accessible register */ UINT8 status_register; /* current status register */ UINT8 irq_state; /* current IRQ state */ UINT8 irq_mask; /* current IRQ mask */ UINT8 irq_enable; /* current IRQ enable */ UINT8 keyon_enable; /* key on enable */ UINT8 ext_mem_enable; /* external memory enable */ UINT8 ext_readlatch; /* external memory prefetched data */ UINT32 ext_mem_address_hi; UINT32 ext_mem_address_mid; UINT32 ext_mem_address; /* where the CPU can read the ROM */ double master_clock; /* master clock frequency */ double rate; //void (*irq_callback)(const device_config *, int); /* IRQ callback */ void (*irq_callback)(int); /* IRQ callback */ struct YMZ280BVoice voice[8]; /* the 8 voices */ //devcb_resolved_read8 ext_ram_read; /* external RAM read handler */ //devcb_resolved_write8 ext_ram_write; /* external RAM write handler */ #if MAKE_WAVS void * wavresample; /* resampled waveform */ #endif INT16 *scratch; //const device_config *device; }; static void write_to_register(ymz280b_state *chip, int data); /* step size index shift table */ static const int index_scale[8] = { 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 }; /* lookup table for the precomputed difference */ static int diff_lookup[16]; static unsigned char lookup_init = 0x00; /* lookup-table is initialized */ /* timer callback */ /*static TIMER_CALLBACK( update_irq_state_timer_0 ); static TIMER_CALLBACK( update_irq_state_timer_1 ); static TIMER_CALLBACK( update_irq_state_timer_2 ); static TIMER_CALLBACK( update_irq_state_timer_3 ); static TIMER_CALLBACK( update_irq_state_timer_4 ); static TIMER_CALLBACK( update_irq_state_timer_5 ); static TIMER_CALLBACK( update_irq_state_timer_6 ); static TIMER_CALLBACK( update_irq_state_timer_7 ); static const timer_fired_func update_irq_state_cb[] = { update_irq_state_timer_0, update_irq_state_timer_1, update_irq_state_timer_2, update_irq_state_timer_3, update_irq_state_timer_4, update_irq_state_timer_5, update_irq_state_timer_6, update_irq_state_timer_7 };*/ extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; #define MAX_CHIPS 0x10 static ymz280b_state YMZ280BData[MAX_CHIPS]; /*INLINE ymz280b_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YMZ280B); return (ymz280b_state *)device->token; }*/ INLINE void update_irq_state(ymz280b_state *chip) { int irq_bits = chip->status_register & chip->irq_mask; /* always off if the enable is off */ if (!chip->irq_enable) irq_bits = 0; /* update the state if changed */ if (irq_bits && !chip->irq_state) { chip->irq_state = 1; if (chip->irq_callback) //(*chip->irq_callback)(chip->device, 1); (*chip->irq_callback)(1); //else logerror("YMZ280B: IRQ generated, but no callback specified!"); } else if (!irq_bits && chip->irq_state) { chip->irq_state = 0; if (chip->irq_callback) //(*chip->irq_callback)(chip->device, 0); (*chip->irq_callback)(0); //else logerror("YMZ280B: IRQ generated, but no callback specified!"); } } INLINE void update_step(ymz280b_state *chip, struct YMZ280BVoice *voice) { double frequency; /* compute the frequency */ if (voice->mode == 1) frequency = chip->master_clock * (double)((voice->fnum & 0x0ff) + 1) * (1.0 / 256.0); else frequency = chip->master_clock * (double)((voice->fnum & 0x1ff) + 1) * (1.0 / 256.0); voice->output_step = (UINT32)(frequency * (double)FRAC_ONE / INTERNAL_SAMPLE_RATE); } INLINE void update_volumes(struct YMZ280BVoice *voice) { if (voice->pan == 8) { voice->output_left = voice->level; voice->output_right = voice->level; } else if (voice->pan < 8) { voice->output_left = voice->level; /* pan 1 is hard-left, what's pan 0? for now assume same as pan 1 */ voice->output_right = (voice->pan == 0) ? 0 : voice->level * (voice->pan - 1) / 7; } else { voice->output_left = voice->level * (15 - voice->pan) / 7; voice->output_right = voice->level; } } //static STATE_POSTLOAD( YMZ280B_state_save_update_step ) /*void YMZ280B_state_save_update_step(void *param) { ymz280b_state *chip = (ymz280b_state *)param; int j; for (j = 0; j < 8; j++) { struct YMZ280BVoice *voice = &chip->voice[j]; update_step(chip, voice); if(voice->irq_schedule) // timer_set(machine, attotime_zero, chip, 0, update_irq_state_cb[j]); update_irq_state_timer_common(param, j); } }*/ INLINE UINT8 ymz280b_read_memory(UINT8 *base, UINT32 size, UINT32 offset) { offset &= 0xFFFFFF; if (offset < size) return base[offset]; /* 16MB chip limit (shouldn't happen) */ //else if (offset > 0xffffff) // return base[offset & 0xffffff]; else return 0; } static void update_irq_state_timer_common(void *param, int voicenum) { ymz280b_state *chip = (ymz280b_state *)param; struct YMZ280BVoice *voice = &chip->voice[voicenum]; if(!voice->irq_schedule) return; voice->playing = 0; chip->status_register |= 1 << voicenum; update_irq_state(chip); voice->irq_schedule = 0; } /*static TIMER_CALLBACK( update_irq_state_timer_0 ) { update_irq_state_timer_common(ptr, 0); } static TIMER_CALLBACK( update_irq_state_timer_1 ) { update_irq_state_timer_common(ptr, 1); } static TIMER_CALLBACK( update_irq_state_timer_2 ) { update_irq_state_timer_common(ptr, 2); } static TIMER_CALLBACK( update_irq_state_timer_3 ) { update_irq_state_timer_common(ptr, 3); } static TIMER_CALLBACK( update_irq_state_timer_4 ) { update_irq_state_timer_common(ptr, 4); } static TIMER_CALLBACK( update_irq_state_timer_5 ) { update_irq_state_timer_common(ptr, 5); } static TIMER_CALLBACK( update_irq_state_timer_6 ) { update_irq_state_timer_common(ptr, 6); } static TIMER_CALLBACK( update_irq_state_timer_7 ) { update_irq_state_timer_common(ptr, 7); }*/ /********************************************************************************************** compute_tables -- compute the difference tables ***********************************************************************************************/ static void compute_tables(void) { int nib; if (lookup_init) return; /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (nib & 0x07) * 2 + 1; diff_lookup[nib] = (nib & 0x08) ? -value : value; } lookup_init = 0x01; } /********************************************************************************************** generate_adpcm -- general ADPCM decoding routine ***********************************************************************************************/ static int generate_adpcm(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { int position = voice->position; int signal = voice->signal; int step = voice->step; int val; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ //val = base[position / 2] >> ((~position & 1) << 2); val = ymz280b_read_memory(base, size, position / 2) >> ((~position & 1) << 2); signal = (signal * 254) / 256; signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ //val = base[position / 2] >> ((~position & 1) << 2); val = ymz280b_read_memory(base, size, position / 2) >> ((~position & 1) << 2); signal = (signal * 254) / 256; signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if (position == voice->loop_start && voice->loop_count == 0) { voice->loop_signal = signal; voice->loop_step = step; } if (position >= voice->loop_end) { if (voice->keyon) { position = voice->loop_start; signal = voice->loop_signal; step = voice->loop_step; voice->loop_count++; } } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; voice->signal = signal; voice->step = step; return samples; } /********************************************************************************************** generate_pcm8 -- general 8-bit PCM decoding routine ***********************************************************************************************/ static int generate_pcm8(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { int position = voice->position; int val; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = base[position / 2]; val = ymz280b_read_memory(base, size, position / 2); /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = base[position / 2]; val = ymz280b_read_memory(base, size, position / 2); /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if (position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; return samples; } /********************************************************************************************** generate_pcm16 -- general 16-bit PCM decoding routine ***********************************************************************************************/ static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { int position = voice->position; int val; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* is it even used in any MAME game? */ //popmessage("YMZ280B 16-bit PCM contact MAMEDEV"); /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ // the manual says "16-bit 2's complement MSB-first format" //val = (INT16)((base[position / 2 + 0] << 8) + base[position / 2 + 1]); val = (INT16)((ymz280b_read_memory(base, size, position / 2 + 0) << 8) + ymz280b_read_memory(base, size, position / 2 + 1)); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = (INT16)((base[position / 2 + 0] << 8) + base[position / 2 + 1]); val = (INT16)((ymz280b_read_memory(base, size, position / 2 + 0) << 8) + ymz280b_read_memory(base, size, position / 2 + 1)); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if (position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; return samples; } /********************************************************************************************** ymz280b_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( ymz280b_update ) void ymz280b_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { //ymz280b_state *chip = (ymz280b_state *)param; ymz280b_state *chip = &YMZ280BData[ChipID]; stream_sample_t *lacc = outputs[0]; stream_sample_t *racc = outputs[1]; int v; /* clear out the accumulator */ memset(lacc, 0, samples * sizeof(lacc[0])); memset(racc, 0, samples * sizeof(racc[0])); /* loop over voices */ for (v = 0; v < 8; v++) { struct YMZ280BVoice *voice = &chip->voice[v]; INT16 prev = voice->last_sample; INT16 curr = voice->curr_sample; INT16 *curr_data = chip->scratch; INT32 *ldest = lacc; INT32 *rdest = racc; UINT32 new_samples, samples_left; UINT32 final_pos; int remaining = samples; int lvol = voice->output_left; int rvol = voice->output_right; #if MAKE_WAVS_CH signed short int* wavlog; memset(wavmem[v], 0x00, samples * 0x02); wavlog = wavmem[v]; #endif /* skip if muted */ if (voice->Muted) continue; /* quick out if we're not playing and we're at 0 */ if (!voice->playing && curr == 0 && prev == 0) { /* make sure next sound plays immediately */ voice->output_pos = FRAC_ONE; continue; } /* finish off the current sample */ /* interpolate */ while (remaining > 0 && voice->output_pos < FRAC_ONE) { int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS; *ldest++ += interp_sample * lvol; *rdest++ += interp_sample * rvol; #if MAKE_WAVS_CH *(wavlog ++) = (signed short int)interp_sample; #endif voice->output_pos += voice->output_step; remaining--; } /* if we're over, continue; otherwise, we're done */ if (voice->output_pos >= FRAC_ONE) voice->output_pos -= FRAC_ONE; else continue; /* compute how many new samples we need */ final_pos = voice->output_pos + remaining * voice->output_step; new_samples = (final_pos + FRAC_ONE) >> FRAC_BITS; if (new_samples > MAX_SAMPLE_CHUNK) new_samples = MAX_SAMPLE_CHUNK; samples_left = new_samples; /* generate them into our buffer */ switch (voice->playing << 7 | voice->mode) { case 0x81: samples_left = generate_adpcm(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; case 0x82: samples_left = generate_pcm8(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; case 0x83: samples_left = generate_pcm16(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; default: samples_left = 0; memset(chip->scratch, 0, new_samples * sizeof(chip->scratch[0])); break; } /* if there are leftovers, ramp back to 0 */ if (samples_left) { /* note: samples_left bit 16 is set if the voice was finished at the same time the function ended */ int base; int i, t; samples_left &= 0xffff; base = new_samples - samples_left; t = (base == 0) ? curr : chip->scratch[base - 1]; for (i = 0; i < samples_left; i++) { if (t < 0) t = -((-t * 15) >> 4); else if (t > 0) t = (t * 15) >> 4; chip->scratch[base + i] = t; } /* if we hit the end and IRQs are enabled, signal it */ if (base != 0) { voice->playing = 0; /* set update_irq_state_timer. IRQ is signaled on next CPU execution. */ //timer_set(chip->device->machine, attotime_zero, chip, 0, update_irq_state_cb[v]); voice->irq_schedule = 1; } } /* advance forward one sample */ prev = curr; curr = *curr_data++; /* then sample-rate convert with linear interpolation */ while (remaining > 0) { /* interpolate */ while (remaining > 0 && voice->output_pos < FRAC_ONE) { int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS; *ldest++ += interp_sample * lvol; *rdest++ += interp_sample * rvol; #if MAKE_WAVS_CH *(wavlog ++) = (signed short int)interp_sample; #endif voice->output_pos += voice->output_step; remaining--; } /* if we're over, grab the next samples */ if (voice->output_pos >= FRAC_ONE) { voice->output_pos -= FRAC_ONE; prev = curr; curr = *curr_data++; } } /* remember the last samples */ voice->last_sample = prev; voice->curr_sample = curr; } for (v = 0; v < samples; v++) { outputs[0][v] /= 256; outputs[1][v] /= 256; } for (v = 0; v < 8; v++) update_irq_state_timer_common(chip, v); #if MAKE_WAVS_CH for (v = 0; v < 8; v++) { fwrite(wavmem[v], 0x02, samples, hWavFile[v]); } #endif } /********************************************************************************************** DEVICE_START( ymz280b ) -- start emulation of the YMZ280B ***********************************************************************************************/ //static DEVICE_START( ymz280b ) int device_start_ymz280b(UINT8 ChipID, int clock) { static const ymz280b_interface defintrf = { 0 }; //const ymz280b_interface *intf = (device->static_config != NULL) ? (const ymz280b_interface *)device->static_config : &defintrf; const ymz280b_interface *intf = &defintrf; //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip; int chn; if (ChipID >= MAX_CHIPS) return 0; chip = &YMZ280BData[ChipID]; //chip->device = device; //devcb_resolve_read8(&chip->ext_ram_read, &intf->ext_read, device); //devcb_resolve_write8(&chip->ext_ram_write, &intf->ext_write, device); /* compute ADPCM tables */ compute_tables(); /* initialize the rest of the structure */ chip->master_clock = (double)clock / 384.0; chip->rate = chip->master_clock * 2.0; // disabled until the frequency calculation gets fixed /*if (((CHIP_SAMPLING_MODE & 0x01) && chip->rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) chip->rate = (double)CHIP_SAMPLE_RATE;*/ //chip->region_base = device->region; chip->region_size = 0x00; chip->region_base = NULL; chip->irq_callback = intf->irq_callback; /* create the stream */ //chip->stream = stream_create(device, 0, 2, INTERNAL_SAMPLE_RATE, chip, ymz280b_update); /* allocate memory */ //chip->scratch = auto_alloc_array(device->machine, INT16, MAX_SAMPLE_CHUNK); chip->scratch = malloc(MAX_SAMPLE_CHUNK * sizeof(INT16)); memset(chip->scratch, 0x00, MAX_SAMPLE_CHUNK * sizeof(INT16)); /* state save */ /*{ int j; state_save_register_device_item(device, 0, chip->current_register); state_save_register_device_item(device, 0, chip->status_register); state_save_register_device_item(device, 0, chip->irq_state); state_save_register_device_item(device, 0, chip->irq_mask); state_save_register_device_item(device, 0, chip->irq_enable); state_save_register_device_item(device, 0, chip->keyon_enable); state_save_register_device_item(device, 0, chip->rom_readback_addr); for (j = 0; j < 8; j++) { state_save_register_device_item(device, j, chip->voice[j].playing); state_save_register_device_item(device, j, chip->voice[j].keyon); state_save_register_device_item(device, j, chip->voice[j].looping); state_save_register_device_item(device, j, chip->voice[j].mode); state_save_register_device_item(device, j, chip->voice[j].fnum); state_save_register_device_item(device, j, chip->voice[j].level); state_save_register_device_item(device, j, chip->voice[j].pan); state_save_register_device_item(device, j, chip->voice[j].start); state_save_register_device_item(device, j, chip->voice[j].stop); state_save_register_device_item(device, j, chip->voice[j].loop_start); state_save_register_device_item(device, j, chip->voice[j].loop_end); state_save_register_device_item(device, j, chip->voice[j].position); state_save_register_device_item(device, j, chip->voice[j].signal); state_save_register_device_item(device, j, chip->voice[j].step); state_save_register_device_item(device, j, chip->voice[j].loop_signal); state_save_register_device_item(device, j, chip->voice[j].loop_step); state_save_register_device_item(device, j, chip->voice[j].loop_count); state_save_register_device_item(device, j, chip->voice[j].output_left); state_save_register_device_item(device, j, chip->voice[j].output_right); state_save_register_device_item(device, j, chip->voice[j].output_pos); state_save_register_device_item(device, j, chip->voice[j].last_sample); state_save_register_device_item(device, j, chip->voice[j].curr_sample); state_save_register_device_item(device, j, chip->voice[j].irq_schedule); } }*/ for (chn = 0; chn < 8; chn ++) chip->voice[chn].Muted = 0x00; //state_save_register_postload(device->machine, YMZ280B_state_save_update_step, chip); #if MAKE_WAVS chip->wavresample = wav_open("resamp.wav", INTERNAL_SAMPLE_RATE, 2); #endif #if MAKE_WAVS_CH hWavFile[0] = fopen("logwav0.raw", "wb"); hWavFile[1] = fopen("logwav1.raw", "wb"); hWavFile[2] = fopen("logwav2.raw", "wb"); hWavFile[3] = fopen("logwav3.raw", "wb"); hWavFile[4] = fopen("logwav4.raw", "wb"); hWavFile[5] = fopen("logwav5.raw", "wb"); hWavFile[6] = fopen("logwav6.raw", "wb"); hWavFile[7] = fopen("logwav7.raw", "wb"); { char v; for (v = 0; v < 8; v++) { wavmem[v] = (signed short int*)malloc(0x10 * 0x02); } } #endif return (int)INTERNAL_SAMPLE_RATE; } //static DEVICE_STOP( ymz280b ) void device_stop_ymz280b(UINT8 ChipID) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = &YMZ280BData[ChipID]; free(chip->region_base); chip->region_base = NULL; free(chip->scratch); #if MAKE_WAVS_CH { char v; for (v = 0; v < 8; v++) { free(wavmem[v]); fclose(hWavFile[v]); } } #endif return; } //static DEVICE_RESET( ymz280b ) void device_reset_ymz280b(UINT8 ChipID) { ymz280b_state *chip = &YMZ280BData[ChipID]; /*struct YMZ280BVoice *voice; unsigned char curvoc; chip->current_register = 0x00; chip->status_register = 0x00; chip->irq_state = 0x00; chip->irq_mask = 0x00; chip->irq_enable = 0x00; chip->keyon_enable = 0x00; chip->ext_mem_address = 0x000000; for (curvoc = 0; curvoc < 8; curvoc ++) { voice = &chip->voice[curvoc]; voice->playing = 0; voice->keyon = 0; voice->looping = 0; voice->mode = 0; voice->fnum = 0; voice->level = 0; voice->pan = 8; voice->start = 0x000000; voice->stop = 0x000000; voice->loop_start = 0x000000; voice->loop_end = 0x000000; voice->position = 0x000000; }*/ // new code from MAME 0.143u4 int i; /* initial clear registers */ for (i = 0xff; i >= 0; i--) { if (i == 0x83 || (i >= 88 && i <= 0xFD)) continue; // avoid too many debug messages chip->current_register = i; write_to_register(chip, 0); } chip->current_register = 0; chip->status_register = 0; /* clear other voice parameters */ for (i = 0; i < 8; i++) { struct YMZ280BVoice *voice = &chip->voice[i]; voice->curr_sample = 0; voice->last_sample = 0; voice->output_pos = FRAC_ONE; voice->playing = 0; } return; } /********************************************************************************************** write_to_register -- handle a write to the current register ***********************************************************************************************/ static void write_to_register(ymz280b_state *chip, int data) { struct YMZ280BVoice *voice; int i; //UINT8 mode_new; /* force an update */ //stream_update(chip->stream); /* lower registers follow a pattern */ if (chip->current_register < 0x80) { voice = &chip->voice[(chip->current_register >> 2) & 7]; switch (chip->current_register & 0xe3) { case 0x00: /* pitch low 8 bits */ voice->fnum = (voice->fnum & 0x100) | (data & 0xff); update_step(chip, voice); break; case 0x01: /* pitch upper 1 bit, loop, key on, mode */ voice->fnum = (voice->fnum & 0xff) | ((data & 0x01) << 8); voice->looping = (data & 0x10) >> 4; /*mode_new = (data & 0x60) >> 5; if (! DISABLE_YMZ_FIX) { // that fixes the scratch-bug if (voice->mode != mode_new) { // On-the-fly Mode-Change won't make sense, // so I'm doing: KeyOff + Mode Change -> Instant Stop. // (Deroon DeroDero uses this quite often) // This is done by setting KeyOn to 0. // Instant Stop/Restarting is done below. voice->keyon = 0; voice->irq_schedule = 0; } if (! mode_new) data &= 0x7F; } voice->mode = mode_new;*/ if ((data & 0x60) == 0) data &= 0x7f; /* ignore mode setting and set to same state as KON=0 */ else voice->mode = (data & 0x60) >> 5; if (!voice->keyon && (data & 0x80) && chip->keyon_enable) { voice->playing = 1; voice->position = voice->start; voice->signal = voice->loop_signal = 0; voice->step = voice->loop_step = 0x7f; voice->loop_count = 0; /* if update_irq_state_timer is set, cancel it. */ voice->irq_schedule = 0; } /*else if (voice->keyon && !(data & 0x80) && !voice->looping) { voice->playing = 0; // if update_irq_state_timer is set, cancel it. voice->irq_schedule = 0; } else if (! DISABLE_YMZ_FIX && ! voice->keyon && !(data & 0x80) && voice->playing) { // 2x KeyOff -> Instant Stop, too (see Deroon DeroDero: Round Start-Tune) voice->playing = 0; voice->irq_schedule = 0; }*/ // new code from MAME 0.143u4 else if (voice->keyon && !(data & 0x80)) { voice->playing = 0; // if update_irq_state_timer is set, cancel it. voice->irq_schedule = 0; } voice->keyon = (data & 0x80) >> 7; update_step(chip, voice); break; case 0x02: /* total level */ voice->level = data; update_volumes(voice); break; case 0x03: /* pan */ voice->pan = data & 0x0f; update_volumes(voice); break; case 0x20: /* start address high */ voice->start = (voice->start & (0x00ffff << 1)) | (data << 17); break; case 0x21: /* loop start address high */ voice->loop_start = (voice->loop_start & (0x00ffff << 1)) | (data << 17); break; case 0x22: /* loop end address high */ voice->loop_end = (voice->loop_end & (0x00ffff << 1)) | (data << 17); break; case 0x23: /* stop address high */ voice->stop = (voice->stop & (0x00ffff << 1)) | (data << 17); break; case 0x40: /* start address middle */ voice->start = (voice->start & (0xff00ff << 1)) | (data << 9); break; case 0x41: /* loop start address middle */ voice->loop_start = (voice->loop_start & (0xff00ff << 1)) | (data << 9); break; case 0x42: /* loop end address middle */ voice->loop_end = (voice->loop_end & (0xff00ff << 1)) | (data << 9); break; case 0x43: /* stop address middle */ voice->stop = (voice->stop & (0xff00ff << 1)) | (data << 9); break; case 0x60: /* start address low */ voice->start = (voice->start & (0xffff00 << 1)) | (data << 1); break; case 0x61: /* loop start address low */ voice->loop_start = (voice->loop_start & (0xffff00 << 1)) | (data << 1); break; case 0x62: /* loop end address low */ voice->loop_end = (voice->loop_end & (0xffff00 << 1)) | (data << 1); break; case 0x63: /* stop address low */ voice->stop = (voice->stop & (0xffff00 << 1)) | (data << 1); break; default: #ifdef _DEBUG logerror("YMZ280B: unknown register write %02X = %02X\n", chip->current_register, data); #endif break; } } /* upper registers are special */ else { switch (chip->current_register) { /* DSP related (not implemented yet) */ case 0x80: // d0-2: DSP Rch, d3: enable Rch (0: yes, 1: no), d4-6: DSP Lch, d7: enable Lch (0: yes, 1: no) case 0x81: // d0: enable control of $82 (0: yes, 1: no) case 0x82: // DSP data logerror("YMZ280B: DSP register write %02X = %02X\n", chip->current_register, data); break; case 0x84: /* ROM readback / RAM write (high) */ chip->ext_mem_address_hi = data << 16; break; case 0x85: /* ROM readback / RAM write (middle) */ chip->ext_mem_address_mid = data << 8; break; case 0x86: /* ROM readback / RAM write (low) -> update latch */ chip->ext_mem_address = chip->ext_mem_address_hi | chip->ext_mem_address_mid | data; if (chip->ext_mem_enable) chip->ext_readlatch = ymz280b_read_memory(chip->region_base, chip->region_size, chip->ext_mem_address); break; case 0x87: /* RAM write */ if (chip->ext_mem_enable) { /*if (!chip->ext_ram_write.isnull()) chip->ext_ram_write(chip->ext_mem_address, data); else logerror("YMZ280B attempted RAM write to %X\n", chip->ext_mem_address);*/ chip->ext_mem_address = (chip->ext_mem_address + 1) & 0xffffff; } break; case 0xfe: /* IRQ mask */ chip->irq_mask = data; update_irq_state(chip); break; case 0xff: /* IRQ enable, test, etc */ chip->ext_mem_enable = (data & 0x40) >> 6; chip->irq_enable = (data & 0x10) >> 4; update_irq_state(chip); if (chip->keyon_enable && !(data & 0x80)) { for (i = 0; i < 8; i++) { chip->voice[i].playing = 0; /* if update_irq_state_timer is set, cancel it. */ chip->voice[i].irq_schedule = 0; } } else if (!chip->keyon_enable && (data & 0x80)) { for (i = 0; i < 8; i++) { if (chip->voice[i].keyon && chip->voice[i].looping) chip->voice[i].playing = 1; } } chip->keyon_enable = (data & 0x80) >> 7; break; default: #ifdef _DEBUG logerror("YMZ280B: unknown register write %02X = %02X\n", chip->current_register, data); #endif break; } } } /********************************************************************************************** compute_status -- determine the status bits ***********************************************************************************************/ static int compute_status(ymz280b_state *chip) { UINT8 result; /* force an update */ //stream_update(chip->stream); result = chip->status_register; /* clear the IRQ state */ chip->status_register = 0; update_irq_state(chip); return result; } /********************************************************************************************** ymz280b_r/ymz280b_w -- handle external accesses ***********************************************************************************************/ //READ8_DEVICE_HANDLER( ymz280b_r ) UINT8 ymz280b_r(UINT8 ChipID, offs_t offset) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = &YMZ280BData[ChipID]; if ((offset & 1) == 0) { UINT8 ret; if (! chip->ext_mem_enable) return 0xff; /* read from external memory */ ret = chip->ext_readlatch; ret = ymz280b_read_memory(chip->region_base, chip->region_size, chip->ext_mem_address); chip->ext_mem_address = (chip->ext_mem_address + 1) & 0xffffff; return ret; } else { return compute_status(chip); } } //WRITE8_DEVICE_HANDLER( ymz280b_w ) void ymz280b_w(UINT8 ChipID, offs_t offset, UINT8 data) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = &YMZ280BData[ChipID]; if ((offset & 1) == 0) chip->current_register = data; else { /* force an update */ //chip->stream->update(); write_to_register(chip, data); } } void ymz280b_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ymz280b_state *chip = &YMZ280BData[ChipID]; if (chip->region_size != ROMSize) { chip->region_base = (UINT8*)realloc(chip->region_base, ROMSize); chip->region_size = ROMSize; memset(chip->region_base, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->region_base + DataStart, ROMData, DataLength); return; } void ymz280b_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) { ymz280b_state *chip = &YMZ280BData[ChipID]; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ymz280b ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ymz280b_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymz280b ); break; case DEVINFO_FCT_STOP: // Nothing break; case DEVINFO_FCT_RESET: info->start = DEVICE_RESET_NAME( ymz280b ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YMZ280B"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha Wavetable"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ ================================================ FILE: VGMPlay/chips/ymz280b.h ================================================ /********************************************************************************************** * * Yamaha YMZ280B driver * by Aaron Giles * **********************************************************************************************/ #pragma once //#include "devcb.h" typedef struct _ymz280b_interface ymz280b_interface; struct _ymz280b_interface { //void (*irq_callback)(const device_config *device, int state); /* irq callback */ void (*irq_callback)(int state); /* irq callback */ //devcb_read8 ext_read; /* external RAM read */ //devcb_write8 ext_write; /* external RAM write */ }; /*READ8_DEVICE_HANDLER ( ymz280b_r ); WRITE8_DEVICE_HANDLER( ymz280b_w ); DEVICE_GET_INFO( ymz280b ); #define SOUND_YMZ280B DEVICE_GET_INFO_NAME( ymz280b )*/ void ymz280b_update(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ymz280b(UINT8 ChipID, int clock); void device_stop_ymz280b(UINT8 ChipID); void device_reset_ymz280b(UINT8 ChipID); UINT8 ymz280b_r(UINT8 ChipID, offs_t offset); void ymz280b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ymz280b_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ymz280b_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); ================================================ FILE: VGMPlay/dbus.c ================================================ /* DBus/MPRIS for VGMPlay. By Tasos Sahanidis required packages: libdbus-1-dev compiling: CFLAGS=$(pkg-config --cflags dbus-1) LDFLAGS=$(pkg-config --libs dbus-1) They weren't lying when they said that using libdbus directly signs you up for some pain... */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "chips/mamedef.h" // for UINT8 #include "mmkeys.h" #include #include "dbus.h" #include "stdbool.h" #include "VGMPlay.h" // For VGMFile.h and CHIP_COUNT #include #include #include #include #include #include "VGMPlay_Intf.h" // DBus MPRIS Constants #define DBUS_MPRIS_PATH "/org/mpris/MediaPlayer2" #define DBUS_MPRIS_MEDIAPLAYER2 "org.mpris.MediaPlayer2" #define DBUS_MPRIS_PLAYER "org.mpris.MediaPlayer2.Player" #define DBUS_MPRIS_VGMPLAY "org.mpris.MediaPlayer2.vgmplay" #define DBUS_PROPERTIES "org.freedesktop.DBus.Properties" //#define DBUS_DEBUG #ifdef DBUS_DEBUG #define ART_EXISTS_PRINTF(x) fprintf(stderr, "\nTrying %s\n", (x)); #else #define ART_EXISTS_PRINTF(x) #endif #define ART_EXISTS(path) ART_EXISTS_PRINTF(path) \ if(FileExists((path))) { \ free(basepath); \ return; \ } static mmkey_cbfunc evtCallback = NULL; extern INT32 VGMSmplPlayed; extern UINT32 SampleRate; extern GD3_TAG VGMTag; extern wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag); extern INT32 SampleVGM2Playback(INT32 SampleVal); extern VGM_HEADER VGMHead; // Playlist status extern UINT32 PLFileCount; extern UINT32 CurPLFile; extern UINT8 PLMode; // Playlist Vars extern char PLFileName[]; // Playback Status extern UINT8 PlayingMode; extern bool PausePlay; DBusConnection* dbus_connection = NULL; // Seek Function extern void SeekVGM(bool Relative, INT32 PlayBkSamples); // Filename extern char VgmFileName[]; extern char* GetLastDirSeparator(const char* FilePath); // Needed for loop detection static UINT32 OldLoopCount; extern UINT32 VGMCurLoop; // MPRIS Metadata Struct typedef struct DBusMetadata_ { const void* title; const char* dbusType; const void* content; int contentType; size_t childLen; } DBusMetadata; // Cached art path static char* cached_artpath = NULL; // Effectively the allocation size of cached_artpath, including the terminator static size_t pathmax = 0; // Misc Helper Functions static inline void invalidateArtCache() { *cached_artpath = '\0'; } static char* wcharToUTF8(wchar_t* GD3) { size_t len = wcstombs(NULL, GD3, 0) + 1; char* out = malloc(len); wcstombs(out, GD3, len); return out; } static char* urlencode(const char* str) { size_t len = strlen(str); // Don't try to encode blank strings if(!len) return calloc(1, sizeof(char)); // strlen("file://") + max str size + \0 char* newstring = malloc(7 + len * 3 + 1); char* loop = newstring; loop += sprintf(loop, "%s", "file://"); for(size_t i = 0; i < len; i++) { // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm unsigned char c = (unsigned char)str[i]; switch(c) { case 32: case 36: case 38: case 43: case 44: case 58: case 59: case 61: case 63: case 64: loop += sprintf(loop, "%%%02X", c); break; default: if(c > 127) loop += sprintf(loop, "%%%02X", c); else loop += sprintf(loop, "%c", str[i]); } } return newstring; } // Return current position in μs static inline int64_t ReturnPosMsec(UINT32 SamplePos, UINT32 SmplRate) { return (int64_t)((SamplePos / (double)SmplRate)*1000000.0); } // Return current position in samples static inline INT32 ReturnSamplePos(int64_t UsecPos, UINT32 SmplRate) { return (UsecPos / 1000000.0)*(double)SmplRate; } static inline int FileExists(char* file) { return access(file, F_OK) + 1; } // DBus Helper Functions static void DBusEmptyMethodResponse(DBusConnection* connection, DBusMessage* request) { DBusMessage* reply = dbus_message_new_method_return(request); dbus_message_append_args(reply, DBUS_TYPE_INVALID); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } static void DBusReplyToIntrospect(DBusConnection* connection, DBusMessage* request) { const char* introspection_data = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " " " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" ; DBusMessage* reply = dbus_message_new_method_return(request); dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, DBUS_TYPE_INVALID); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } static void DBusReplyWithVariant(DBusMessageIter* args, int type, char* type_as_string, const void* response) { DBusMessageIter subargs; dbus_message_iter_open_container(args, DBUS_TYPE_VARIANT, type_as_string, &subargs); dbus_message_iter_append_basic(&subargs, type, response); dbus_message_iter_close_container(args, &subargs); } void DBusAppendCanGoNext(DBusMessageIter* args) { dbus_bool_t response = FALSE; if(PLMode == 0x01) if(CurPLFile < PLFileCount - 1) response = TRUE; DBusReplyWithVariant(args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } void DBusAppendCanGoPrevious(DBusMessageIter* args) { dbus_bool_t response = FALSE; if(PLMode == 0x01) if(CurPLFile > 0x00) response = TRUE; DBusReplyWithVariant(args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } static void DBusSendMetadataArray(DBusMessageIter* dict_root, DBusMetadata meta[], size_t dbus_meta_array_len) { DBusMessageIter root_variant, dict, dict_entry, variant; dbus_message_iter_open_container(dict_root, DBUS_TYPE_VARIANT, "a{sv}", &root_variant); // Open Root Container dbus_message_iter_open_container(&root_variant, DBUS_TYPE_ARRAY, "{sv}", &dict); for(size_t i = 0; i < dbus_meta_array_len; i++) { // Ignore empty strings if(meta[i].contentType == DBUS_TYPE_STRING) if(!strlen(*(char**)meta[i].content)) continue; dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &meta[i].title); // Field Value dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, meta[i].dbusType, &variant); if(meta[i].contentType == DBUS_TYPE_ARRAY) { DBusMessageIter array_root; dbus_message_iter_open_container(&variant, meta[i].contentType, meta[i].dbusType, &array_root); for(size_t len = 0; len < meta[i].childLen; len++) { const DBusMetadata* content = meta[i].content; dbus_message_iter_append_basic(&array_root, content[len].contentType, content[len].content); } dbus_message_iter_close_container(&variant, &array_root); } else dbus_message_iter_append_basic(&variant, meta[i].contentType, meta[i].content); dbus_message_iter_close_container(&dict_entry, &variant); dbus_message_iter_close_container(&dict, &dict_entry); } // Close Root Container dbus_message_iter_close_container(&root_variant, &dict); dbus_message_iter_close_container(dict_root, &root_variant); } static inline char* getAbsBasePath() { // Pick the appropriate pointer const char* fileptr = VgmFileName; if(PLMode == 0x01) fileptr = PLFileName; char* basepath = realpath(fileptr, NULL); if(basepath) { char* sep = GetLastDirSeparator(basepath); // It's okay to + 1 because sep points to a character before the terminator if(sep) sep[1] = '\0'; #ifdef DBUS_DEBUG fprintf(stderr, "\nBase Path %s\n", basepath); #endif } return basepath; } static inline void getArtPath(const char* utf8album, char* artpath, const size_t artpath_size) { char* basepath = getAbsBasePath(); if(basepath == NULL || basepath[0] == '\0') { free(basepath); return; } // Now that we have the base path, we start looking for art // If we are reading a playlist, append everything after the separator to the path and replace its m3u extension with png if(PLMode == 0x01) { // Using the GNU version of basename which doesn't modify the argument char* plname = basename(PLFileName); char* plname_ext = strrchr(plname, '.'); // Find out how many characters we need to print until the file extension // We use int because that's what the precision specifier accepts // The filename is really not going to be more than INT_MAX characters int plname_len = (plname_ext == NULL ? (int)strlen(plname) : (int)(plname_ext - plname)); snprintf(artpath, artpath_size, "%s%.*s.png", basepath, plname_len, plname); ART_EXISTS(artpath); } // If we get here, we're probably in single track mode, or the playlist is named differently // check base path + album + .png snprintf(artpath, artpath_size, "%s%s.png", basepath, utf8album); ART_EXISTS(artpath); // As a last resort, pick the first image glob can find in the base path // Append the case insensitive extension to the base path snprintf(artpath, artpath_size, "%s*.[pP][nN][gG]", basepath); #ifdef DBUS_DEBUG fprintf(stderr, "Using glob %s\n", artpath); #endif glob_t result; if(glob(artpath, GLOB_NOSORT, NULL, &result) == 0) { if(result.gl_pathc > 0) { snprintf(artpath, artpath_size, "%s", result.gl_pathv[0]); globfree(&result); ART_EXISTS(artpath); } } globfree(&result); // There's nothing else we can do. Return an empty string *artpath = '\0'; free(basepath); } static void DBusSendMetadata(DBusMessageIter* dict_root) { // Send an empty array in a variant if nothing is playing if(PlayingMode == 0xFF) { DBusSendMetadataArray(dict_root, NULL, 0); return; } // Prepare metadata // Album wchar_t* album = GetTagStrEJ(VGMTag.strGameNameE, VGMTag.strGameNameJ); char* utf8album = wcharToUTF8(album); // Title wchar_t* title = GetTagStrEJ(VGMTag.strTrackNameE, VGMTag.strTrackNameJ); char* utf8title = wcharToUTF8(title); // Length int64_t len64 = 0; INT32 VGMPbSmplCount = SampleVGM2Playback(VGMHead.lngTotalSamples); len64 = ReturnPosMsec(VGMPbSmplCount, SampleRate); // Artist wchar_t* artist = GetTagStrEJ(VGMTag.strAuthorNameE, VGMTag.strAuthorNameJ); char* utf8artist = wcharToUTF8(artist); // Track Number in playlist int32_t tracknum = 0; if(PLMode == 0x01) tracknum = (int32_t)(CurPLFile + 0x01); // Try to get the cover art url if(cached_artpath[0] == '\0') getArtPath(utf8album, cached_artpath, pathmax); #ifdef DBUS_DEBUG fprintf(stderr, "\nFinal art path %s\n", artpath); #endif // URL encode the path to the png char* arturlescaped = urlencode(cached_artpath); // Game release date wchar_t* release = VGMTag.strReleaseDate; char* utf8release = wcharToUTF8(release); // VGM File Creator wchar_t* creator = VGMTag.strCreator; char* utf8creator = wcharToUTF8(creator); // Notes wchar_t* notes = VGMTag.strNotes; char* utf8notes = wcharToUTF8(notes); // System wchar_t* system = GetTagStrEJ(VGMTag.strSystemNameE, VGMTag.strSystemNameJ); char* utf8system = wcharToUTF8(system); // VGM File version uint32_t version = VGMHead.lngVersion; // Loop point int64_t loop = ReturnPosMsec(VGMHead.lngLoopSamples, SampleRate); if(!strlen(utf8artist)) { utf8artist = realloc(utf8artist, strlen(utf8album) + 1); strcpy(utf8artist, utf8album); } // Encapsulate some data in DBusMetadata Arrays // Artist Array DBusMetadata dbusartist[] = { { "", DBUS_TYPE_STRING_AS_STRING, &utf8artist, DBUS_TYPE_STRING, 0 }, }; // Genre Array const char* genre = "Video Game Music"; DBusMetadata dbusgenre[] = { { "", DBUS_TYPE_STRING_AS_STRING, &genre, DBUS_TYPE_STRING, 0 }, }; DBusMetadata chips[CHIP_COUNT] = { 0 }; size_t chipslen = 0; // Generate chips array for(UINT8 CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { UINT8 ChpType; UINT32 ChpClk = GetChipClock(&VGMHead, CurChip, &ChpType); if(ChpClk && GetChipClock(&VGMHead, 0x80 | CurChip, NULL)) ChpClk |= 0x40000000; if(ChpClk) { if(CurChip == 0x00 && (ChpClk & 0x80000000)) ChpClk &= ~0x40000000; if(ChpClk & 0x80000000) { ChpClk &= ~0x80000000; CurChip |= 0x80; } const char* chip = GetAccurateChipName(CurChip, ChpType); char** charptr = malloc(sizeof(char*)); chips[chipslen].content = charptr; *charptr = strdup(chip); // Set the type chips[chipslen].dbusType = DBUS_TYPE_STRING_AS_STRING; chips[chipslen].contentType = DBUS_TYPE_STRING; // Duplicate the chip if necessasry if(ChpClk & 0x40000000) { chipslen++; chips[chipslen].content = chips[chipslen - 1].content; chips[chipslen].dbusType = DBUS_TYPE_STRING_AS_STRING; chips[chipslen].contentType = DBUS_TYPE_STRING; } chipslen++; } } // URL encoded Filename char* abspath = realpath(VgmFileName, NULL); if(abspath == NULL) abspath = calloc(1, sizeof(char)); char* url = urlencode(abspath); free(abspath); // Stubs const char* trackid = "/org/mpris/MediaPlayer2/CurrentTrack"; const char* lastused = "2018-01-04T12:21:32Z"; int32_t discnum = 1; int32_t usecnt = 0; double userrating = 0; DBusMetadata meta[] = { { "mpris:trackid", DBUS_TYPE_STRING_AS_STRING, &trackid, DBUS_TYPE_STRING, 0 }, { "xesam:url", DBUS_TYPE_STRING_AS_STRING, &url, DBUS_TYPE_STRING, 0 }, { "mpris:artUrl", DBUS_TYPE_STRING_AS_STRING, &arturlescaped, DBUS_TYPE_STRING, 0 }, { "xesam:lastused", DBUS_TYPE_STRING_AS_STRING, &lastused, DBUS_TYPE_STRING, 0 }, { "xesam:genre", "as", &dbusgenre, DBUS_TYPE_ARRAY, 1 }, { "xesam:album", DBUS_TYPE_STRING_AS_STRING, &utf8album, DBUS_TYPE_STRING, 0 }, { "xesam:title", DBUS_TYPE_STRING_AS_STRING, &utf8title, DBUS_TYPE_STRING, 0 }, { "mpris:length", DBUS_TYPE_INT64_AS_STRING, &len64, DBUS_TYPE_INT64, 0 }, { "xesam:artist", "as", &dbusartist, DBUS_TYPE_ARRAY, 1 }, { "xesam:composer", "as", &dbusartist, DBUS_TYPE_ARRAY, 1 }, { "xesam:trackNumber", DBUS_TYPE_INT32_AS_STRING, &tracknum, DBUS_TYPE_INT32, 1 }, { "xesam:discNumber", DBUS_TYPE_INT32_AS_STRING, &discnum, DBUS_TYPE_INT32, 1 }, { "xesam:useCount", DBUS_TYPE_INT32_AS_STRING, &usecnt, DBUS_TYPE_INT32, 1 }, { "xesam:userRating", DBUS_TYPE_DOUBLE_AS_STRING, &userrating, DBUS_TYPE_DOUBLE, 1 }, // Extra non-xesam/mpris entries { "vgm:release", DBUS_TYPE_STRING_AS_STRING, &utf8release, DBUS_TYPE_STRING, 0 }, { "vgm:creator", DBUS_TYPE_STRING_AS_STRING, &utf8creator, DBUS_TYPE_STRING, 0 }, { "vgm:notes", DBUS_TYPE_STRING_AS_STRING, &utf8notes, DBUS_TYPE_STRING, 0 }, { "vgm:system", DBUS_TYPE_STRING_AS_STRING, &utf8system, DBUS_TYPE_STRING, 0 }, { "vgm:version", DBUS_TYPE_UINT32_AS_STRING, &version, DBUS_TYPE_UINT32, 0 }, { "vgm:loop", DBUS_TYPE_INT64_AS_STRING, &loop, DBUS_TYPE_INT64, 0 }, { "vgm:chips", "as", &chips, DBUS_TYPE_ARRAY, chipslen }, }; DBusSendMetadataArray(dict_root, meta, sizeof(meta)/sizeof(*meta)); // Free everything free(arturlescaped); free(url); free(utf8title); free(utf8album); free(utf8artist); free(utf8release); free(utf8creator); free(utf8notes); free(utf8system); for(size_t i = 0; i < chipslen; i++) { // If the next pointer is the same as the current one, don't free it. // We also discard const here since we know we manually allocated these above. char** ptr = (char**)chips[i].content; if(chips[i].content == chips[i+1].content) continue; char* ch = *ptr; free(ptr); free(ch); } } static void DBusSendPlaybackStatus(DBusMessageIter* args) { const char* response; if(PlayingMode == 0xFF) response = "Stopped"; else if(PausePlay) response = "Paused"; else response = "Playing"; DBusReplyWithVariant(args, DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &response); } void DBus_EmitSignal_Internal(DBusConnection* connection, UINT8 type) { #ifdef DBUS_DEBUG fprintf(stderr, "Emitting signal type 0x%x\n", type); #endif if(connection == NULL) return; // Make sure we're connected to DBus // Otherwise discard the event if(!dbus_connection_get_is_connected(connection)) return; DBusMessage* msg; DBusMessageIter args; if(type & SIGNAL_SEEK) { msg = dbus_message_new_signal(DBUS_MPRIS_PATH, DBUS_MPRIS_PLAYER, "Seeked"); dbus_message_iter_init_append(msg, &args); int64_t response = ReturnPosMsec(VGMSmplPlayed, SampleRate); dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64, &response); dbus_connection_send(connection, msg, NULL); dbus_message_unref(msg); // Despite Seeked() being a different signal // we need to send the changed position property too // so we shouldn't return just yet. } msg = dbus_message_new_signal(DBUS_MPRIS_PATH, DBUS_PROPERTIES, "PropertiesChanged"); dbus_message_iter_init_append(msg, &args); // The interface in which the properties were changed must be sent first // Thankfully the only properties changing are in the same interface const char* player = DBUS_MPRIS_PLAYER; dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &player); // Wrap everything inside an a{sv} DBusMessageIter dict, dict_entry; dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict); if(type & SIGNAL_METADATA) { // It is possible for the art to change if the playlist contains tracks from multiple games // since art can be found by the "Game Name".png field // Invalidate it on track change, as it will be populated later on demand invalidateArtCache(); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); const char* title = "Metadata"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusSendMetadata(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); } if(type & SIGNAL_CONTROLS) { dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); const char* title = "CanGoPrevious"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusAppendCanGoPrevious(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); title = "CanGoNext"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusAppendCanGoNext(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); } if(type & SIGNAL_PLAYSTATUS) { dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); const char* playing = "PlaybackStatus"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &playing); DBusSendPlaybackStatus(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); } if((type & SIGNAL_SEEK) || (type & SIGNAL_PLAYSTATUS)) { dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); const char* playing = "Position"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &playing); int64_t response = ReturnPosMsec(VGMSmplPlayed, SampleRate); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_INT64, DBUS_TYPE_INT64_AS_STRING, &response); dbus_message_iter_close_container(&dict, &dict_entry); } if((type & SIGNAL_VOLUME)) { dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); const char* playing = "Volume"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &playing); const double response = 1.0; DBusReplyWithVariant(&dict_entry, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &response); dbus_message_iter_close_container(&dict, &dict_entry); } dbus_message_iter_close_container(&args, &dict); // Send a blank array _with signature "s"_. dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "s", &dict); dbus_message_iter_close_container(&args, &dict); dbus_connection_send(connection, msg, NULL); dbus_message_unref(msg); } void DBus_EmitSignal(UINT8 type) { DBus_EmitSignal_Internal(dbus_connection, type); } static void DBusSendMimeTypes(DBusMessageIter* args) { DBusMessageIter variant, subargs; dbus_message_iter_open_container(args, DBUS_TYPE_VARIANT, "as", &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subargs); char* response[] = {"audio/x-vgm", "audio/x-vgz"}; size_t i_len = sizeof(response) / sizeof(*response); for(size_t i = 0; i < i_len; ++i) { dbus_message_iter_append_basic(&subargs, DBUS_TYPE_STRING, &response[i]); } dbus_message_iter_close_container(&variant, &subargs); dbus_message_iter_close_container(args, &variant); } static void DBusSendUriSchemes(DBusMessageIter* args) { DBusMessageIter variant, subargs; dbus_message_iter_open_container(args, DBUS_TYPE_VARIANT, "as", &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subargs); char* response[] = {"file"}; size_t i_len = sizeof(response) / sizeof(*response); for(size_t i = 0; i < i_len; ++i) { dbus_message_iter_append_basic(&subargs, DBUS_TYPE_STRING, &response[i]); } dbus_message_iter_close_container(&variant, &subargs); dbus_message_iter_close_container(args, &variant); } static void DBusSendEmptyMethodResponse(DBusConnection* connection, DBusMessage* message) { DBusMessage* reply; DBusMessageIter args; reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &args); dbus_connection_send(connection, reply, NULL); } static DBusHandlerResult DBusHandler(DBusConnection* connection, DBusMessage* message, void* user_data) { #ifdef DBUS_DEBUG const char* interface_name = dbus_message_get_interface(message); const char* member_name = dbus_message_get_member(message); const char* path_name = dbus_message_get_path(message); const char* sender = dbus_message_get_sender(message); fprintf(stderr, "Interface: %s; Member: %s; Path: %s; Sender: %s;\n", interface_name, member_name, path_name, sender); #endif if(!dbus_message_has_path(message, DBUS_MPRIS_PATH)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // Respond to Introspect if(dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { DBusReplyToIntrospect(connection, message); return DBUS_HANDLER_RESULT_HANDLED; } else if(dbus_message_is_method_call(message, DBUS_MPRIS_MEDIAPLAYER2, "Raise")) { printf("\a"); fflush(stdout); DBusSendEmptyMethodResponse(connection, message); return DBUS_HANDLER_RESULT_HANDLED; } // Respond to Get else if(dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get")) { char* method_interface_arg = NULL; char* method_property_arg = NULL; DBusMessage* reply; if(!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &method_interface_arg, DBUS_TYPE_STRING, &method_property_arg, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; //printf("Interface name: %s\n", method_interface_arg); //init reply reply = dbus_message_new_method_return(message); //printf("Property name: %s\n", method_property_arg); // Global Iterator DBusMessageIter args; dbus_message_iter_init_append(reply, &args); if(!strcmp(method_interface_arg, DBUS_MPRIS_MEDIAPLAYER2)) { if(!strcmp(method_property_arg, "SupportedMimeTypes")) { DBusSendMimeTypes(&args); } else if(!strcmp(method_property_arg, "SupportedUriSchemes")) { DBusSendUriSchemes(&args); } else if(!strcmp(method_property_arg, "CanQuit")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "CanRaise")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "HasTrackList")) { const dbus_bool_t response = FALSE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "DesktopEntry")) { const char* response = "vgmplay"; DBusReplyWithVariant(&args, DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &response); } else if(!strcmp(method_property_arg, "Identity")) { const char* response = "VGMPlay"; DBusReplyWithVariant(&args, DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &response); } else dbus_message_append_args(reply, DBUS_TYPE_INVALID); } else if(!strcmp(method_interface_arg, DBUS_MPRIS_PLAYER)) { if(!strcmp(method_property_arg, "CanPlay")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "CanPause")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "CanGoNext")) { DBusAppendCanGoNext(&args); } else if(!strcmp(method_property_arg, "CanGoPrevious")) { DBusAppendCanGoPrevious(&args); } else if(!strcmp(method_property_arg, "CanSeek")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "PlaybackStatus")) { DBusSendPlaybackStatus(&args); } else if(!strcmp(method_property_arg, "Position")) { int64_t response = ReturnPosMsec(VGMSmplPlayed, SampleRate); DBusReplyWithVariant(&args, DBUS_TYPE_INT64, DBUS_TYPE_INT64_AS_STRING, &response); } //Dummy volume else if(!strcmp(method_property_arg, "Volume") || !strcmp(method_property_arg, "MaximumRate") || !strcmp(method_property_arg, "MinimumRate") || !strcmp(method_property_arg, "Rate")) { const double response = 1.0; DBusReplyWithVariant(&args, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &response); } else if(!strcmp(method_property_arg, "CanControl")) { const dbus_bool_t response = TRUE; DBusReplyWithVariant(&args, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &response); } else if(!strcmp(method_property_arg, "Metadata")) { DBusSendMetadata(&args); } else dbus_message_append_args(reply, DBUS_TYPE_INVALID); } else { #ifdef DBUS_DEBUG fprintf(stderr, "Unimplemented interface %s passed to Get()\n", method_interface_arg); #endif dbus_message_unref(reply); reply = dbus_message_new_error(message, "org.freedesktop.DBus.Error.InvalidArgs", "No such interface"); } dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } // Respond to GetAll else if(dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll")) { char* method_interface_arg = NULL; DBusMessage* reply; if(!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &method_interface_arg, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; //init reply reply = dbus_message_new_method_return(message); //printf("Property name: %s\n", property_name); // Global Iterator DBusMessageIter args; dbus_message_iter_init_append(reply, &args); const dbus_bool_t dbustrue = TRUE; const dbus_bool_t dbusfalse = FALSE; const char* title; const char* strresponse; if(!strcmp(method_interface_arg, "org.mpris.MediaPlayer2")) { // a{sv} DBusMessageIter dict, dict_entry; dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict); // Open Dict dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "SupportedMimeTypes"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusSendMimeTypes(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "SupportedUriSchemes"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusSendUriSchemes(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanQuit"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanRaise"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "HasTrackList"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbusfalse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "DesktopEntry"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); strresponse = "vgmplay"; DBusReplyWithVariant(&dict_entry, DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &strresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "Identity"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); strresponse = "VGMPlay"; DBusReplyWithVariant(&dict_entry, DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &strresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_close_container(&args, &dict); } else if(!strcmp(method_interface_arg, DBUS_MPRIS_PLAYER)) { const double doubleresponse = 1.0; // a{sv} DBusMessageIter dict, dict_entry; dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanControl"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanGoNext"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusAppendCanGoNext(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanGoPrevious"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusAppendCanGoPrevious(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanPause"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanPlay"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "CanSeek"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &dbustrue); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "Metadata"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusSendMetadata(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "MaximumRate"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &doubleresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "MinimumRate"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &doubleresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "Rate"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &doubleresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "Volume"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_DOUBLE, DBUS_TYPE_DOUBLE_AS_STRING, &doubleresponse); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "Position"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); int64_t position = ReturnPosMsec(VGMSmplPlayed, SampleRate); DBusReplyWithVariant(&dict_entry, DBUS_TYPE_INT64, DBUS_TYPE_INT64_AS_STRING, &position); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry); // Field Title title = "PlaybackStatus"; dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &title); DBusSendPlaybackStatus(&dict_entry); dbus_message_iter_close_container(&dict, &dict_entry); dbus_message_iter_close_container(&args, &dict); } else { #ifdef DBUS_DEBUG fprintf(stderr, "Unimplemented interface %s passed to GetAll\n", method_interface_arg); #endif dbus_message_unref(reply); reply = dbus_message_new_error(message, "org.freedesktop.DBus.Error.InvalidArgs", "No such interface"); } dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to Seek else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Seek")) { int64_t offset = 0; if(!dbus_message_get_args(message, NULL, DBUS_TYPE_INT64, &offset, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; #ifdef DBUS_DEBUG fprintf(stderr, "Seek called with %"PRId64"\n", offset); #endif INT32 TargetSeekPos = ReturnSamplePos(offset, SampleRate); SeekVGM(true, TargetSeekPos); DBusEmptyMethodResponse(connection, message); // Emit seeked signal DBus_EmitSignal_Internal(connection, SIGNAL_SEEK); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to PlayPause else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "PlayPause")) { DBusEmptyMethodResponse(connection, message); evtCallback(MMKEY_PLAY); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to Play else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Play")) { DBusEmptyMethodResponse(connection, message); if(PausePlay) evtCallback(MMKEY_PLAY); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to Pause else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Pause")) { DBusEmptyMethodResponse(connection, message); if(!PausePlay) evtCallback(MMKEY_PLAY); return DBUS_HANDLER_RESULT_HANDLED; } // Stop is currently a stub else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Stop")) { DBusEmptyMethodResponse(connection, message); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to Previous else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Previous")) { DBusEmptyMethodResponse(connection, message); evtCallback(MMKEY_PREV); return DBUS_HANDLER_RESULT_HANDLED; } //Respond to Next else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "Next")) { DBusEmptyMethodResponse(connection, message); evtCallback(MMKEY_NEXT); return DBUS_HANDLER_RESULT_HANDLED; } else if(dbus_message_is_method_call(message, DBUS_MPRIS_PLAYER, "SetPosition")) { int64_t pos; const char* path; if(!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INT64, &pos, DBUS_TYPE_INVALID)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; INT32 seek_pos = ReturnSamplePos(pos, SampleRate); SeekVGM(false, seek_pos); DBusEmptyMethodResponse(connection, message); DBus_EmitSignal_Internal(connection, SIGNAL_SEEK); return DBUS_HANDLER_RESULT_HANDLED; } else if(dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set")) { // Dummy Set to send a signal to revert Volume change attempts DBus_EmitSignal_Internal(connection, SIGNAL_VOLUME); return DBUS_HANDLER_RESULT_HANDLED; } else { #ifdef DBUS_DEBUG fprintf(stderr, "Method %s for interface %s not implemented", member_name, interface_name); #endif return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } } UINT8 MultimediaKeyHook_Init(void) { // Allocate memory for the art path cache long pathmax_s = pathconf("/", _PC_PATH_MAX); if (pathmax_s <= 0) pathmax = 4096; else pathmax = pathmax_s + 1; cached_artpath = malloc(pathmax); *cached_artpath = '\0'; dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, NULL); if(!dbus_connection) return 0x00; // If we're not the owners, don't bother with anything else if(dbus_bus_request_name(dbus_connection, DBUS_MPRIS_VGMPLAY, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { dbus_connection_unref(dbus_connection); dbus_connection = NULL; return 0x00; } DBusObjectPathVTable vtable = { .message_function = DBusHandler, .unregister_function = NULL, }; dbus_connection_try_register_object_path(dbus_connection, DBUS_MPRIS_PATH, &vtable, NULL, NULL); return 0x00; } void MultimediaKeyHook_Deinit(void) { if(dbus_connection != NULL) dbus_connection_unref(dbus_connection); free(cached_artpath); cached_artpath = NULL; } void MultimediaKeyHook_SetCallback(mmkey_cbfunc callbackFunc) { evtCallback = callbackFunc; } void DBus_ReadWriteDispatch(void) { if(dbus_connection == NULL) return; // Detect loops and send the seeked signal when appropriate if(OldLoopCount != VGMCurLoop) { OldLoopCount = VGMCurLoop; DBus_EmitSignal_Internal(dbus_connection, SIGNAL_SEEK); } // Wait at most for 1ms dbus_connection_read_write_dispatch(dbus_connection, 1); } ================================================ FILE: VGMPlay/dbus.h ================================================ #ifndef __DBUS_H__ #define __DBUS_H__ // Defines for the DBUS Signal handler #define SIGNAL_METADATA 0x01 // Metadata changed #define SIGNAL_PLAYSTATUS 0x02 // Playback Status Changed #define SIGNAL_SEEKSTATUS 0x04 // Seek Status Changed #define SIGNAL_SEEK 0x08 // Seeked #define SIGNAL_CONTROLS 0x10 // Playback controls need to be updated (CanGoNext/Previous) #define SIGNAL_VOLUME 0x20 // Volume needs to be updated #define SIGNAL_ALL 0xFF // All Signals #include "chips/mamedef.h" void DBus_ReadWriteDispatch(void); void DBus_EmitSignal(UINT8 type); #endif // __DBUS_H__ ================================================ FILE: VGMPlay/dbus_stub.c ================================================ #include "chips/mamedef.h" void DBus_ReadWriteDispatch(void) { } void DBus_EmitSignal(UINT8 type) { } ================================================ FILE: VGMPlay/licenses/GPL.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ================================================ FILE: VGMPlay/licenses/List.txt ================================================ MAME - mame_license.txt DosBox - GPL.txt openMSX - GPL.txt Gens/GS - GPL.txt NSFPlay - [Google Code lists Apache License 2.0] Ootake - ? Gens - Cardware MEKA - ? zlib - see zlib.h in_wsr - ? vbjin/mednafen - GNU GPLv2 EMU2149/EMU2413 - MIT License ================================================ FILE: VGMPlay/licenses/mame_license.txt ================================================ Unless otherwise explicitly stated, all code in MAME is released under the following license: Copyright Nicola Salmoria and the MAME team All rights reserved. Redistribution and use of this code or any derivative works are permitted provided that the following conditions are met: * Redistributions may not be sold, nor may they be used in a commercial product or activity. * Redistributions that are modified from the original source must include the complete source code, including the source code for all components used by a binary built from the modified sources. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. * Redistributions must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: VGMPlay/mmkeys.h ================================================ #ifndef __MMKEYS_H__ #define __MMKEYS_H__ typedef void (*mmkey_cbfunc)(UINT8 event); #define MMKEY_PLAY 0x01 #define MMKEY_PREV 0x10 #define MMKEY_NEXT 0x11 UINT8 MultimediaKeyHook_Init(void); void MultimediaKeyHook_Deinit(void); void MultimediaKeyHook_SetCallback(mmkey_cbfunc callbackFunc); #endif // __MMKEYS_H__ ================================================ FILE: VGMPlay/mmkeys_Win.c ================================================ #include #include #include "chips/mamedef.h" #include "mmkeys.h" #ifndef VK_MEDIA_NEXT_TRACK // from WinUser.h #define VK_MEDIA_NEXT_TRACK 0xB0 #define VK_MEDIA_PREV_TRACK 0xB1 #define VK_MEDIA_STOP 0xB2 #define VK_MEDIA_PLAY_PAUSE 0xB3 #endif static DWORD idThread = 0; static HANDLE hThread = NULL; static HANDLE hEvent = NULL; static mmkey_cbfunc evtCallback = NULL; static DWORD WINAPI KeyMessageThread(void* args) { MSG msg; BOOL retValB; // enforce creation of message queue PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); retValB = RegisterHotKey(NULL, MMKEY_PLAY, 0, VK_MEDIA_PLAY_PAUSE); retValB = RegisterHotKey(NULL, MMKEY_PREV, 0, VK_MEDIA_PREV_TRACK); retValB = RegisterHotKey(NULL, MMKEY_NEXT, 0, VK_MEDIA_NEXT_TRACK); SetEvent(hEvent); while(retValB = GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_HOTKEY) { if (evtCallback != NULL) evtCallback((UINT8)msg.wParam); } } UnregisterHotKey(NULL, MMKEY_PLAY); UnregisterHotKey(NULL, MMKEY_PREV); UnregisterHotKey(NULL, MMKEY_NEXT); return 0; } UINT8 MultimediaKeyHook_Init(void) { if (hThread != NULL) return 0x01; hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); hThread = CreateThread(NULL, 0x00, &KeyMessageThread, NULL, 0x00, &idThread); if (hThread == NULL) return 0xFF; // CreateThread failed WaitForSingleObject(hEvent, INFINITE); return 0x00; } void MultimediaKeyHook_Deinit(void) { if (hThread == NULL) return; PostThreadMessage(idThread, WM_QUIT, 0, 0); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); hThread = NULL; return; } void MultimediaKeyHook_SetCallback(mmkey_cbfunc callbackFunc) { evtCallback = callbackFunc; return; } ================================================ FILE: VGMPlay/mmkeys_stub.c ================================================ #include "chips/mamedef.h" #include "mmkeys.h" UINT8 MultimediaKeyHook_Init(void) { return 0; } void MultimediaKeyHook_Deinit(void) { } void MultimediaKeyHook_SetCallback(mmkey_cbfunc callbackFunc) { } ================================================ FILE: VGMPlay/pt_ioctl.c ================================================ /******************************************************************************/ /* */ /* IoExample for PortTalk V2.1 */ /* Version 2.1, 12th January 2002 */ /* http://www.beyondlogic.org */ /* */ /* Copyright 2002 Craig Peacock. Craig.Peacock@beyondlogic.org */ /* Any publication or distribution of this code in source form is prohibited */ /* without prior written permission of the copyright holder. This source code */ /* is provided "as is", without any guarantee made as to its suitability or */ /* fitness for any particular use. Permission is herby granted to modify or */ /* enhance this sample code to produce a derivative program which may only be */ /* distributed in compiled object form only. */ /******************************************************************************/ #include #include #include #include "PortTalk_IOCTL.h" unsigned char OpenPortTalk(void); void ClosePortTalk(void); void outportb(unsigned short PortAddress, unsigned char byte); unsigned char inportb(unsigned short PortAddress); void InstallPortTalkDriver(void); unsigned char StartPortTalkDriver(void); #define inp(PortAddress) inportb(PortAddress) #define outp(PortAddress, Value) outportb(PortAddress, Value) HANDLE PortTalk_Handle; /* Handle for PortTalk Driver */ void outportb(unsigned short PortAddress, unsigned char byte) { unsigned int error; DWORD BytesReturned; unsigned char Buffer[3]; unsigned short * pBuffer; pBuffer = (unsigned short *)&Buffer[0]; *pBuffer = PortAddress; Buffer[2] = byte; error = DeviceIoControl(PortTalk_Handle, IOCTL_WRITE_PORT_UCHAR, &Buffer, 3, NULL, 0, &BytesReturned, NULL); if (!error) printf("Error occured during outportb while talking to PortTalk driver %d\n",GetLastError()); } unsigned char inportb(unsigned short PortAddress) { unsigned int error; DWORD BytesReturned; unsigned char Buffer[3]; unsigned short * pBuffer; pBuffer = (unsigned short *)&Buffer; *pBuffer = PortAddress; error = DeviceIoControl(PortTalk_Handle, IOCTL_READ_PORT_UCHAR, &Buffer, 2, &Buffer, 1, &BytesReturned, NULL); if (!error) printf("Error occured during inportb while talking to PortTalk driver %d\n",GetLastError()); return(Buffer[0]); } unsigned char OpenPortTalk(void) { /* Open PortTalk Driver. If we cannot open it, try installing and starting it */ PortTalk_Handle = CreateFile("\\\\.\\PortTalk", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(PortTalk_Handle == INVALID_HANDLE_VALUE) { /* Start or Install PortTalk Driver */ StartPortTalkDriver(); /* Then try to open once more, before failing */ PortTalk_Handle = CreateFile("\\\\.\\PortTalk", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(PortTalk_Handle == INVALID_HANDLE_VALUE) { printf("PortTalk: Couldn't access PortTalk Driver, Please ensure driver is loaded.\n\n"); return -1; } } return 0; } void ClosePortTalk(void) { CloseHandle(PortTalk_Handle); } unsigned char StartPortTalkDriver(void) { SC_HANDLE SchSCManager; SC_HANDLE schService; BOOL ret; DWORD err; /* Open Handle to Service Control Manager */ SchSCManager = OpenSCManager (NULL, /* machine (NULL == local) */ NULL, /* database (NULL == default) */ SC_MANAGER_ALL_ACCESS); /* access required */ if (SchSCManager == NULL) if (GetLastError() == ERROR_ACCESS_DENIED) { /* We do not have enough rights to open the SCM, therefore we must */ /* be a poor user with only user rights. */ printf("PortTalk: You do not have rights to access the Service Control Manager and\n"); printf("PortTalk: the PortTalk driver is not installed or started. Please ask \n"); printf("PortTalk: your administrator to install the driver on your behalf.\n"); return(0); } do { /* Open a Handle to the PortTalk Service Database */ schService = OpenService(SchSCManager, /* handle to service control manager database */ "PortTalk", /* pointer to name of service to start */ SERVICE_ALL_ACCESS); /* type of access to service */ if (schService == NULL) switch (GetLastError()){ case ERROR_ACCESS_DENIED: printf("PortTalk: You do not have rights to the PortTalk service database\n"); return(0); case ERROR_INVALID_NAME: printf("PortTalk: The specified service name is invalid.\n"); return(0); case ERROR_SERVICE_DOES_NOT_EXIST: printf("PortTalk: The PortTalk driver does not exist. Installing driver.\n"); printf("PortTalk: This can take up to 30 seconds on some machines . .\n"); InstallPortTalkDriver(); break; } } while (schService == NULL); /* Start the PortTalk Driver. Errors will occur here if PortTalk.SYS file doesn't exist */ ret = StartService (schService, /* service identifier */ 0, /* number of arguments */ NULL); /* pointer to arguments */ if (ret) printf("PortTalk: The PortTalk driver has been successfully started.\n"); else { err = GetLastError(); if (err == ERROR_SERVICE_ALREADY_RUNNING) printf("PortTalk: The PortTalk driver is already running.\n"); else { printf("PortTalk: Unknown error while starting PortTalk driver service.\n"); printf("PortTalk: Does PortTalk.SYS exist in your \\System32\\Drivers Directory?\n"); return(0); } } /* Close handle to Service Control Manager */ CloseServiceHandle (schService); return(TRUE); } void InstallPortTalkDriver(void) { SC_HANDLE SchSCManager; SC_HANDLE schService; DWORD err; CHAR DriverFileName[80]; /* Get Current Directory. Assumes PortTalk.SYS driver is in this directory. */ /* Doesn't detect if file exists, nor if file is on removable media - if this */ /* is the case then when windows next boots, the driver will fail to load and */ /* a error entry is made in the event viewer to reflect this */ /* Get System Directory. This should be something like c:\windows\system32 or */ /* c:\winnt\system32 with a Maximum Character lenght of 20. As we have a */ /* buffer of 80 bytes and a string of 24 bytes to append, we can go for a max */ /* of 55 bytes */ if (!GetSystemDirectory(DriverFileName, 55)) { printf("PortTalk: Failed to get System Directory. Is System Directory Path > 55 Characters?\n"); printf("PortTalk: Please manually copy driver to your system32/driver directory.\n"); } /* Append our Driver Name */ lstrcat(DriverFileName,"\\Drivers\\PortTalk.sys"); printf("PortTalk: Copying driver to %s\n",DriverFileName); /* Copy Driver to System32/drivers directory. This fails if the file doesn't exist. */ if (!CopyFile("PortTalk.sys", DriverFileName, FALSE)) { printf("PortTalk: Failed to copy driver to %s\n",DriverFileName); printf("PortTalk: Please manually copy driver to your system32/driver directory.\n"); } /* Open Handle to Service Control Manager */ SchSCManager = OpenSCManager (NULL, /* machine (NULL == local) */ NULL, /* database (NULL == default) */ SC_MANAGER_ALL_ACCESS); /* access required */ /* Create Service/Driver - This adds the appropriate registry keys in */ /* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services - It doesn't */ /* care if the driver exists, or if the path is correct. */ schService = CreateService (SchSCManager, /* SCManager database */ "PortTalk", /* name of service */ "PortTalk", /* name to display */ SERVICE_ALL_ACCESS, /* desired access */ SERVICE_KERNEL_DRIVER, /* service type */ SERVICE_DEMAND_START, /* start type */ SERVICE_ERROR_NORMAL, /* error control type */ "System32\\Drivers\\PortTalk.sys", /* service's binary */ NULL, /* no load ordering group */ NULL, /* no tag identifier */ NULL, /* no dependencies */ NULL, /* LocalSystem account */ NULL /* no password */ ); if (schService == NULL) { err = GetLastError(); if (err == ERROR_SERVICE_EXISTS) printf("PortTalk: Driver already exists. No action taken.\n"); else printf("PortTalk: Unknown error while creating Service.\n"); } else printf("PortTalk: Driver successfully installed.\n"); /* Close Handle to Service Control Manager */ CloseServiceHandle (schService); } ================================================ FILE: VGMPlay/stdbool.h ================================================ // custom stdbool.h to for 1-byte bool types #ifndef _CSTM_STDBOOL_H_ #define _CSTM_STDBOOL_H_ #ifndef __cplusplus // C++ already has the bool-type #define false 0x00 #define true 0x01 // the MS VC++ 6 compiler uses a one-byte-type (unsigned char, to be exact), so I'll reproduce this here typedef unsigned char bool; #endif // !__cplusplus #endif // !_CSTM_STDBOOL_H_ ================================================ FILE: VGMPlay/vgm-player ================================================ #!/bin/sh # Launcher script to work around limitations in vgmplay: # OSS only - detects oss wrapper and allows explicit selection # Config must be in working dir - automatically cd/mktemps as needed # Can't open release packs - plays from temporary directories # # Original script by ZekeSulastin # 7z support by Valley Bell trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"' EXIT trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"' HUP INT QUIT TERM SEGV _set_output= _output= _pulse_server= _log_path= _binary= _config= _pwd="$(pwd)" _host="$(hostname)" _ext= _unpack=false _tmpdir= _music_file= _m3u_stub= XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}" _padsp="$(command -v padsp 2>/dev/null)" _aoss="$(command -v aoss 2>/dev/null)" _help () { cat <, --pulse-server= set alternate PulseAudio server (implies --padsp) -l, --log outputs to wav files in current directory --log-path= outputs to wav files in (implies --log) WARNING: If you plan to log using this script, use --log or --log-path! Do NOT set logging on in a custom config, else the script logic will remove your logs! --binary= use alternate vgmplay binary --config= use alternate configuration file Keys (In-Player): Left/Right seek 5 seconds backward/forward Ctrl + Left/Right seek 60 seconds backward/forward Space pause Escape (twice) or Q quit program F fade out current track R restart current track PageUp or B previous track in playlist PageDown or N next track in playlist EOF exit 0 } _to_stderr () { echo -e "$0: $2" 1>&2 exit $1 } _die () { case $1 in too_many_outputs ) _to_stderr 64 "can only force one output type" ;; no_pulse_server ) _to_stderr 64 "--pulse-server requires server parameter" ;; no_log_path ) _to_stderr 64 "--log-to requires path parameter" ;; unknown_option ) _to_stderr 64 "unrecognized option '$2'\nTry 'vgm-player --help' for more information." ;; no_aoss ) _to_stderr 69 "alsa output forced but aoss not found" ;; no_oss ) _to_stderr 74 "oss output forced but /dev/dsp unwritable or not present" ;; no_pulse ) _to_stderr 69 "pulse output forced but padsp not found" ;; too_many_files ) _to_stderr 64 "only one music file should be specified" ;; file_unreadable ) _to_stderr 66 "unable to read specified input - does it exist?" ;; invalid_format ) _to_stderr 66 "format unsupported" ;; invalid_binary ) _to_stderr 69 "invalid binary specified" ;; no_binary ) _to_stderr 64 "--binary requires binary parameter" ;; no_config ) _to_stderr 64 "--config requires config parameter" ;; noread_config ) _to_stderr 69 "specified config unreadable - does it exist?" ;; invalid_config ) _to_stderr 64 "invalid config specified" ;; vgmplay_not_found ) _to_stderr 69 "vgmplay binary not found" ;; no_unpacker ) _to_stderr 69 "no suitable unpacker found" ;; tmpdir_fail ) _to_stderr 70 "unable to create/use temporary working directory" ;; no_output ) _to_stderr 69 "no usable otuput detected" ;; log_path_nowrite ) _to_stderr 73 "log path unwriteable" ;; no_music_file ) _to_stderr 64 "No music file specified." ;; * ) _to_stderr 64 "Unhandled script error: $1" ;; esac } _set_output () { if [ -n "$_set_output" ] && [ "$1" != "$_set_output" ]; then _die too_many_outputs fi _set_output=$1 case "$_set_output" in alsa ) [ -x "$_aoss" ] || _die no_aoss ;; oss ) [ -w /dev/dsp ] || _die no_oss ;; pulse ) [ -n "$_padsp" ] || _die no_pulse ;; * ) ;; esac } # Set terminal title for xterm/rxvt case $TERM in xterm* | rxvt* ) printf '%b' '\033]2;VGM Player\007' ;; * ) ;; esac # Parse command line options # All single-letter options are exclusive while :; do case $1 in --help ) _help ;; -v | --version ) echo "VGMPlay 0.40.9 player script" exit 0 ;; -a | --alsa ) _set_output alsa shift ;; -o | --oss ) _set_output oss shift ;; -p | --pulse ) _set_output pulse shift ;; --pulse-server=* ) _set_output pulse _pulse_server="${1#*=}" [ -n "$_pulse_server" ] || _die no_pulse_server shift ;; -s* ) _set_output pulse _pulse_server="${1#-s}" if [ -z "$_pulse_server" ]; then _pulse_server="$2" shift fi [ -n "$_pulse_server" ] || _die no_pulse_server shift ;; -l | --log ) _set_output log _log_path="$_pwd" shift ;; --log-path=* ) _set_output log _log_path="${1#*=}" [ -d "$_log_path" ] || _die no_log_path shift ;; --config=* ) _config="${1#*=}" [ -n "$_config" ] || _die no_binary [ -r "$_config" ] || _die noread_config file "$_config" | grep -q "ASCII" || _die invalid_config shift ;; --binary=* ) _binary="${1#*=}" [ -n "$_binary"] || _die no_binary if [ ! -x "$_binary" ] || [ ! "${binary##*/}" = "vgmplay" ]; then die _invalid_binary fi shift ;; -* ) _die unknown_option "$1" ;; -- ) shift ;; "" ) _die no_music_file ;; * ) [ -z "$2" ] || _die too_many_files _ext="$(echo ${1##*.} | tr [:upper:] [:lower:])" case $_ext in vgm | vgz | cmf | dro | m3u | zip | 7z | vgm7z ) [ -r "$1" ] || _die file_unreadable _music_file="$1";; * ) _die invalid_format esac case $_music_file in ~* | /* ) break ;; * ) _music_file="$_pwd/$_music_file" ;; esac break ;; esac done # Does `vgmplay` exist if --binary not specified? # Prefers vgmplay in working dir over any others if [ -n "$_binary" ]; then break elif [ -x "$_pwd/vgmplay" ] && [ -f "$_pwd/vgmplay" ]; then _binary="$_pwd/vgmplay" else _binary="$(command -v vgmplay 2>/dev/null)" fi [ -n "$_binary" ] || _die vgmplay_not_found # If a compressed pack, does a suitable unpacker exist? case $_ext in zip ) _unpack=true command -v unzip 2>/dev/null || _die no_unpacker ;; 7z | vgm7z ) _unpack=true command -v 7z 2>/dev/null || _die no_unpacker ;; vgm | vgz | cmf | dro | m3u ) break ;; * ) _die invalid_format ;; esac # Select config if [ -r "$_config" ]; then break else for i in "$_pwd" "$XDG_CONFIG_HOME/vgmplay" "$HOME/.config/vgmplay" "$XDG_CONFIG_DIRS/vgmplay" "$HOME/.local/share/vgmplay" "/etc/xdg/vgmplay" "/usr/local/share/vgmplay" "/usr/share/vgmplay"; do _config="$i/vgmplay.ini" [ -r "$_config" ] && break _config="$i/VGMPlay.ini" [ -r "$_config" ] && break done fi # vgmplay will function without a config, but will warn the user thus from inside the program # Make temporary working directory for vgmplay # Logdir availability checked here ... # Uses $XDG_RUNTIME_DIR, $TMPDIR, /tmp # Uses mktemp program for my sanity's sake # Assumes user isn't going to try to log into a directory w/ a preexisting vgmplay.ini # Seriously, if you're going to do that why would you use this helper to start with if [ -d "$_log_path" ]; then command sed "s/^LogSound.*/LogSound = 1/" < "$_config" > "$_log_path/vgmplay.ini" || _die log_path_nowrite elif [ -n "$XDG_RUNTIME_DIR" ]; then _tmpdir="$(command mktemp -d --tmpdir="$XDG_RUNTIME_DIR")" || _die tmpdir_fail command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail else #mktemp defaults to tmpdir then /tmp! _tmpdir="$(command mktemp -d)" || _die tmpdir_fail command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail fi # Determine output to use if not forced # Also sets pulse-server if detected # PULSE_SERVER, oss, padsp, aoss, log set, error if [ -z "$_set_output" ]; then if [ -n "$PULSE_SERVER" ] && [ -x "$_padsp" ]; then _set_output="pulse-server" elif [ -w /dev/dsp ]; then _set_output="oss" elif [ -x "$_padsp" ] && command pulseaudio --check ; then _set_output="pulse" elif [ -x "$_aoss" ]; then _set_output="alsa" elif [ -n "$_log_path" ]; then _set_output="log" else _die no_output fi fi [ -n "$_pulse_server" ] && _set_output=pulse-server if [ "$_unpack" = true ]; then if [ "$_set_output" = log ]; then [ -d "$_log_path" ] && cd "$_log_path" || _die log_path_nowrite else [ -d "$_tmpdir" ] && cd "$_tmpdir" || _die tmpdir_failed fi if [ $_ext = "zip" ]; then command unzip "$_music_file" > /dev/null elif [ $_ext = "7z" ] || [ $_ext = "vgm7z" ]; then command 7z x "$_music_file" > /dev/null fi # Notes: # - sed is used to remove the leading ./ # - find is used to scan subfolders as well _m3u_stub=$(command find . -iname '*.m3u' -print 2> /dev/null | head -n 1 | sed -e 's/^.\///') if [ -z "$_m3u_stub" ]; then _m3u_stub="List.m3u" command find . -iname '*.vg[mz]' -print | sort | sed -e 's/^.\///' > "$_m3u_stub" 2> /dev/null fi _music_file="$PWD/$_m3u_stub" fi # Plays back song with specified everything case $_set_output in log ) ( cd "$_log_path" "$_binary" "$_music_file" ) command mv "${_music_file%/*}/"*.wav "${_log_path}" [ -n "$_music_stub" ] && rm -rf "$_log_path/$_music_stub" ;; pulse-server ) _pulse_server="${_pulse_server:-$PULSE_SERVER}" ( cd "$_tmpdir" exec "$_padsp" -s "$_pulse_server" -n "VGM Player" -m "OSS Emulation (from $_host)" "$_binary" "$_music_file" ) ;; pulse ) ( cd "$_tmpdir" exec "$_padsp" -n "VGM Player" -m "OSS Emulation" "$_binary" "$_music_file" ) ;; alsa ) ( cd "$_tmpdir" exec "$_aoss" "$_binary" "$_music_file" ) ;; oss ) ( cd "$_tmpdir" exec "$_binary" "$_music_file" ) ;; esac #read waitvar # vim:ts=4:sw=4:et ================================================ FILE: VGMPlay/vgm2pcm.c ================================================ #define _GNU_SOURCE #include #include #include #include #ifdef WIN32 #include #else #include #define MAX_PATH PATH_MAX #include #include #endif #ifdef WIN32 #define DIR_CHR '\\' #define DIR_STR "\\" #define QMARK_CHR '\"' #else #define DIR_CHR '/' #define DIR_STR "/" #define QMARK_CHR '\'' #endif #include "chips/mamedef.h" #include "stdbool.h" #include "VGMPlay.h" #include "VGMPlay_Intf.h" #define SAMPLESIZE sizeof(WAVE_16BS) UINT8 CmdList[0x100]; // used by VGMPlay.c and VGMPlay_AddFmts.c bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c extern VGM_HEADER VGMHead; extern UINT32 SampleRate; extern UINT32 VGMMaxLoopM; extern UINT32 FadeTime; extern bool EndPlay; extern char *AppPaths[8]; static char AppPathBuffer[MAX_PATH * 2]; static char* GetAppFileName(void) { char* AppPath; int RetVal; AppPath = (char*)malloc(MAX_PATH * sizeof(char)); #ifdef WIN32 RetVal = GetModuleFileName(NULL, AppPath, MAX_PATH); if (! RetVal) AppPath[0] = '\0'; #else RetVal = readlink("/proc/self/exe", AppPath, MAX_PATH); if (RetVal == -1) AppPath[0] = '\0'; #endif return AppPath; } INLINE int fputBE16(UINT16 Value, FILE* hFile) { int RetVal; int ResVal; RetVal = fputc((Value & 0xFF00) >> 8, hFile); RetVal = fputc((Value & 0x00FF) >> 0, hFile); ResVal = (RetVal != EOF) ? 0x02 : 0x00; return ResVal; } int main(int argc, char *argv[]) { UINT8 result; WAVE_16BS *sampleBuffer; UINT32 bufferedLength; FILE *outputFile; char *AppName; char* AppPathPtr; const char *StrPtr; UINT8 CurPath; UINT32 ChrPos; if (argc < 3) { fputs("usage: vgm2pcm vgm_file pcm_file\n", stderr); return 1; } VGMPlay_Init(); // Path 2: exe's directory AppPathPtr = AppPathBuffer; AppName = GetAppFileName(); // "C:\VGMPlay\VGMPlay.exe" // Note: GetAppFileName always returns native directory separators. StrPtr = strrchr(AppName, DIR_CHR); if (StrPtr != NULL) { ChrPos = StrPtr + 1 - AppName; strncpy(AppPathPtr, AppName, ChrPos); AppPathPtr[ChrPos] = 0x00; // "C:\VGMPlay\" AppPaths[CurPath] = AppPathPtr; CurPath ++; AppPathPtr += ChrPos + 1; } VGMPlay_Init2(); if (!OpenVGMFile(argv[1])) { fprintf(stderr, "vgm2pcm: error: failed to open vgm_file (%s)\n", argv[1]); return 1; } if(!strcmp(argv[2], "-")) { outputFile = stdout; } else { outputFile = fopen(argv[2], "wb"); if (outputFile == NULL) { fprintf(stderr, "vgm2pcm: error: failed to open pcm_file (%s)\n", argv[2]); return 1; } } PlayVGM(); sampleBuffer = (WAVE_16BS*)malloc(SAMPLESIZE * SampleRate); if (sampleBuffer == NULL) { fprintf(stderr, "vgm2pcm: error: failed to allocate %lu bytes of memory\n", SAMPLESIZE * SampleRate); return 1; } while (!EndPlay) { UINT32 bufferSize = SampleRate; bufferedLength = FillBuffer(sampleBuffer, bufferSize); if (bufferedLength) { UINT32 numberOfSamples; UINT32 currentSample; const UINT16* sampleData; sampleData = (UINT16*)sampleBuffer; numberOfSamples = SAMPLESIZE * bufferedLength / 0x02; for (currentSample = 0x00; currentSample < numberOfSamples; currentSample++) { fputBE16(sampleData[currentSample], outputFile); } } } StopVGM(); CloseVGMFile(); VGMPlay_Deinit(); return 0; } ================================================ FILE: VGMPlay/vgm2wav.c ================================================ /* * This file is part of VGMPlay * * (c)2015 libertyernie * Based on vgm2pcm.c: * (c)2015 Francis Gagné * (c)2015 Valley Bell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #ifndef _MSC_VER // This turns command line options on (using getopt.h) unless you are using MSVC / Visual Studio, which doesn't have it. #define VGM2WAV_HAS_GETOPT #include #endif #include "chips/mamedef.h" #include "stdbool.h" #include "VGMPlay.h" #include "VGMPlay_Intf.h" #define SAMPLESIZE sizeof(WAVE_16BS) UINT8 CmdList[0x100]; // used by VGMPlay.c and VGMPlay_AddFmts.c bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c extern VGM_HEADER VGMHead; extern UINT32 SampleRate; extern bool EndPlay; extern UINT32 VGMMaxLoop; extern UINT32 FadeTime; bool WriteSmplChunk; INLINE int fputLE16(UINT16 Value, FILE* hFile) { int RetVal; int ResVal; RetVal = fputc((Value & 0x00FF) >> 0, hFile); RetVal = fputc((Value & 0xFF00) >> 8, hFile); ResVal = (RetVal != EOF) ? 0x02 : 0x00; return ResVal; } INLINE int fputLE32(UINT32 Value, FILE* hFile) { int RetVal; int ResVal; RetVal = fputc((Value & 0x000000FF) >> 0, hFile); RetVal = fputc((Value & 0x0000FF00) >> 8, hFile); RetVal = fputc((Value & 0x00FF0000) >> 16, hFile); RetVal = fputc((Value & 0xFF000000) >> 24, hFile); ResVal = (RetVal != EOF) ? 0x02 : 0x00; return ResVal; } void usage(const char *name) { fprintf(stderr, "usage: %s [options] vgm_file wav_file\n" "wav_file can be - for standard output.\n", name); #ifdef VGM2WAV_HAS_GETOPT fputs("\n" "Options:\n" "--loop-count {number}\n" "--fade-ms {number}\n" "--no-smpl-chunk\n" "\n", stderr); #else fputs("Options not supported in this build (compiled without getopt.)\n", stderr); #endif } int main(int argc, char *argv[]) { WAVE_16BS *sampleBuffer; UINT32 bufferedLength; FILE *outputFile; long int wavRIFFLengthPos; long int wavDataLengthPos; int sampleBytesWritten = 0; // Initialize VGMPlay before parsing arguments, so we can set VGMMaxLoop and FadeTime VGMPlay_Init(); VGMPlay_Init2(); VGMMaxLoop = 2; FadeTime = 5000; WriteSmplChunk = true; int c; // Parse command line arguments #ifdef VGM2WAV_HAS_GETOPT static struct option long_options[] = { { "loop-count", required_argument, NULL, 'l' }, { "fade-ms", required_argument, NULL, 'f' }, { "no-smpl-chunk", no_argument, NULL, 'S' }, { "help", no_argument, NULL, '?' }, { NULL, 0, NULL, 0 } }; while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) { switch (c) { case 'l': c = atoi(optarg); if (c <= 0) { fputs("Error: loop count must be at least 1.\n", stderr); usage(argv[0]); return 1; } VGMMaxLoop = c; //fprintf(stderr, "Setting max loops to %u\n", VGMMaxLoop); break; case 'f': FadeTime = atoi(optarg); //fprintf(stderr, "Setting fade-out time in milliseconds to %u\n", FadeTime); break; case 'S': WriteSmplChunk = false; break; case -1: break; case '?': usage(argv[0]); return 0; default: usage(argv[0]); return 1; } } // Pretend for the rest of the program that those options don't exist argv[optind - 1] = argv[0]; argc -= optind - 1; argv += optind - 1; #endif if (argc < 3) { usage(argv[0]); return 1; } if (!OpenVGMFile(argv[1])) { fprintf(stderr, "vgm2wav: error: failed to open vgm_file (%s)\n", argv[1]); return 1; } if (argv[2][0] == '-' && argv[2][1] == '\0') { #ifdef O_BINARY setmode(fileno(stdout), O_BINARY); #endif outputFile = stdout; } else { outputFile = fopen(argv[2], "wb"); if (outputFile == NULL) { fprintf(stderr, "vgm2wav: error: failed to open wav_file (%s)\n", argv[2]); return 1; } } if (WriteSmplChunk && VGMHead.lngLoopSamples == 0) { WriteSmplChunk = false; } fwrite("RIFF", 1, 4, outputFile); wavRIFFLengthPos = ftell(outputFile); fputLE32(-1, outputFile); fwrite("WAVE", 1, 4, outputFile); fwrite("fmt ", 1, 4, outputFile); fputLE32(16, outputFile); fputLE16(1, outputFile); fputLE16(2, outputFile); fputLE32(SampleRate, outputFile); fputLE32(SampleRate * 2 * 2, outputFile); fputLE16(2 * 2, outputFile); fputLE16(16, outputFile); if (WriteSmplChunk) { fwrite("smpl", 1, 4, outputFile); fputLE32(60, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(1, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); fputLE32(VGMHead.lngTotalSamples - VGMHead.lngLoopSamples, outputFile); fputLE32(VGMHead.lngTotalSamples, outputFile); fputLE32(0, outputFile); fputLE32(0, outputFile); } fwrite("data", 1, 4, outputFile); wavDataLengthPos = ftell(outputFile); fputLE32(-1, outputFile); PlayVGM(); sampleBuffer = (WAVE_16BS*)malloc(SAMPLESIZE * SampleRate); if (sampleBuffer == NULL) { fprintf(stderr, "vgm2wav: error: failed to allocate %lu bytes of memory\n", SAMPLESIZE * SampleRate); return 1; } while (!EndPlay) { UINT32 bufferSize = SampleRate; bufferedLength = FillBuffer(sampleBuffer, bufferSize); if (bufferedLength) { UINT32 numberOfSamples; UINT32 currentSample; const UINT16* sampleData; sampleData = (UINT16*)sampleBuffer; numberOfSamples = SAMPLESIZE * bufferedLength / 0x02; for (currentSample = 0x00; currentSample < numberOfSamples; currentSample++) { fputLE16(sampleData[currentSample], outputFile); sampleBytesWritten += 2; } } } fflush(outputFile); StopVGM(); CloseVGMFile(); VGMPlay_Deinit(); if (wavRIFFLengthPos >= 0) { fseek(outputFile, wavRIFFLengthPos, SEEK_SET); if (WriteSmplChunk) { fputLE32(sampleBytesWritten + 28 + 68 + 8, outputFile); } else { fputLE32(sampleBytesWritten + 28 + 8, outputFile); } } if (wavDataLengthPos >= 0) { fseek(outputFile, wavDataLengthPos, SEEK_SET); fputLE32(sampleBytesWritten, outputFile); } return 0; } ================================================ FILE: VGMPlay/vgm2wav.dsp ================================================ # Microsoft Developer Studio Project File - Name="vgm2pcm" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** NICHT BEARBEITEN ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=VGMPlay - Win32 Debug !MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE !MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl !MESSAGE !MESSAGE NMAKE /f "VGMPlay.mak". !MESSAGE !MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben !MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: !MESSAGE !MESSAGE NMAKE /f "VGMPlay.mak" CFG="VGMPlay - Win32 Debug" !MESSAGE !MESSAGE Fr die Konfiguration stehen zur Auswahl: !MESSAGE !MESSAGE "VGMPlay - Win32 Release" (basierend auf "Win32 (x86) Console Application") !MESSAGE "VGMPlay - Win32 Debug" (basierend auf "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "VGMPlay - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c # ADD CPP /nologo /MD /W3 /GX /Ox /Ot /Og /Oi /Ob2 /I "zlib" /D "NDEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FD /c # SUBTRACT CPP /Oa /Ow /YX /Yc /Yu # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 msvcrt.lib kernel32.lib user32.lib advapi32.lib winmm.lib zdll.lib /nologo /subsystem:console /machine:I386 /libpath:"zlib" # Begin Special Build Tool SOURCE="$(InputPath)" PostBuild_Cmds=..\vgm2txt\HiddenMsg.exe Release\VGMPlay.exe # End Special Build Tool !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "zlib" /D "_DEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FR /FD /GZ /c # SUBTRACT CPP /YX /Yc /Yu # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 msvcrtd.lib kernel32.lib user32.lib advapi32.lib winmm.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /libpath:"zlib" # SUBTRACT LINK32 /profile /map !ENDIF # Begin Target # Name "VGMPlay - Win32 Release" # Name "VGMPlay - Win32 Debug" # Begin Group "Quellcodedateien" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\pt_ioctl.c # End Source File # Begin Source File SOURCE=.\Stream.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\VGMPlay.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\VGMPlay_AddFmts.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\vgm2wav.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # End Group # Begin Group "Header-Dateien" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\PortTalk_IOCTL.h # End Source File # Begin Source File SOURCE=.\Stream.h # End Source File # Begin Source File SOURCE=".\XMasFiles\SWJ-SQRC01_1C.h" # End Source File # Begin Source File SOURCE=.\VGMFile.h # End Source File # Begin Source File SOURCE=.\VGMPlay.h # End Source File # Begin Source File SOURCE=.\VGMPlay_Intf.h # End Source File # Begin Source File SOURCE=.\XMasFiles\XMasBonus.h # End Source File # End Group # Begin Group "Ressourcendateien" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # Begin Group "SoundCore" # PROP Default_Filter "c;h;cpp" # Begin Group "FM OPL Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2413intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2413intf.h # End Source File # Begin Source File SOURCE=.\chips\2413tone.h # End Source File # Begin Source File SOURCE=.\chips\262intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\262intf.h # End Source File # Begin Source File SOURCE=.\chips\281btone.h # End Source File # Begin Source File SOURCE=.\chips\3526intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\3526intf.h # End Source File # Begin Source File SOURCE=.\chips\3812intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\3812intf.h # End Source File # Begin Source File SOURCE=.\chips\8950intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\8950intf.h # End Source File # Begin Source File SOURCE=.\chips\adlibemu.h # End Source File # Begin Source File SOURCE=.\chips\adlibemu_opl2.c # End Source File # Begin Source File SOURCE=.\chips\adlibemu_opl3.c # End Source File # Begin Source File SOURCE=.\chips\emu2413.c # End Source File # Begin Source File SOURCE=.\chips\emu2413.h # End Source File # Begin Source File SOURCE=.\chips\emutypes.h # End Source File # Begin Source File SOURCE=.\chips\fmopl.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\fmopl.h # End Source File # Begin Source File SOURCE=.\chips\opl.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=.\chips\opl.h # End Source File # Begin Source File SOURCE=.\chips\vrc7tone.h # End Source File # Begin Source File SOURCE=.\chips\ym2413.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ym2413.h # End Source File # Begin Source File SOURCE=.\chips\ymf262.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf262.h # End Source File # Begin Source File SOURCE=.\chips\ymf278b.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf278b.h # End Source File # End Group # Begin Group "FM OPN Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2203intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2203intf.h # End Source File # Begin Source File SOURCE=.\chips\2608intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2608intf.h # End Source File # Begin Source File SOURCE=.\chips\2610intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2610intf.h # End Source File # Begin Source File SOURCE=.\chips\2612intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2612intf.h # End Source File # Begin Source File SOURCE=.\chips\fm.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\fm.h # End Source File # Begin Source File SOURCE=.\chips\fm2612.c # End Source File # Begin Source File SOURCE=.\chips\ym2612.c # End Source File # Begin Source File SOURCE=.\chips\ym2612.h # End Source File # Begin Source File SOURCE=.\chips\ym3438.c # End Source File # Begin Source File SOURCE=.\chips\ym3438.h # End Source File # End Group # Begin Group "FM OPx Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\2151intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\2151intf.h # End Source File # Begin Source File SOURCE=.\chips\scsp.c # End Source File # Begin Source File SOURCE=.\chips\scsp.h # End Source File # Begin Source File SOURCE=.\chips\scspdsp.c # End Source File # Begin Source File SOURCE=.\chips\scspdsp.h # End Source File # Begin Source File SOURCE=.\chips\scsplfo.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=.\chips\ym2151.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ym2151.h # End Source File # Begin Source File SOURCE=.\chips\ymf271.c # ADD CPP /W1 # End Source File # Begin Source File SOURCE=.\chips\ymf271.h # End Source File # End Group # Begin Group "PCM Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\c140.c # End Source File # Begin Source File SOURCE=.\chips\c140.h # End Source File # Begin Source File SOURCE=.\chips\c352.c # End Source File # Begin Source File SOURCE=.\chips\c352.h # End Source File # Begin Source File SOURCE=.\chips\es5503.c # End Source File # Begin Source File SOURCE=.\chips\es5503.h # End Source File # Begin Source File SOURCE=.\chips\es5506.c # End Source File # Begin Source File SOURCE=.\chips\es5506.h # End Source File # Begin Source File SOURCE=.\chips\iremga20.c # End Source File # Begin Source File SOURCE=.\chips\iremga20.h # End Source File # Begin Source File SOURCE=.\chips\k053260.c # End Source File # Begin Source File SOURCE=.\chips\k053260.h # End Source File # Begin Source File SOURCE=.\chips\k054539.c # End Source File # Begin Source File SOURCE=.\chips\k054539.h # End Source File # Begin Source File SOURCE=.\chips\multipcm.c # End Source File # Begin Source File SOURCE=.\chips\multipcm.h # End Source File # Begin Source File SOURCE=.\chips\okim6258.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\okim6258.h # End Source File # Begin Source File SOURCE=.\chips\okim6295.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\okim6295.h # End Source File # Begin Source File SOURCE=.\chips\pwm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\pwm.h # End Source File # Begin Source File SOURCE=.\chips\qsound.c # End Source File # Begin Source File SOURCE=.\chips\qsound.h # End Source File # Begin Source File SOURCE=.\chips\rf5c68.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\rf5c68.h # End Source File # Begin Source File SOURCE=.\chips\scd_pcm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\scd_pcm.h # End Source File # Begin Source File SOURCE=.\chips\segapcm.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\segapcm.h # End Source File # Begin Source File SOURCE=.\chips\upd7759.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\upd7759.h # End Source File # Begin Source File SOURCE=.\chips\x1_010.c # End Source File # Begin Source File SOURCE=.\chips\x1_010.h # End Source File # Begin Source File SOURCE=.\chips\ymdeltat.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ymdeltat.h # End Source File # Begin Source File SOURCE=.\chips\ymz280b.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" # ADD CPP /W1 !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ymz280b.h # End Source File # End Group # Begin Group "OPL Mapper" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\ay8910_opl.c # End Source File # Begin Source File SOURCE=.\chips\sn76496_opl.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413_opl.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413hd.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /W1 /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ym2413hd.h # End Source File # End Group # Begin Group "PSG Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\ay8910.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\ay8910.h # End Source File # Begin Source File SOURCE=.\chips\ay_intf.c # End Source File # Begin Source File SOURCE=.\chips\ay_intf.h # End Source File # Begin Source File SOURCE=.\chips\c6280.c # End Source File # Begin Source File SOURCE=.\chips\c6280.h # End Source File # Begin Source File SOURCE=.\chips\c6280intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\c6280intf.h # End Source File # Begin Source File SOURCE=.\chips\emu2149.c # End Source File # Begin Source File SOURCE=.\chips\emu2149.h # End Source File # Begin Source File SOURCE=.\chips\gb.c # End Source File # Begin Source File SOURCE=.\chips\gb.h # End Source File # Begin Source File SOURCE=.\chips\k051649.c # End Source File # Begin Source File SOURCE=.\chips\k051649.h # End Source File # Begin Source File SOURCE=.\chips\Ootake_PSG.c # End Source File # Begin Source File SOURCE=.\chips\Ootake_PSG.h # End Source File # Begin Source File SOURCE=.\chips\pokey.c # End Source File # Begin Source File SOURCE=.\chips\pokey.h # End Source File # Begin Source File SOURCE=.\chips\saa1099.c # End Source File # Begin Source File SOURCE=.\chips\saa1099.h # End Source File # Begin Source File SOURCE=.\chips\sn76489.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn76489.h # End Source File # Begin Source File SOURCE=.\chips\sn76496.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn76496.h # End Source File # Begin Source File SOURCE=.\chips\sn764intf.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\sn764intf.h # End Source File # Begin Source File SOURCE=.\chips\vsu.c # End Source File # Begin Source File SOURCE=.\chips\vsu.h # End Source File # Begin Source File SOURCE=.\chips\ws_audio.c # End Source File # Begin Source File SOURCE=.\chips\ws_audio.h # End Source File # Begin Source File SOURCE=.\chips\ws_initialIo.h # End Source File # End Group # Begin Group "NES Chips" # PROP Default_Filter "c;h" # Begin Source File SOURCE=.\chips\nes_apu.c # End Source File # Begin Source File SOURCE=.\chips\nes_apu.h # End Source File # Begin Source File SOURCE=.\chips\nes_defs.h # End Source File # Begin Source File SOURCE=.\chips\nes_intf.c # End Source File # Begin Source File SOURCE=.\chips\nes_intf.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_apu.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_apu.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_dmc.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_dmc.h # End Source File # Begin Source File SOURCE=.\chips\np_nes_fds.c # End Source File # Begin Source File SOURCE=.\chips\np_nes_fds.h # End Source File # End Group # Begin Source File SOURCE=.\chips\ChipIncl.h # End Source File # Begin Source File SOURCE=.\ChipMapper.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\ChipMapper.h # End Source File # Begin Source File SOURCE=.\chips\dac_control.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\dac_control.h # End Source File # Begin Source File SOURCE=.\chips\mamedef.h # End Source File # Begin Source File SOURCE=.\chips\panning.c !IF "$(CFG)" == "VGMPlay - Win32 Release" # ADD CPP /Oa !ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" !ENDIF # End Source File # Begin Source File SOURCE=.\chips\panning.h # End Source File # End Group # Begin Source File SOURCE=.\ReadMe.txt # End Source File # End Target # End Project ================================================ FILE: VGMPlay/vgmplay.1 ================================================ .TH vgmplay "1" "November 10" "Valley Bell" "User Commands" .nh .SH NAME vgmplay \- the official and always up-to-date player for all VGM files .SH SYNOPSIS \fBvgmplay\fP file .SH KEYS Cursor Left/Right - Seek 5 seconds backward/forward .PP Ctrl + Cursor Left/Right - Seek 1 minute backward/forward .PP Space - Pause .PP ESC/Q - Quit the program .PP F - Fade out .PP R - Restart current Track .PP PageUp/B - Previous Track .PP PageDown/N - Next Track .SH SUPPORTED FILETYPES Video Game Music Files (.vgm, .vgz) .PP Creative Music Files (.cmf) .PP DosBox RAW OPL Log Files (.dro) .PP Playlist files (.m3u) .SH SUPPORTED CHIPS .PP SN76496 (2) (Sega PSG) and T6W28 (2) (NeoGeo Pocket custom) .PP YM2413 (1) (OPLL) .PP YM2612 (OPN2) .PP YM2151 (OPM) .PP SegaPCM .PP RF5C68 .PP YM2203 (OPN) .PP YM2608 (OPNA) .PP YM2610/B (OPNB) .PP YM3812 (1) (OPL2) .PP YM3526 (1) (OPL) .PP Y8950 (1) (MSX AUDIO) .PP YMF262 (1) (OP3) .PP YMF278B (3) (OPL4) .PP YMF271 (OPLX) .PP YMZ280B .PP RF5C164 (Sega MegaCD PCM) .PP PWM (from Sega 32x) .PP AY8910 (MSX PSG) .PP GameBoy DMG .PP NES APU (incl. FDS) .PP MultiPCM .PP UPD7759 .PP OKI6258 (Sharp X68000 ADPCM) .PP OKI6295 .PP K051649 .PP K054539 .PP HuC6280 (PC Engine) .PP C140 .PP K053260 .PP Pokey (Atari) .PP QSound .PP SCSP (Saturn Custom Sound Processor, YMF292-F) .PP (1) This chip can be emulated via OPL Hardware (like Soundblaster sound cards). .PP (2) OPL hardware emulation is available, but software emulation is prefered. Hardware emulation is used if another chip activates HW emulation or FMForce is True. (3) You need a sample ROM, called yrw801.rom, to make playback work. Place it in the directory where vgmplay lies or in /usr/local/share/vgmplay/. .PP OPL hardware emulation can be enabled by setting the "FMPort"-entry in the ini-file. Under Linux the program must be run as root to use hardware FM. .PP It's possible to write Wave files by editing the "LogSound"-line in the ini-file. Batch conversions are possible by opening a playlist. FM hardware cannot be logged to Wave files. .SH CONFIGURATION vgmplay is configured in the file VGMPlay.ini, which should be located in $XDG_CONFIG_HOME/vgmplay (thus, by default ~/.config/vgmplay/VGMPlay.ini). A sample configuration /usr/share/vgmplay/VGMPlay.ini is available for copying and general reference. .SH BUGS PauseEmulation is disabled under Linux if no FM Hardware is used. .PP You have to double-tap ESC to quit the program. .PP Sometimes MAME's sound cores tend to sound strange. .SH COMMENTS The T6W28 doesn't use MAME's T6W28 core. Instead the SN76496 core is modified to emulate the T6W28 with 2 SN76496 chips. The SN76496 OPL emulation is okay, but it's impossible to get the noise sound right. .PP EMU2413 Emulator was added, because sometimes the one of MAME sounds strange. The Gens YM2612 core was added for the same reason before MAME's YM2612 core was fixed. .PP .SH AUTHORS This program was written by Valley Bell. .PP Almost all software emulators are from MAME (http://mamedev.org) .PP EMU2413 and Gens YM2612 were ported from Maxim's in_vgm .PP The YMF278B core was ported from openMSX .PP zlib compression by Jean-loup Gailly and Mark Adler is used .PP All custom OPL Mappers were written using MAME software emulators and the OPL2/3 programming guides by Jeffrey S. Lee and Vladimir Arnost .PP one YM2413 OPL Mapper was ported from MEKA. .PP The RF5C164 and PWM cores were ported from Gens/GS .PP The MAME YM2612 core was fixed with the help of Blargg's MAME YM2612 fix and Genesis Plus GX' YM2612 core .PP AdLibEmu (OPL2 and OPL3 core) was ported from DOSBox .PP The default HuC6280 core is from Ootake. .PP EMU2149, the alternative NES APU core and the NES FDS core were ported from rainwarrior's NSFPlay. ================================================ FILE: VGMPlay/vgmspec171.txt ================================================ VGM Spec v1.71 beta ============== VGM (Video Game Music) is a sample-accurate sound logging format for the Sega Master System, the Sega Game Gear and possibly many other machines (e.g. Sega Genesis). The normal file extension is .vgm but files can also be GZip compressed into .vgz files. However, a VGM player should attempt to support compressed and uncompressed files with either extension. (ZLib's GZIO library makes this trivial to implement.) The format starts with a 256 byte header: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0x00 ["Vgm " ident ][EoF offset ][Version ][SN76489 clock ] 0x10 [YM2413 clock ][GD3 offset ][Total # samples][Loop offset ] 0x20 [Loop # samples ][Rate ][SN FB ][SNW][SF][YM2612 clock ] 0x30 [YM2151 clock ][VGM data offset][Sega PCM clock ][SPCM Interface ] 0x40 [RF5C68 clock ][YM2203 clock ][YM2608 clock ][YM2610/B clock ] 0x50 [YM3812 clock ][YM3526 clock ][Y8950 clock ][YMF262 clock ] 0x60 [YMF278B clock ][YMF271 clock ][YMZ280B clock ][RF5C164 clock ] 0x70 [PWM clock ][AY8910 clock ][AYT][AY Flags ][VM] *** [LB][LM] 0x80 [GB DMG clock ][NES APU clock ][MultiPCM clock ][uPD7759 clock ] 0x90 [OKIM6258 clock ][OF][KF][CF] *** [OKIM6295 clock ][K051649 clock ] 0xA0 [K054539 clock ][HuC6280 clock ][C140 clock ][K053260 clock ] 0xB0 [Pokey clock ][QSound clock ][SCSP clock ][Extra Hdr ofs ] 0xC0 [WSwan clock ][VSU clock ][SAA1099 clock ][ES5503 clock ] 0xD0 [ES5506 clock ][EC][EC][CD] *** [X1-010 clock ][C352 clock ] 0xE0 [GA20 clock ] *** *** *** *** *** *** *** *** *** *** *** *** 0xF0 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** - Unused space (marked with *) is reserved for future expansion, and must be zero. - All integer values are *unsigned* and written in "Intel" byte order (Little Endian), so for example 0x12345678 is written as 0x78 0x56 0x34 0x12. - All pointer offsets are written as relative to the current position in the file, so for example the GD3 offset at 0x14 in the header is the file position of the GD3 tag minus 0x14. - All header sizes are valid for all versions from 1.50 on, as long as header has at least 64 bytes. If the VGM data starts at an offset that is lower than 0x100, all overlapping header bytes have to be handled as they were zero. - VGMs run with a rate of 44100 Hz. The unit of time is called "sample". 0x00: "Vgm " (0x56 0x67 0x6d 0x20) file identification (32 bits) 0x04: Eof offset (32 bits) Relative offset to end of file (i.e. file length - 4). This is mainly used to find the next track when concatenating player stubs and multiple files. 0x08: Version number (32 bits) Version number in BCD-Code. e.g. Version 1.71 is stored as 0x00000171. This is used for backwards compatibility in players, and defines which header values are valid. 0x0C: SN76489 clock (32 bits) Input clock rate in Hz for the SN76489 PSG chip. A typical value is 3579545. It should be 0 if there is no PSG chip used. Note: Bit 31 (0x80000000) is used on combination with the dual-chip-bit to indicate that this is a T6W28. (PSG variant used in NeoGeo Pocket) 0x10: YM2413 clock (32 bits) Input clock rate in Hz for the YM2413 chip. A typical value is 3579545. It should be 0 if there is no YM2413 chip used. If bit 31 is set it is a VRC7 (version 1.51+). 0x14: GD3 offset (32 bits) Relative offset to GD3 tag. 0 if no GD3 tag. GD3 tags are descriptive tags similar in use to ID3 tags in MP3 files. See the GD3 specification for more details. The GD3 tag is usually stored immediately after the VGM data. 0x18: Total # samples (32 bits) Total of all wait values in the file. 0x1C: Loop offset (32 bits) Relative offset to loop point, or 0 if no loop. For example, if the data for the one-off intro to a song was in bytes 0x0040-0x3FFF of the file, but the main looping section started at 0x4000, this would contain the value 0x4000-0x1C = 0x00003FE4. 0x20: Loop # samples (32 bits) Number of samples in one loop, or 0 if there is no loop. Total of all wait values between the loop point and the end of the file. [VGM 1.01 additions:] 0x24: Rate (32 bits) "Rate" of recording in Hz, used for rate scaling on playback. It is typically 50 for PAL systems and 60 for NTSC systems. It should be set to zero if rate scaling is not appropriate - for example, if the game adjusts its music engine for the system's speed. VGM 1.00 files will have a value of 0. [VGM 1.10 additions:] 0x28: SN76489 feedback (16 bits) The white noise feedback pattern for the SN76489 PSG. Known values are: 0x0009 Sega Master System 2/Game Gear/Mega Drive (SN76489/SN76496 integrated into Sega VDP chip) 0x0003 Sega Computer 3000H, BBC Micro (SN76489AN) 0x0006 SN76494, SN76496 For version 1.01 and earlier files, the feedback pattern should be assumed to be 0x0009. If the PSG is not used then this may be omitted (left at zero). 0x2A: SN76489 shift register width (8 bits) The noise feedback shift register width, in bits. Known values are: 16 Sega Master System 2/Game Gear/Mega Drive (SN76489/SN76496 integrated into Sega VDP chip) 15 Sega Computer 3000H, BBC Micro (SN76489AN) For version 1.01 and earlier files, the shift register width should be assumed to be 16. If the PSG is not used then this may be omitted (left at zero). [VGM 1.51 additions:] 0x2B: SN76489 Flags (8 bits) Misc flags for the SN76489. Most of them don't make audible changes and can be ignored, if the SN76489 emulator lacks the features. bit 0 frequency 0 is 0x400 bit 1 output negate flag bit 2 stereo on/off (on when bit clear) bit 3 /8 Clock Divider on/off (on when bit clear) bit 4 XNOR noise feedback (used by NCR8496) bit 5-7 reserved (must be zero) For version 1.51 and earlier files, all the flags should not be set. If the PSG is not used then this may be omitted (left at zero). [VGM 1.10 additions:] 0x2C: YM2612/YM3438 clock (32 bits) Input clock rate in Hz for the YM2612 chip. A typical value is 7670454. It should be 0 if there us no YM2612 chip used. For version 1.01 and earlier files, the YM2413 clock rate should be used for the clock rate of the YM2612. If bit 31 is set it is a YM3438 (version 1.51+). 0x30: YM2151/YM2164 clock (32 bits) Input clock rate in Hz for the YM2151 chip. A typical value is 3579545. It should be 0 if there us no YM2151 chip used. For version 1.01 and earlier files, the YM2413 clock rate should be used for the clock rate of the YM2151. If bit 31 is set it is a YM2164 (version 1.51+). [VGM 1.50 additions:] 0x34: VGM data offset (32 bits) Relative offset to VGM data stream. If the VGM data starts at absolute offset 0x40, this will contain value 0x0000000C. For versions prior to 1.50, it should be 0 and the VGM data must start at offset 0x40. [VGM 1.51 additions:] 0x38: Sega PCM clock (32 bits) Input clock rate in Hz for the Sega PCM chip. A typical value is 4000000. It should be 0 if there is no Sega PCM chip used. 0x3C: Sega PCM interface register (32 bits) The interface register for the Sega PCM chip. It should be 0 if there is no Sega PCM chip used. 0x40: RF5C68 clock (32 bits) Input clock rate in Hz for the RF5C68 PCM chip. A typical value is 12500000. It should be 0 if there is no RF5C68 chip used. 0x44: YM2203 clock (32 bits) Input clock rate in Hz for the YM2203 chip. A typical value is 3000000. It should be 0 if there is no YM2203 chip used. 0x48: YM2608 clock (32 bits) Input clock rate in Hz for the YM2608 chip. A typical value is 8000000. It should be 0 if there is no YM2608 chip used. 0x4C: YM2610/YM2610B clock (32 bits) Input clock rate in Hz for the YM2610/B chip. A typical value is 8000000. It should be 0 if there is no YM2610/B chip used. Note: Bit 31 is used to set whether it is a YM2610 or a YM2610B chip. If bit 31 is set it is an YM2610B, if bit 31 is clear it is an YM2610. 0x50: YM3812 clock (32 bits) Input clock rate in Hz for the YM3812 chip. A typical value is 3579545. It should be 0 if there is no YM3812 chip used. 0x54: YM3526 clock (32 bits) Input clock rate in Hz for the YM3526 chip. A typical value is 3579545. It should be 0 if there is no YM3526 chip used. 0x58: Y8950 clock (32 bits) Input clock rate in Hz for the Y8950 chip. A typical value is 3579545. It should be 0 if there is no Y8950 chip used. 0x5C: YMF262 clock (32 bits) Input clock rate in Hz for the YMF262 chip. A typical value is 14318180. It should be 0 if there is no YMF262 chip used. 0x60: YMF278B clock (32 bits) Input clock rate in Hz for the YMF278B chip. A typical value is 33868800. It should be 0 if there is no YMF278B chip used. 0x64: YMF271 clock (32 bits) Input clock rate in Hz for the YMF271 chip. A typical value is 16934400. It should be 0 if there is no YMF271 chip used. 0x68: YMZ280B clock (32 bits) Input clock rate in Hz for the YMZ280B chip. A typical value is 16934400. It should be 0 if there is no YMZ280B chip used. 0x6C: RF5C164 clock (32 bits) Input clock rate in Hz for the RF5C164 PCM chip. A typical value is 12500000. It should be 0 if there is no RF5C164 chip used. 0x70: PWM clock (32 bits) Input clock rate in Hz for the PWM chip. A typical value is 23011361. It should be 0 if there is no PWM chip used. 0x74: AY8910 clock (32 bits) Input clock rate in Hz for the AY8910 chip. A typical value is 1789773. It should be 0 if there is no AY8910 chip used. 0x78: AY8910 Chip Type (8 bits) Defines the exact type of AY8910. The values are: 0x00 - AY8910 0x01 - AY8912 0x02 - AY8913 0x03 - AY8930 0x10 - YM2149 0x11 - YM3439 0x12 - YMZ284 0x13 - YMZ294 If the AY8910 is not used then this may be omitted (left at zero). 0x79: AY8910 Flags (8 bits) Misc flags for the AY8910. Default is 0x01. For additional description see ay8910.h in MAME source code. bit 0 Legacy Output bit 1 Single Output bit 2 Discrete Output bit 3 RAW Output bit 4 YM2149 Pin 26 (additional /2 clock divider) bit 5-6 reserved (must be zero) bit 7 Use ZX-Spectrum-like pan configuration (Left-Center-Right). If the AY8910 is not used then this may be omitted (left at zero). 0x7A: YM2203/AY8910 Flags (8 bits) Misc flags for the AY8910. This one is specific for the AY8910 that's connected with/part of the YM2203. 0x7B: YM2608/AY8910 Flags (8 bits) Misc flags for the AY8910. This one is specific for the AY8910 that's connected with/part of the YM2608. [VGM 1.60 additions:] 0x7C: Volume Modifier (8 bits) Volume = 2 ^ (VolumeModifier / 0x20) where VolumeModifier is a number from -63 to 192 (-63 = 0xC1, 0 = 0x00, 192 = 0xC0). Also the value -63 gets replaced with -64 in order to make factor of 0.25 possible. Therefore the volume can reach levels between 0.25 and 64. Default is 0, which is equal to a factor of 1.0 or 100%. Note: Players should support the Volume Modifier in v1.50 files and higher despite their late addition. This way MegaDrive VGMs can use the Volume Modifier without breaking compatibility with old players. 0x7D: reserved Reserved byte for future use. It must be 0. 0x7E: Loop Base (8 bits) Modifies the number of loops that are played before the playback ends. Set this value to eg. 1 to reduce the number of played loops by one. This is useful, if the song is looped twice in the vgm, because there are minor differences between the first and second loop and the song repeats just the second loop. The resulting number of loops that are played is calculated as following: NumLoops = NumLoopsModified - LoopBase Default is 0. Negative numbers are possible (80h...FFh = -128...-1) Note: Players should support the Volume Modifier in v1.51 files. [VGM 1.51 additions:] 0x7F: Loop Modifier (8 bits) Modifies the number of loops that are played before the playback ends. You may want to use this, e.g. if a tune has a very short, but non- repetive loop (then set it to 0x20 double the loop number). The resulting number of loops that are played is calculated as following: NumLoops = ProgramNumLoops * LoopModifier / 0x10 Default is 0, which is equal to 0x10. [VGM 1.61 additions:] 0x80: GameBoy DMG clock (32 bits) Input clock rate in Hz for the GameBoy DMG chip, LR35902. A typical value is 4194304. It should be 0 if there is no GB DMG chip used. 0x84: NES APU clock (32 bits) Input clock rate in Hz for the NES APU chip, N2A03. A typical value is 1789772. It should be 0 if there is no NES APU chip used. Note: Bit 31 is used to enable the FDS sound addon. Set to enable, clear to disable. 0x88: MultiPCM clock (32 bits) Input clock rate in Hz for the MultiPCM chip. A typical value is 8053975. It should be 0 if there is no MultiPCM chip used. 0x8C: uPD7759 clock (32 bits) Input clock rate in Hz for the uPD7759 chip. A typical value is 640000. It should be 0 if there is no uPD7759 chip used. Set bit 31 (0x80000000) to denote that the chip is in a Slave, clear bit 31 to denote that the chip is in a Master configuration. 0x90: OKIM6258 clock (32 bits) Input clock rate in Hz for the OKIM6258 chip. A typical value is 4000000. It should be 0 if there is no OKIM6258 chip used. 0x94: OKIM6258 Flags (8 bits) Misc flags for the OKIM6258. Default is 0x00. bit 0-1 Clock Divider (clock dividers are 1024, 768, 512, 512) bit 2 3/4-bit ADPCM select (default is 4-bit, doesn't work currently) bit 3 10/12-bit Output (default is 10-bit) bit 4-7 reserved (must be zero) If the OKIM6258 is not used then this may be omitted (left at zero). 0x95: K054539 Flags (8 bits) Misc flags for the K054539. Default is 0x01. See also k054539.h in MAME source code. bit 0 Reverse Stereo bit 1 Disable Reverb bit 2 Update at KeyOn bit 3-7 reserved (must be zero) If the K054539 is not used then this may be omitted (left at zero). 0x96: C140 Chip Type (8 bits) Defines the exact type of C140 and its banking method. The values are: 0x00 - C140, Namco System 2 0x01 - C140, Namco System 21 0x02 - 219 ASIC, Namco NA-1/2 If the C140 is not used then this may be omitted (left at zero). 0x97: reserved Reserved byte for future use. It must be 0. 0x98: OKIM6295 clock (32 bits) Input clock rate in Hz for the OKIM6295 chip. A typical value is 8000000. It should be 0 if there is no OKIM6295 chip used. Set bit 31 (0x80000000) to denote the status of pin 7. 0x9C: K051649/K052539 clock (32 bits) Input clock rate in Hz for the K051649 chip. A typical value is 1789773. It should be 0 if there is no K051649 chip used. If bit 31 is set it is a K052539. 0xA0: K054539 clock (32 bits) Input clock rate in Hz for the K054539 chip. A typical value is 18432000. It should be 0 if there is no K054539 chip used. 0xA4: HuC6280 clock (32 bits) Input clock rate in Hz for the HuC6280 chip. A typical value is 3579545. It should be 0 if there is no HuC6280 chip used. 0xA8: C140 clock (32 bits) Input clock rate in Hz for the C140 chip. A typical value is 8000000. It should be 0 if there is no C140 chip used. 0xAC: K053260 clock (32 bits) Input clock rate in Hz for the K053260 chip. A typical value is 3579545. It should be 0 if there is no K053260 chip used. 0xB0: Pokey clock (32 bits) Input clock rate in Hz for the Pokey chip. A typical value is 1789772. It should be 0 if there is no Pokey chip used. 0xB4: QSound clock (32 bits) Input clock rate in Hz for the QSound chip. A typical value is 4000000. It should be 0 if there is no QSound chip used. [VGM 1.71 additions:] 0xB8: SCSP clock (32 bits) Input clock rate in Hz for the SCSP chip. A typical value is 22579200. It should be 0 if there is no SCSP chip used. [VGM 1.70 additions:] 0xBC: Extra Header Offset (32 bits) Relative offset to the extra header or 0 if no extra header is present. [VGM 1.71 additions:] 0xC0: WonderSwan clock (32 bits) Input clock rate in Hz for the WonderSwan chip. A typical value is 3072000. It should be 0 if there is no WonderSwan chip used. 0xC4: Virtual Boy VSU clock (32 bits) Input clock rate in Hz for the VSU chip. A typical value is 5000000. It should be 0 if there is no VSU chip used. 0xC8: SAA1099 clock (32 bits) Input clock rate in Hz for the SAA1099 chip. A typical value is 8000000. It should be 0 if there is no SAA1099 chip used. 0xCC: ES5503 clock (32 bits) Input clock rate in Hz for the ES5503 chip. A typical value is 7159090. It should be 0 if there is no ES5503 chip used. 0xD0: ES5505/ES5506 clock (32 bits) Input clock rate in Hz for the ES5506 chip. A typical value is 16000000. It should be 0 if there is no ES5506 chip used. Note: Bit 31 is used to set whether it is an ES5505 or an ES5506 chip. If bit 31 is set it is an ES5506, if bit 31 is clear it is an ES5505. 0xD4: ES5503 Channels (8 bits) Defines the internal number of output channels for the ES5503. Possible values are 1 to 8. A typical value is 2. If the ES5503 is not used then this may be omitted (left at zero). 0xD5: ES5505/6 Channels (8 bits) Defines the internal number of output channels for the ES5506. Possible values are 1 to 4 for the ES5505 and 1 to 8 for the ES5506. A typical value is 1. If the ES5506 is not used then this may be omitted (left at zero). 0xD6: C352 Clock Divider (8 bits) Defines the clock divider for the C352 chip, divided by 4 in order to achieve a divider range of 0 to 1020. A typical value is 288. If the C352 is not used then this may be omitted (left at zero). Note: Bit 31 is used to disable the rear channels, which are often not connected to speakers. Set to disable them, clear to enable. 0xD7: reserved Reserved byte for future use. It must be 0. 0xD8: Seta X1-010 clock (32 bits) Input clock rate in Hz for the X1-010 chip. A typical value is 16000000. It should be 0 if there is no X1-010 chip used. 0xDC: Namco C352 clock (32 bits) Input clock rate in Hz for the C352 chip. A typical value is 24192000. It should be 0 if there is no C352 chip used. 0xE0: Irem GA20 clock (32 bits) Input clock rate in Hz for the GA20 chip. A typical value is 3579545. It should be 0 if there is no GA20 chip used. 0xE4: reserved Reserved bytes for future use. They must be 0. Starting at the location specified by the VGM data offset (or, offset 0x40 for file versions below 1.50) is found a sequence of commands containing data written to the chips or timing information. A command is one of: 0x00 : no-operation, no file should contain these, but players should just skip them 0x30 dd : Used for dual chip support: see below 0x31 dd : Set AY8910 stereo mask Bit 0-1: Channel A mask (00=off, 01=left, 10=right, 11=center) Bit 2-3: Channel B mask (00=off, 01=left, 10=right, 11=center) Bit 4-5: Channel C mask (00=off, 01=left, 10=right, 11=center) Bit 6: Chip type, 0=AY8910, 1=YM2203 SSG part Bit 7: Chip number, 0 or 1 0x3F dd : Used for dual chip support: see below 0x4F dd : Game Gear PSG stereo, write dd to port 0x06 0x50 dd : PSG (SN76489/SN76496) write value dd 0x51 aa dd : YM2413, write value dd to register aa 0x52 aa dd : YM2612 port 0, write value dd to register aa 0x53 aa dd : YM2612 port 1, write value dd to register aa 0x54 aa dd : YM2151, write value dd to register aa 0x55 aa dd : YM2203, write value dd to register aa 0x56 aa dd : YM2608 port 0, write value dd to register aa 0x57 aa dd : YM2608 port 1, write value dd to register aa 0x58 aa dd : YM2610 port 0, write value dd to register aa 0x59 aa dd : YM2610 port 1, write value dd to register aa 0x5A aa dd : YM3812, write value dd to register aa 0x5B aa dd : YM3526, write value dd to register aa 0x5C aa dd : Y8950, write value dd to register aa 0x5D aa dd : YMZ280B, write value dd to register aa 0x5E aa dd : YMF262 port 0, write value dd to register aa 0x5F aa dd : YMF262 port 1, write value dd to register aa 0x61 nn nn : Wait n samples, n can range from 0 to 65535 (approx 1.49 seconds). Longer pauses than this are represented by multiple wait commands. 0x62 : wait 735 samples (60th of a second), a shortcut for 0x61 0xdf 0x02 0x63 : wait 882 samples (50th of a second), a shortcut for 0x61 0x72 0x03 0x66 : end of sound data 0x67 ... : data block: see below 0x68 ... : PCM RAM write: see below 0x7n : wait n+1 samples, n can range from 0 to 15. 0x8n : YM2612 port 0 address 2A write from the data bank, then wait n samples; n can range from 0 to 15. Note that the wait is n, NOT n+1. (Note: Written to first chip instance only.) 0x90-0x95 : DAC Stream Control Write: see below 0xA0 aa dd : AY8910, write value dd to register aa 0xA1-0xAF aa dd : Used for dual chip support: see below 0xB0 aa dd : RF5C68, write value dd to register aa 0xB1 aa dd : RF5C164, write value dd to register aa 0xB2 ad dd : PWM, write value ddd to register a (d is MSB, dd is LSB) 0xB3 aa dd : GameBoy DMG, write value dd to register aa Note: Register 00 equals GameBoy address FF10. 0xB4 aa dd : NES APU, write value dd to register aa Note: Registers 00-1F equal NES address 4000-401F, registers 20-3E equal NES address 4080-409E, register 3F equals NES address 4023, registers 40-7F equal NES address 4040-407F. 0xB5 aa dd : MultiPCM, write value dd to register aa 0xB6 aa dd : uPD7759, write value dd to register aa 0xB7 aa dd : OKIM6258, write value dd to register aa 0xB8 aa dd : OKIM6295, write value dd to register aa 0xB9 aa dd : HuC6280, write value dd to register aa 0xBA aa dd : K053260, write value dd to register aa 0xBB aa dd : Pokey, write value dd to register aa 0xBC aa dd : WonderSwan, write value dd to register aa 0xBD aa dd : SAA1099, write value dd to register aa 0xBE aa dd : ES5506, write 8-bit value dd to register aa 0xBF aa dd : GA20, write value dd to register aa 0xC0 bbaa dd : Sega PCM, write value dd to memory offset aabb 0xC1 bbaa dd : RF5C68, write value dd to memory offset aabb 0xC2 bbaa dd : RF5C164, write value dd to memory offset aabb 0xC3 cc bbaa : MultiPCM, write set bank offset aabb to channel cc 0xC4 mmll rr : QSound, write value mmll to register rr (mm - data MSB, ll - data LSB) 0xC5 mmll dd : SCSP, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) 0xC6 mmll dd : WonderSwan, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) 0xC7 mmll dd : VSU, write value dd to register mmll (mm - MSB, ll - LSB) 0xC8 mmll dd : X1-010, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) 0xD0 pp aa dd : YMF278B port pp, write value dd to register aa 0xD1 pp aa dd : YMF271 port pp, write value dd to register aa 0xD2 pp aa dd : SCC1 port pp, write value dd to register aa 0xD3 pp aa dd : K054539 write value dd to register ppaa 0xD4 pp aa dd : C140 write value dd to register ppaa 0xD5 pp aa dd : ES5503 write value dd to register ppaa 0xD6 aa ddee : ES5506 write 16-bit value ddee to register aa 0xE0 dddddddd : seek to offset dddddddd (Intel byte order) in PCM data bank 0xE1 aabb ddee: C352 write 16-bit value ddee to register aabb Some ranges are reserved for future use, with different numbers of operands: 0x32..0x3E dd : one operand, reserved for future use 0x40..0x4E dd dd : two operands, reserved for future use Note: was one operand only til v1.60 0xC9..0xCF dd dd dd : three operands, reserved for future use 0xD7..0xDF dd dd dd : three operands, reserved for future use 0xE2..0xFF dd dd dd dd : four operands, reserved for future use On encountering these, the correct number of bytes should be skipped. When encountering undefined commands, the processing the file should be stopped instantly. Data blocks ----------- VGM command 0x67 specifies a data block. These are used to store large amounts of data, which can be used in parallel with the normal VGM data stream. The data block format is: 0x67 0x66 tt ss ss ss ss (data) where: 0x67 = VGM command 0x66 = compatibility command to make older players stop parsing the stream tt = data type ss ss ss ss (32 bits) = size of data, in bytes (data) = data, of size previously specified Data blocks of recorded streams, if present, should be at the very start of the VGM data. Multiple data blocks expand the data bank. (The start offset and length of the block in the data bank should be saved for command 0x95.) Because data blocks can happen anywhere in the stream, players must be able to parse data blocks anywhere in the stream. The data block type specifies what type of data it contains. Currently defined types are: 00..3F : data of recorded streams (uncompressed) 40..7E : data of recorded streams (compressed) data block format for compressed streams: tt (8 bits) = compression type 00 - n-Bit-Compression 01 - DPCM-Compression ss ss ss ss (32 bits) = size of uncompressed data (for memory allocation) It is assumed that each decompressed value uses ceil(bd/8) bytes. (attr) = attribute bytes used by the decompression-algorithm n-Bit-Compression: bd (8 bits) = Bits decompressed bc (8 bits) = Bits compressed st (8 bits) = compression sub-type 00 - copy (high bits aren't used) 01 - shift left (low bits aren't used) 02 - use table (data = index into decompression table, see data block 7F) aa aa (16 bits) = value that is added (ignored if table is used) The data block is treated as a bitstream with bc bits per value. The top bits in each byte are read first. The extracted bits of each value are transformed into a value with at least bd bits using method st. Finally, aaaa is added to get the resulting value. DPCM-Compression: (uses a decompression table) bd (8 bits) = Bits decompressed bc (8 bits) = Bits compressed st (8 bits) = [reserved for future use, must be 00] aa aa (16 bits) = start value The data is read as a bitstream (see n-Bit). The read value is used as index into a delta-table (defined by data block 7F). The delta value is added to the "state" value, which is also the result value. The "state" value is initialized with aaaa at the beginning. (data) = compressed data, of size (block size - 0x0A - attr size) 7F : Decompression Table tt (8 bits) = compression type (see data block 40..7E) st (8 bits) = compression sub-type (see data block 40..7E) bd (8 bits) = Bits decompressed bc (8 bits) = Bits compressed (only used for verifying against block 40..7E) cc cc (16 bits) = number of following values (with each of size ceil(bd / 8)) (data) = table data, cccc values with a total size of (block size - 0x06) Note: Multiple decompression tables are valid. The player should keep a list of one table per tt and st combination. If there are multiple tables of the same tt/st type, the new one overrides the old one and all following compressed data blocks will use the new table. 80..BF : ROM/RAM Image dumps (contain usually samples) data block format for ROM dumps: rr rr rr rr (32 bits) = size of the entire ROM ss ss ss ss (32 bits) = start address of data (data) = ROM data, of size (block size - 0x08) The size of the VGM can be decreased a lot by saving only the used parts of the ROM. This is done by saving multiple small ROM data blocks. The start address is the ROM offset where the data will be written, the ROM size is used to allocate space for the ROM (and some chips rely on it). C0..DF : RAM writes (for RAM with up to 64 KB) data block format for direct RAM writes: ss ss (16 bits) = start address of data (affected by a chip's banking registers) (data) = RAM data, of size (block size - 0x02) E0..FF : RAM writes (for RAM with more than 64 KB) data block format for direct RAM writes: ss ss ss ss (32 bits) = start address of data (affected by a chip's banking registers) (data) = RAM data, of size (block size - 0x04) 00 = YM2612 PCM data for use with associated commands 01 = RF5C68 PCM data for use with associated commands 02 = RF5C164 PCM data for use with associated commands 03 = PWM PCM data for use with associated commands 04 = OKIM6258 ADPCM data for use with associated commands 05 = HuC6280 PCM data for use with associated commands 06 = SCSP PCM data for use with associated commands 07 = NES APU DPCM data for use with associated commands 40..7E = same as 00..3E, but compressed 80 = Sega PCM ROM data 81 = YM2608 DELTA-T ROM data 82 = YM2610 ADPCM ROM data 83 = YM2610 DELTA-T ROM data 84 = YMF278B ROM data 85 = YMF271 ROM data 86 = YMZ280B ROM data 87 = YMF278B RAM data 88 = Y8950 DELTA-T ROM data 89 = MultiPCM ROM data 8A = uPD7759 ROM data 8B = OKIM6295 ROM data 8C = K054539 ROM data 8D = C140 ROM data 8E = K053260 ROM data 8F = Q-Sound ROM data 90 = ES5506 ROM data 91 = X1-010 ROM data 92 = C352 ROM data 93 = GA20 ROM data C0 = RF5C68 RAM write C1 = RF5C164 RAM write C2 = NES APU RAM write E0 = SCSP RAM write E1 = ES5503 RAM write All unknown types must be skipped by the player. PCM RAM writes -------------- VGM command 0x68 specifies a PCM RAM write. These are used to write data from data blocks to the RAM of a PCM chip. The data block format is: 0x68 0x66 cc oo oo oo dd dd dd ss ss ss where: 0x68 = VGM command 0x66 = compatibility command to make older players stop parsing the stream cc = chip type (see data block types 00..3F) oo oo oo (24 bits) = read offset in data block dd dd dd (24 bits) = write offset in chip's ram (affected by chip's registers) ss ss ss (24 bits) = size of data, in bytes Since size can't be zero, a size of 0 bytes means 0x0100 0000 bytes. All unknown types must be skipped by the player. DAC Stream Control Write ------------------------ VGM commands 0x90 to 0x95 specify writes to the DAC Stream Control Driver. These are used to stream data from data blocks to the chips via chip writes. To use it you must: 1. Setup the Stream (set chip type and command) - this activates the stream 2. Set the Stream Data Bank 3. Set the Stream Frequency 4. Now you can start the stream, change its frequency, start it again, stop it, etc ... There are the following commands: Note: Stream ID 0xFF is reserved and ignored unless noted otherwise. Setup Stream Control: 0x90 ss tt pp cc ss = Stream ID tt = Chip Type (see clock-order in header, e.g. YM2612 = 0x02) bit 7 is used to select the 2nd chip pp cc = write command/register cc at port pp Note: For chips that use Channel Select Registers (like the RF5C-family and the HuC6280), the format is pp cd where pp is the channel number, c is the channel register and d is the data register. If you set pp to FF, the channel select write is skipped. Set Stream Data: 0x91 ss dd ll bb ss = Stream ID dd = Data Bank ID (see data block types 0x00..0x3f) ll = Step Size (how many data is skipped after every write, usually 1) Set to 2, if you're using an interleaved stream (e.g. for left/right channel). bb = Step Base (data offset added to the Start Offset when starting stream playback, usually 0) If you're using an interleaved stream, set it to 0 in one stream and to 1 in the other one. Note: Step Size/Step Step are given in command-data-size (i.e. 1 for YM2612, 2 for PWM), not bytes Set Stream Frequency: 0x92 ss ff ff ff ff ss = Stream ID ff = Frequency (or Sample Rate, in Hz) at which the writes are done Start Stream: 0x93 ss aa aa aa aa mm ll ll ll ll ss = Stream ID aa = Data Start offset in data bank (byte offset in data bank) Note: if set to -1, the Data Start offset is ignored mm = Length Mode (how the Data Length is calculated) 00 - ignore (just change current data position) 01 - length = number of commands 02 - length in msec 03 - play until end of data 1? - (bit 4) Reverse Mode 8? - (bit 7) Loop (automatically restarts when finished) ll = Data Length Stop Stream: 0x94 ss ss = Stream ID Note: 0xFF stops all streams Start Stream (fast call): 0x95 ss bb bb ff ss = Stream ID bb = Block ID (number of the data block that is part of the data bank set with command 0x91) ff = Flags bit 0 - Loop (see command 0x93, mm bit 7) bit 4 - Reverse Mode (see command 0x93) General Note to the DAC Stream Control: Although it may be quite hard to press already streamed data into these commands, it makes it very easy to write vgm-creation tools that need to stream something. (like YM2612 DAC drums/voices/etc.) The DAC Stream Control can use with almost all chips and is NOT limited to chips such as YM2612 and PWM. Dual Chip Support ----------------- These chips support two instances of a chip in one vgm: PSG, YM2413, YM2612, YM2151, SegaPCM, YM2203, YM2608, YM2610, YM3812, YM3526, Y8950, YMZ280B, YMF262, YMF278B, YMF271, AY8910, GameBoy DMG, NES APU, MultiPCM, uPD7759, OKIM6258, OKIM6295, K051649, K054539, HuC6280, C140, K053260, Pokey, SCSP, WonderSwan, VSU, SAA1099, ES5503, ES5506, X1-010, C352, GA20. Dual chip support is activated by setting bit 30 (0x40000000) in the chip's clock value. (Note: The PSG needs this bit set for T6W28 mode.) Note: For the YM3812 only, setting bit 31 (0x80000000) in the chip's clock value hard-pans the first chip on the left and the second chip on the right. This replicates the pan configuration of the Sound Blaster Pro 1 and Pro Audio Spectrum sound cards on the IBM PC. Dual Chip Support #1: The second chip instance is controlled via separate commands. The second SN76489 PSG uses 0x30 (0x3F for GG Stereo). All chips of the YM-family that use command 0x5n use 0xAn for the second chip. n is the last digit of the main command. e.g. 0x52 (1st chip) -> 0xA2 (2nd chip) Dual Chip Support #2: All other chips use bit 7 (0x80) of the first parameter byte to distinguish between the 1st and 2nd chip. (0x00-7F = Chip 1, 0x80-0xFF = chip 2) Note: The SegaPCM chip has the 2nd-chip-bit in the high byte of the address parameter. This is the second parameter byte. Dual Chip Support #3: Data block commands have bit 31 set for ROM/RAM dumps that go to the second chip. Extra Header ------------ With VGM v1.70, there was an extra header added. This one has to be placed between the usual header and the actual VGM data. This is the format of the extra header: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0x00 [Header Size ][ChpClock Offset][ChpVol Offset ] Header Size is the size of the extra header. It has to be 4 or larger, depending in the needed offsets. Then there are two offsets that point to extra header data for: - additional Chip Clocks for second chips - user-defined chip volumes Chip Clock Header ----------------- 1 byte - Entry Count (chips with extra clocks) [5 bytes - List Entry 1] [5 bytes - List Entry 2] ... Each list entry has the format: 1 byte - Chip ID (chip order follows the header) 4 bytes - clock for second chip of the type above Chip Volume Header ------------------ 1 byte - Entry Count (chips with user-defined volumes) [4 bytes - List Entry 1] [4 bytes - List Entry 2] ... Each list entry has the format: 1 byte - Chip ID (chip order follows the header) Note: If bit 7 is set, it's the volume for a paired chip. (e.g. the AY-part of the YM2203) 1 byte - Flags Note: If bit 0 is set, it's the volume for the second chip. 2 bytes - volume for the chip Note: If Bit 15 is 0, this is an absolute volume setting. If Bit 15 is 1, it's relative and the chip volume gets multiplied by ((Value & 0x7FFF) / 0x0100). History ------- [1.00] Initial public release by Dave [1.01] Rate value added by Maxim; 1.00 files are fully compatible [1.10] PSG white noise feedback and shift register width parameters added by Maxim, with note on how to handle earlier version files. Additional wait command added by Maxim with thanks to Steve Snake for the suggestion. 1.01 files are fully compatible but 1.01 players might have problems with 1.10 files, hence the 0.1 version change. [1.50] VGM data offset added to header by Maxim. Data block support added by blargg, to allow for better handling of YM2612 PCM data. Both of these changes have the potential to cause problems, but are really good changes, so the version number has been increased all the way to 1.50. [1.51] Sega PCM, RF5C68, YM2203, YM2608, YM2610/B, YM3812, YM3526, Y8950, YMF262, YMF278B, YMF271, YMZ280B, RF5C164, PWM and AY8910 chips and commands added. Additional data block types RF5C68 RAM write, RF5C164 RAM write, Sega PCM ROM, YM2608 DELTA-T ROM, YM2610 ADPCM ROM, YM2610 DELTA-T ROM, YMF278B ROM, YMF271 ROM, YMF271 RAM, YMZ280B ROM and Y8950 DELTA-T ROM Data added. Data Block Types splitted into 4 categories. (PCM Stream, compressed PCM Stream, ROM/RAM Dump, RAM write) SN76489 Flags and Loop Modifier added. It is the first time the header size exceeds 0x40 bytes. 1.51 files are fully compatible to 1.50 players, but there may be problems with the new commands. Note: Dual chip support was added too, but as a "cheat"-feature. The dual-chip -bits in the clock values are not compatible to 1.50, but the rest is. All changes done by Valley Bell. [1.60] RF5C68, RF5C164 and PWM PCM blocks and compressed data blocks added. A whole bunch of new commands (PCM RAM write and DAC Stream Control) added. Volume Modifier and Loop Base added. The new commands (especially 0x9?) may cause problems with older players. All changes done by Valley Bell. [1.61] GameBoy DMG, NES APU, MultiPCM, uPD7759, OKIM6258, OKIM6295, K051649, K051649, HuC6280, C140, K053260, Pokey and Q-Sound chips added. (including necessary data blocks) Changed number of operands from 1 to 2 for reserved commands 0x40-0x4E. Although they're still unused, old players might handle future vgm versions wrongly. All changes done by Valley Bell. [1.70] Added extra header with seperate chip clocks for the second one of dual chips and chip volume adjustments. All changes done by Valley Bell. [1.71] SCSP, WonderSwan, Virtual Boy VSU, SAA1099, ES5503, ES5506, Seta X1-010, Namco C352, Irem GA20 added. (including necessary ROM data blocks) [Valley Bell] Data blocks (type 0x) for OKIM6258, HuC6280, SCSP and NES added. VGM v1.61 players should support the data block of their respective chips despite their late addition. [Valley Bell] Added bit 31 flags to change chip types for YM2612, YM2151 and K051649. These changes should be considered backwards compatible with VGM 1.51. [grauw, ctr] Added command 0x31 to set AY8910 stereo mask. [NewRisingSun] Documented some chip flags that have been present since earlier versions of VGMPlay. [NewRisingSun] Added "XNOR noise feedback" bit to SN76489 flags. The new flag should be supported by VGM v1.51 players where possible. [Valley Bell] ================================================ FILE: VGMPlay/xdg/vgmplay-mime.xml ================================================ VGM Audio File Compressed VGM Audio File ================================================ FILE: VGMPlay/xdg/vgmplay.desktop.in ================================================ [Desktop Entry] Name=VGMPlay GenericName=VGMPlay Comment=VGM File Player Exec=@BIN_PATH@vgmplay %f Terminal=true Icon=vgmplay Type=Application StartupNotify=false Version=1.0 Categories=AudioVideo; MimeType=audio/x-vgm;audio/x-vgz;application/mpegurl;application/x-mpegurl;audio/mpegurl;audio/x-mpegurl; Keywords=Audio;Music;VGM;Player;Video Game Music;Sound Chip; OKI; SN; YM; OPL; PCM; AY; ================================================ FILE: VGMPlay/zlib/zconf.h ================================================ /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO typedef unsigned long z_size_t; #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif #ifndef Z_ARG /* function prototypes for stdarg */ # if defined(STDC) || defined(Z_HAVE_STDARG_H) # define Z_ARG(args) args # else # define Z_ARG(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # include /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) # define Z_HAVE_UNISTD_H #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ ================================================ FILE: VGMPlay/zlib/zlib.def ================================================ ; zlib data compression library EXPORTS ; basic functions zlibVersion deflate deflateEnd inflate inflateEnd ; advanced functions deflateSetDictionary deflateGetDictionary deflateCopy deflateReset deflateParams deflateTune deflateBound deflatePending deflatePrime deflateSetHeader inflateSetDictionary inflateGetDictionary inflateSync inflateCopy inflateReset inflateReset2 inflatePrime inflateMark inflateGetHeader inflateBack inflateBackEnd zlibCompileFlags ; utility functions compress compress2 compressBound uncompress uncompress2 gzopen gzdopen gzbuffer gzsetparams gzread gzfread gzwrite gzfwrite gzprintf gzvprintf gzputs gzgets gzputc gzgetc gzungetc gzflush gzseek gzrewind gztell gzoffset gzeof gzdirect gzclose gzclose_r gzclose_w gzerror gzclearerr ; large file functions gzopen64 gzseek64 gztell64 gzoffset64 adler32_combine64 crc32_combine64 ; checksum functions adler32 adler32_z crc32 crc32_z adler32_combine crc32_combine ; various hacks, don't look :) deflateInit_ deflateInit2_ inflateInit_ inflateInit2_ inflateBackInit_ gzgetc_ zError inflateSyncPoint get_crc_table inflateUndermine inflateValidate inflateCodesUsed inflateResetKeep deflateResetKeep gzopen_w ================================================ FILE: VGMPlay/zlib/zlib.h ================================================ /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.11, January 15th, 2017 Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.2.11" #define ZLIB_VERNUM 0x12b0 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 11 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip and raw deflate streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); typedef void (*free_func) OF((voidpf opaque, voidpf address)); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text for deflate, or the decoding state for inflate */ uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. In that case, zlib is thread-safe. When zalloc and zfree are Z_NULL on entry to the initialization function, they are set to internal routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion OF((void)); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary. Some output may be provided even if flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. See deflatePending(), which can be used if desired to determine whether or not there is more ouput in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used in the first deflate call after deflateInit if all the compression is to be done in a single step. In order to complete in one call, avail_out must be at least the value returned by deflateBound (see below). Then deflate is guaranteed to return Z_STREAM_END. If not enough output space is provided, deflate will not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the Adler-32 checksum of all input read so far (that is, total_in bytes). If a gzip stream is being generated, then strm->adler will be the CRC-32 checksum of the input read so far. (See deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL or the state was inadvertently written over by the application), or Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. In the current version of inflate, the provided input is not read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression. Actual decompression will be done by inflate(). So next_in, and avail_in, next_out, and avail_out are unused and unchanged. The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), then next_in and avail_in are updated accordingly, and processing will resume at this point for the next call of inflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. If the caller of inflate() does not provide both available input and available output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the stream completes, which reduces inflate's memory footprint. If the stream does not complete, either because not all of the stream is provided or not enough output space is provided, then a sliding window will be allocated and inflate() can be called again to continue the operation as if Z_NO_FLUSH had been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the effects of the flush parameter in this implementation are on the return value of inflate() as noted below, when inflate() returns early when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output produced so far. The CRC-32 is checked against the gzip trailer, as is the uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value, in which case strm->msg points to a string with a more specific error), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL, or the state was inadvertently written over by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is to be attempted. */ ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state was inconsistent. */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. For the current implementation of deflate(), a windowBits value of 8 (a window size of 256 bytes) is not supported. As a result, a request for 8 will result in 9 (a 512-byte window). In that case, providing 8 to inflateInit2() will result in an error when the zlib header with 9 is checked against the initialization of inflate(). The remedy is to not use 8 with deflateInit2() with this initialization, or at least in that case use 9 with inflateInit2(). windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to the appropriate value, if the operating system was determined at compile time. If a gzip stream is being written, strm->adler is a CRC-32 instead of an Adler-32. For raw deflate or gzip encoding, a request for a 256-byte window is rejected as invalid, since only the zlib header provides a means of transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If deflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. deflateGetDictionary() may return a length less than the window size, even when more than the window size in input has been provided. It may return up to 258 bytes less in that case, due to how zlib's implementation of deflate manages the sliding window and lookahead for matches, where matches can be up to 258 bytes long. If the application needs the last window-size bytes of input, then that would need to be saved by the application outside of zlib. deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been set unchanged. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, int level, int strategy)); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the strategy is changed, and if any input has been consumed in a previous deflate() call, then the input available so far is compressed with the old level and strategy using deflate(strm, Z_BLOCK). There are three approaches for the compression levels 0, 1..3, and 4..9 respectively. The new level and strategy will take effect at the next call of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not take effect. In this case, deflateParams() can be called again with the same parameters and more output space to try again. In order to assure a change in the parameters on the first try, the deflate stream should be flushed using deflate() with Z_BLOCK or other flush request until strm.avail_out is not zero, before calling deflateParams(). Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be applied to the the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if there was not enough output space to complete the compression of the available input data before a change in the strategy or approach. Note that in the case of a Z_BUF_ERROR, the parameters are not changed. A return value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be retried with more output space. */ ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain)); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen)); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). If that first deflate() call is provided the sourceLen input bytes, an output buffer allocated to the size returned by deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, unsigned *pending, int *bits)); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. If pending or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, int value)); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head)); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to 255, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int windowBits)); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see below), inflate() will not automatically decode concatenated gzip streams. inflate() will return Z_STREAM_END at the end of the gzip stream. The state would need to be reset to continue decoding a subsequent gzip stream. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similary, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync searches for a 00 00 FF FF pattern in the compressed data. All full flush points have this pattern, but not all occurrences of this pattern are full flush points. inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, z_streamp source)); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, int windowBits)); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. If the window size is changed, then the memory allocated for the window is freed, and the window will be reallocated by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, int bits, int value)); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, unsigned char FAR *window)); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func) OF((void FAR *, z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than inflate() for file i/o applications, in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. inflate() can be faster on modern CPUs when used with large buffers. inflateBack() trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the default behavior of inflate(), which expects a zlib header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero -- buf is ignored in that case -- and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ #ifndef Z_SOLO /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress() is equivalent to compress2() with a level parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In the case where there is not enough room, uncompress() will fill the output buffer with the uncompressed data up to that point. */ ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen)); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of source bytes consumed. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or appending with no compression and not using the gzip format. "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. The addition of "x" when writing will create the file exclusively, which fails if the file already exists. On systems that support it, the addition of "e" when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create such a file. (Also see gzflush() for another way to do this.) When appending, gzopen does not test whether the file begins with a gzip stream, nor does it look for the end of the gzip streams to begin appending. gzopen will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. When reading, this will be detected automatically by looking for the magic two- byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); /* gzdopen associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); /* Set the internal buffer size used by this library's functions. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Three times that size in buffer space is allocated. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); /* Dynamically update the compression level or strategy. See the description of deflateInit2 for the meaning of these parameters. Previously provided data is flushed before the parameter change. gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not opened for writing, Z_ERRNO if there is an error writing the flushed data, or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If the input file is not in gzip format, gzread copies the given number of bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream. Any number of gzip streams may be concatenated in the input file, and will all be decompressed by gzread(). If something other than a gzip stream is encountered after a gzip stream, that remaining trailing garbage is ignored (and no error is returned). gzread can be used to read a gzip file that is being concurrently written. Upon reaching the end of the input, gzread will return with the available data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then gzclearerr can be used to clear the end of file indicator in order to permit gzread to be tried again. Z_OK indicates that a gzip stream was completed on the last gzread. Z_BUF_ERROR indicates that the input file ended in the middle of a gzip stream. Note that gzread does not return -1 in the event of an incomplete gzip stream. This error is deferred until gzclose(), which will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip stream. Alternatively, gzerror can be used before gzclose to detect this case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. If len is too large to fit in an int, then nothing is read, -1 is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, gzFile file)); /* Read up to nitems items of size size from file to buf, otherwise operating as gzread() does. This duplicates the interface of stdio's fread(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfread() returns the number of full items read of size size, or zero if the end of the file was reached and a full item could not be read, or if there was an error. gzerror() must be consulted if zero is returned in order to determine if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is read, zero is returned, and the error state is set to Z_STREAM_ERROR. In the event that the end of file is reached and only a partial item is available at the end, i.e. the remaining uncompressed data length is not a multiple of size, then the final partial item is nevetheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior is the same as the behavior of fread() implementations in common libraries, but it prevents the direct use of gzfread() to read a concurrently written file, reseting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); /* Writes the given number of uncompressed bytes into the compressed file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, z_size_t nitems, gzFile file)); /* gzfwrite() writes nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfwrite() returns the number of full items written of size size, or zero if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is written, zero is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or a negative zlib error code in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf() because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len == 1, the string is terminated with a null character. If no characters are read due to an end-of-file or len < 1, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); /* Writes c, converted to an unsigned char, into the compressed file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. As such, it does not do all of the checking the other functions do. I.e. it does not check to see if file is NULL, nor whether the structure file points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); /* Push one character back onto the stream to be read as the first character on the next read. At least one character of push-back is allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); /* Flushes all pending output into the compressed file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, z_off_t offset, int whence)); Sets the starting position for the next gzread or gzwrite on the given compressed file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); /* Rewinds the given file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) */ /* ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); Returns the starting position for the next gzread or gzwrite on the given compressed file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); Returns the current offset in the file being read or written. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof OF((gzFile file)); /* Returns true (1) if the end-of-file indicator has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: gzdirect() is not needed when writing. Transparent writing must be explicitly requested, so the application already knows the answer. When linking statically, using gzdirect() will include all of the zlib code for gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); /* Flushes all pending output if necessary, closes the compressed file and deallocates the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); /* Clears the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ #endif /* !Z_SOLO */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as adler32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note that the z_off_t type (like off_t) is a signed integer. If len2 is negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, z_size_t len)); /* Same as crc32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, const char *version, int stream_size)); ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)); ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size)); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define z_inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #else # define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #endif #ifndef Z_SOLO /* gzgetc() macro and its supporting function and exposed data structure. Note * that the real internal state is much larger than the exposed structure. * This abbreviated structure exposes just enough for the gzgetc() macro. The * user should not mess with these exposed elements, since their names or * behavior could change in the future, perhaps even capriciously. They can * only be used by the gzgetc() macro. You have been warned. */ struct gzFile_s { unsigned have; unsigned char *next; z_off64_t pos; }; ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) # ifdef Z_PREFIX_SET # define z_gzopen z_gzopen64 # define z_gzseek z_gzseek64 # define z_gztell z_gztell64 # define z_gzoffset z_gzoffset64 # define z_adler32_combine z_adler32_combine64 # define z_crc32_combine z_crc32_combine64 # else # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # endif # ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif #else /* Z_SOLO */ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif /* !Z_SOLO */ /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, const char *format, va_list va)); # endif #endif #ifdef __cplusplus } #endif #endif /* ZLIB_H */ ================================================ FILE: in_vgm/README.md ================================================ # in_vgm Build Instructions In order to build in_vgm you need to download the source of VGMPlay and extract it to `..\VGMPlay\`. Then you can open `in_vgm.dsw` with MS Visual C++ to compile it. in_vgm was made with MS VC++ 6. It should compile with later versions of MS Visual C++ too, but it will ask you to convert the project first. ## Debug Build Notes To make testing easier, the `in_vgm.dll` gets copied to the `Winamp\Plugins` folder after the Debug build finished. You may want to edit the post-build-command so that it fits with your Winamp PlugIn-directory. ================================================ FILE: in_vgm/Winamp/DSP.H ================================================ #ifndef NULLSOFT_WINAMP_DSP_H #define NULLSOFT_WINAMP_DSP_H // DSP plugin interface // notes: // any window that remains in foreground should optimally pass unused // keystrokes to the parent (winamp's) window, so that the user // can still control it. As for storing configuration, // Configuration data should be stored in \plugin.ini // (look at the vis plugin for configuration code) typedef struct winampDSPModule { char *description; // description HWND hwndParent; // parent window (filled in by calling app) HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app) void (*Config)(struct winampDSPModule *this_mod); // configuration dialog (if needed) int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (if needed) // modify waveform samples: returns number of samples to actually write // (typically numsamples, but no more than twice numsamples, and no less than half numsamples) // numsamples should always be at least 128. should, but I'm not sure int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); void (*Quit)(struct winampDSPModule *this_mod); // called when unloading void *userData; // user data, optional } winampDSPModule; typedef struct { int version; // DSP_HDRVER char *description; // description of library winampDSPModule* (*getModule)(int); // module retrieval function int (*sf)(int key); // DSP_HDRVER == 0x21 } winampDSPHeader; // exported symbols #ifdef USE_DSP_HDR_HWND typedef winampDSPHeader* (*winampDSPGetHeaderType)(HWND); #define DSP_HDRVER 0x22 #else typedef winampDSPHeader* (*winampDSPGetHeaderType)(HWND); // header version: 0x20 == 0.20 == winamp 2.0 #define DSP_HDRVER 0x20 #endif // return values from the winampUninstallPlugin(HINSTANCE hdll, HWND parent, int param) // which determine if we can uninstall the plugin immediately or on winamp restart #define DSP_PLUGIN_UNINSTALL_NOW 0x0 #define DSP_PLUGIN_UNINSTALL_REBOOT 0x1 // // uninstall support was added from 5.0+ and uninstall now support from 5.5+ // it is down to you to ensure that if uninstall now is returned that it will not cause a crash // (ie don't use if you've been subclassing the main window) // Version note: // // Added passing of Winamp's main hwnd in the call to the exported winampDSPHeader() // which allows for primarily the use of localisation features with the bundled plugins. // If you want to use the new version then either you can edit you version of dsp.h or // you can add USE_DSP_HDR_HWND to your project's defined list or before use of dsp.h // #endif ================================================ FILE: in_vgm/Winamp/GEN.H ================================================ #ifndef NULLSOFT_WINAMP_GEN_H #define NULLSOFT_WINAMP_GEN_H #include #define GEN_INIT_SUCCESS 0 // return values from the winampUninstallPlugin(HINSTANCE hdll, HWND parent, int param) // which determine if we can uninstall the plugin immediately or on winamp restart // // uninstall support was added from 5.0+ and uninstall now support from 5.5+ // it is down to you to ensure that if uninstall now is returned that it will not cause a crash // (ie don't use if you've been subclassing the main window) #define GEN_PLUGIN_UNINSTALL_NOW 0x1 #define GEN_PLUGIN_UNINSTALL_REBOOT 0x0 typedef struct { int version; char *description; int (*init)(); void (*config)(); void (*quit)(); HWND hwndParent; HINSTANCE hDllInstance; } winampGeneralPurposePlugin; #define GPPHDR_VER 0x10 #ifdef __cplusplus extern "C" { #endif //extern winampGeneralPurposePlugin *gen_plugins[256]; typedef winampGeneralPurposePlugin * (*winampGeneralPurposePluginGetter)(); #ifdef __cplusplus } #endif #endif ================================================ FILE: in_vgm/Winamp/IN2.H ================================================ #ifndef NULLSOFT_WINAMP_IN2H #define NULLSOFT_WINAMP_IN2H #include "out.h" // note: exported symbol is now winampGetInModule2. #define IN_UNICODE 0x0F000000 #ifdef UNICODE_INPUT_PLUGIN #define in_char wchar_t #define IN_VER (IN_UNICODE | 0x100) #else #define in_char char #define IN_VER 0x100 #endif #define IN_MODULE_FLAG_USES_OUTPUT_PLUGIN 1 // By default, Winamp assumes that your input plugin wants to use Winamp's EQ, and doesn't do replay gain // if you handle any of these yourself (EQ, Replay Gain adjustments), then set these flags accordingly #define IN_MODULE_FLAG_EQ 2 // set this if you do your own EQ #define IN_MODULE_FLAG_REPLAYGAIN 8 // set this if you adjusted volume for replay gain // for tracks with no replay gain metadata, you should clear this flag // UNLESS you handle "non_replaygain" gain adjustment yourself #define IN_MODULE_FLAG_REPLAYGAIN_PREAMP 16 // use this if you queried for the replay gain preamp parameter and used it // this parameter is new to 5.54 typedef struct { int version; // module type (IN_VER) char *description; // description of module, with version string HWND hMainWindow; // winamp's main window (filled in by winamp) HINSTANCE hDllInstance; // DLL instance handle (Also filled in by winamp) char *FileExtensions; // "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0" // May be altered from Config, so the user can select what they want int is_seekable; // is this stream seekable? int UsesOutputPlug; // does this plug-in use the output plug-ins? (musn't ever change, ever :) // note that this has turned into a "flags" field // see IN_MODULE_FLAG_* void (*Config)(HWND hwndParent); // configuration dialog void (*About)(HWND hwndParent); // about dialog void (*Init)(); // called at program init void (*Quit)(); // called at program quit #define GETFILEINFO_TITLE_LENGTH 2048 void (*GetFileInfo)(const in_char *file, in_char *title, int *length_in_ms); // if file == NULL, current playing is used #define INFOBOX_EDITED 0 #define INFOBOX_UNCHANGED 1 int (*InfoBox)(const in_char *file, HWND hwndParent); int (*IsOurFile)(const in_char *fn); // called before extension checks, to allow detection of mms://, etc // playback stuff int (*Play)(const in_char *fn); // return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error void (*Pause)(); // pause stream void (*UnPause)(); // unpause stream int (*IsPaused)(); // ispaused? return 1 if paused, 0 if not void (*Stop)(); // stop (unload) stream // time stuff int (*GetLength)(); // get length in ms int (*GetOutputTime)(); // returns current output time in ms. (usually returns outMod->GetOutputTime() void (*SetOutputTime)(int time_in_ms); // seeks to point in stream (in ms). Usually you signal your thread to seek, which seeks and calls outMod->Flush().. // volume stuff void (*SetVolume)(int volume); // from 0 to 255.. usually just call outMod->SetVolume void (*SetPan)(int pan); // from -127 to 127.. usually just call outMod->SetPan // in-window builtin vis stuff void (*SAVSAInit)(int maxlatency_in_ms, int srate); // call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open() // call after opening audio device with max latency in ms and samplerate void (*SAVSADeInit)(); // call in Stop() // simple vis supplying mode void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the spec data directly from PCM data // quick and easy way to get vis working :) // needs at least 576 samples :) // advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff. int (*SAGetMode)(); // gets csa (the current type (4=ws,2=osc,1=spec)) // use when calling SAAdd() int (*SAAdd)(void *data, int timestamp, int csa); // sets the spec data, filled in by winamp // vis stuff (plug-in) // simple vis supplying mode void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data // quick and easy way to get vis working :) // needs at least 576 samples :) // advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff. int (*VSAGetMode)(int *specNch, int *waveNch); // use to figure out what to give to VSAAdd int (*VSAAdd)(void *data, int timestamp); // filled in by winamp, called by plug-in // call this in Play() to tell the vis plug-ins the current output params. void (*VSASetInfo)(int srate, int nch); // <-- Correct (benski, dec 2005).. old declaration had the params backwards // dsp plug-in processing: // (filled in by winamp, calld by input plug) // returns 1 if active (which means that the number of samples returned by dsp_dosamples // could be greater than went in.. Use it to estimate if you'll have enough room in the // output buffer int (*dsp_isactive)(); // returns number of samples to output. This can be as much as twice numsamples. // be sure to allocate enough buffer for samples, then. int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate); // eq stuff void (*EQSet)(int on, char data[10], int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore. // info setting (filled in by winamp) void (*SetInfo)(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :) Out_Module *outMod; // filled in by winamp, optionally used :) } In_Module; // return values from the winampUninstallPlugin(HINSTANCE hdll, HWND parent, int param) // which determine if we can uninstall the plugin immediately or on winamp restart // // uninstall support was added from 5.0+ and uninstall now support from 5.5+ // it is down to you to ensure that if uninstall now is returned that it will not cause a crash // (ie don't use if you've been subclassing the main window) #define IN_PLUGIN_UNINSTALL_NOW 0x1 #define IN_PLUGIN_UNINSTALL_REBOOT 0x0 #endif ================================================ FILE: in_vgm/Winamp/OUT.H ================================================ #ifndef NULLSOFT_OUTH #define NULLSOFT_OUTH #include #include // ids: // waveout: 32 // gapless: 64 // xfade: 63 // disk: 33 // dsound: 38 // NULL: 65 // mm2: 69 #if (_MSC_VER <= 1200) typedef int intptr_t; #endif #define OUT_VER 0x10 typedef struct { int version; // module version (OUT_VER) char *description; // description of module, with version string intptr_t id; // module id. each input module gets its own. non-nullsoft modules should // be >= 65536. HWND hMainWindow; // winamp's main window (filled in by winamp) HINSTANCE hDllInstance; // DLL instance handle (filled in by winamp) void (*Config)(HWND hwndParent); // configuration dialog void (*About)(HWND hwndParent); // about dialog void (*Init)(); // called when loaded void (*Quit)(); // called when unloaded int (*Open)(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms); // returns >=0 on success, <0 on failure // NOTENOTENOTE: bufferlenms and prebufferms are ignored in most if not all output plug-ins. // ... so don't expect the max latency returned to be what you asked for. // returns max latency in ms (0 for diskwriters, etc) // bufferlenms and prebufferms must be in ms. 0 to use defaults. // prebufferms must be <= bufferlenms // pass bufferlenms==-666 to tell the output plugin that it's clock is going to be used to sync video // out_ds turns off silence-eating when -666 is passed void (*Close)(); // close the ol' output device. int (*Write)(char *buf, int len); // 0 on success. Len == bytes to write (<= 8192 always). buf is straight audio data. // 1 returns not able to write (yet). Non-blocking, always. int (*CanWrite)(); // returns number of bytes possible to write at a given time. // Never will decrease unless you call Write (or Close, heh) int (*IsPlaying)(); // non0 if output is still going or if data in buffers waiting to be // written (i.e. closing while IsPlaying() returns 1 would truncate the song int (*Pause)(int pause); // returns previous pause state void (*SetVolume)(int volume); // volume is 0-255 void (*SetPan)(int pan); // pan is -128 to 128 void (*Flush)(int t); // flushes buffers and restarts output at time t (in ms) // (used for seeking) int (*GetOutputTime)(); // returns played time in MS int (*GetWrittenTime)(); // returns time written in MS (used for synching up vis stuff) } Out_Module; #endif ================================================ FILE: in_vgm/Winamp/ipc_pe.h ================================================ #ifndef __IPC_PE_H #define __IPC_PE_H #define IPC_PE_GETCURINDEX 100 // returns current idx #define IPC_PE_GETINDEXTOTAL 101 // returns number of items #define IPC_PE_GETINDEXINFO 102 // (copydata) lpData is of type callbackinfo, callback is called with copydata/fileinfo structure and msg IPC_PE_GETINDEXINFORESULT #define IPC_PE_GETINDEXINFORESULT 103 // callback message for IPC_PE_GETINDEXINFO #define IPC_PE_DELETEINDEX 104 // lParam = index #define IPC_PE_SWAPINDEX 105 // (lParam & 0xFFFF0000) >> 16 = from, (lParam & 0xFFFF) = to #define IPC_PE_INSERTFILENAME 106 // (copydata) lpData is of type fileinfo #define IPC_PE_GETDIRTY 107 // returns 1 if the playlist changed since the last IPC_PE_SETCLEAN #define IPC_PE_SETCLEAN 108 // resets the dirty flag until next modification #define IPC_PE_GETIDXFROMPOINT 109 // pass a point parm, return a playlist index #define IPC_PE_SAVEEND 110 // pass index to save from #define IPC_PE_RESTOREEND 111 // no parm #define IPC_PE_GETNEXTSELECTED 112 // same as IPC_PLAYLIST_GET_NEXT_SELECTED for the main window #define IPC_PE_GETSELECTEDCOUNT 113 #define IPC_PE_INSERTFILENAMEW 114 // (copydata) lpData is of type fileinfoW #define IPC_PE_GETINDEXINFO_TITLE 115 // like IPC_PE_GETINDEXINFO, but writes the title to char file[MAX_PATH] instead of filename #define IPC_PE_GETINDEXINFORESULT_TITLE 116 // callback message for IPC_PE_GETINDEXINFO typedef struct { char file[MAX_PATH]; int index; } fileinfo; typedef struct { wchar_t file[MAX_PATH]; int index; } fileinfoW; typedef struct { HWND callback; int index; } callbackinfo; // the following messages are in_process ONLY #define IPC_PE_GETINDEXTITLE 200 // lParam = pointer to fileinfo2 struct #define IPC_PE_GETINDEXTITLEW 201 // lParam = pointer to fileinfo2W struct #define IPC_PE_GETINDEXINFO_INPROC 202 // lParam = pointer to fileinfo struct #define IPC_PE_GETINDEXINFOW_INPROC 203 // lParam = pointer to fileinfoW struct typedef struct { int fileindex; char filetitle[256]; char filelength[16]; } fileinfo2; typedef struct { int fileindex; wchar_t filetitle[256]; wchar_t filelength[16]; } fileinfo2W; #endif ================================================ FILE: in_vgm/Winamp/wa_dlg.h ================================================ /* ** Copyright (C) 2003-2008 Nullsoft, Inc. ** ** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held ** liable for any damages arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to ** alter it and redistribute it freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. ** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. ** ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. ** ** 3. This notice may not be removed or altered from any source distribution. ** */ #ifndef _WA_DLG_H_ #define _WA_DLG_H_ #include "wa_ipc.h" #ifdef __cplusplus extern "C" { #endif /* 1) gen.bmp has a generic window frame for plugins to use. its format is similar to the minibrowser's. In addition gen.bmp includes a font for the titlebar, in both highlight and no-highlight modes. The font is variable width, and it uses the first color before the letter A as the delimiter. The no-highlight form of letter must be the same width as the highlight form. 2) genex.bmp has button and scrollbar images, as well as some individual pixels that describe the colors for the dialog. The button and scrollbar images should be self explanatory (note that the buttons have 4 pixel sized edges that are not stretched, and the center is stretched), and the scrollbars do something similar. The colors start at (48,0) and run every other pixel. The meaning of each pixel is: x=48: item background (background to edits, listviews etc) x=50: item foreground (text color of edit/listview, etc) x=52: window background (used to set the bg color for the dialog) x=54: button text color x=56: window text color x=58: color of dividers and sunken borders x=60: selection color for playlists x=62: listview header background color x=64: listview header text color x=66: listview header frame top color x=68: listview header frame middle color x=70: listview header frame bottom color x=72: listview header empty color x=74: scrollbar foreground color x=76: scrollbar background color x=78: inverse scrollbar foreground color x=80: inverse scrollbar background color x=82: scrollbar dead area color x=84: listview/treeview selection bar text color (active) x=86: listview/treeview selection bar back color (active) x=88: listview/treeview selection bar text color (inactive) x=90: listview/treeview selection bar back color (inactive) x=92: alternate item background x=94: alternate item foreground */ #define DCW_SUNKENBORDER 0x00010000 #define DCW_DIVIDER 0x00020000 enum { WADLG_ITEMBG, WADLG_ITEMFG, WADLG_WNDBG, WADLG_BUTTONFG, WADLG_WNDFG, WADLG_HILITE, WADLG_SELCOLOR, WADLG_LISTHEADER_BGCOLOR, WADLG_LISTHEADER_FONTCOLOR, WADLG_LISTHEADER_FRAME_TOPCOLOR, WADLG_LISTHEADER_FRAME_MIDDLECOLOR, WADLG_LISTHEADER_FRAME_BOTTOMCOLOR, WADLG_LISTHEADER_EMPTY_BGCOLOR, WADLG_SCROLLBAR_FGCOLOR, WADLG_SCROLLBAR_BGCOLOR, WADLG_SCROLLBAR_INV_FGCOLOR, WADLG_SCROLLBAR_INV_BGCOLOR, WADLG_SCROLLBAR_DEADAREA_COLOR, WADLG_SELBAR_FGCOLOR, WADLG_SELBAR_BGCOLOR, WADLG_INACT_SELBAR_FGCOLOR, WADLG_INACT_SELBAR_BGCOLOR, WADLG_ITEMBG2, WADLG_ITEMFG2, WADLG_NUM_COLORS }; typedef enum _WACURSOR // used in IPC_GETSKINCURSORS { WACURSOR_VOLUME = 0, // volume & balane WACURSOR_POSITION = 1, // position WACURSOR_BTN_WINSHADE = 2, // winshade WACURSOR_BTN_MINIMIZE = 3, // minimize WACURSOR_BTN_CLOSE = 4, // close WACURSOR_MENU = 5, // main menu WACURSOR_TITLEBAR = 6, // title bar WACURSOR_SONGNAME = 7, WACURSOR_NORMAL = 8, WACURSOR_WINSHADE_BTN_WINSHADE = 9, WACURSOR_WINSHADE_BTN_MINIMIZE = 10, WACURSOR_WINSHADE_POSITION = 11, WACURSOR_WINSHADE_BTN_CLOSE = 12, WACURSOR_WINSHADE_MENU = 13, WACURSOR_WINSHADE_NORMAL = 14, WACURSOR_PL_BTN_WINSHADE = 15, WACURSOR_PL_BTN_CLOSE = 16, WACURSOR_PL_TITLEBAR = 17, WACURSOR_PL_VSCROLL = 18, WACURSOR_PL_RESIZE = 19, WACURSOR_PL_NORMAL = 20, WACURSOR_PL_WINSHADE_BTN_WINSHADE = 21, WACURSOR_PL_WINSHADE_BTN_CLOSE = 22, WACURSOR_PL_WINSHADE_HSIZE = 23, WACURSOR_PL_WINSHADE_NORMAL = 24, WACURSOR_EQ_SLIDER = 25, WACURSOR_EQ_BTN_CLOSE = 26, WACURSOR_EQ_TITLEBAR = 27, WACURSOR_EQ_NORMAL = 28, } WACURSOR; void WADlg_init(HWND hwndWinamp); // call this on init, or on WM_DISPLAYCHANGE void WADlg_close(); int WADlg_getColor(int idx); int WADlg_initted(); LRESULT WADlg_handleDialogMsgs(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // void WADlg_DrawChildWindowBorders(HWND hwndDlg, int *tab, int tabsize); // each entry in tab would be the id | DCW_* HBITMAP WADlg_getBitmap(); /// define WA_DLG_IMPLEMENT in one of your source files before including this .h // if you are making a media library plugin, you dont need to do this, look at view_ex for // an example of how to get the function *'s via an IPC message. #ifdef __cplusplus } #endif #ifdef WA_DLG_IMPLEMENT static HBRUSH wadlg_lastbrush=0; static HBITMAP wadlg_bitmap=0; // load this manually static int wadlg_colors[WADLG_NUM_COLORS]; static int wadlg_defcolors[WADLG_NUM_COLORS]= { RGB(0,0,0), RGB(0,255,0), RGB(36,36,60), RGB(57,56,66), RGB(255,255,255), RGB(132,148,165), RGB(0,0,198), RGB(36*2,36*2,60*2), RGB(255,255,255), RGB(36*3,36*3,60*3), RGB(36,36,60), RGB(36*0.5,36*0.5,60*0.5), RGB(36,36,60), RGB(36*1,36*1,60*1), RGB(36*1,36*1,60*1), RGB(121,130,150), RGB(78,88,110), RGB(36*1,36*1,60*1), RGB(255,255,255), RGB(0,0,180), RGB(0,255,0), RGB(0,0,128), RGB(0,0,0), RGB(0,255,0), }; int WADlg_initted() { return !!wadlg_bitmap; } int WADlg_getColor(int idx) { if (idx < 0 || idx >= WADLG_NUM_COLORS) return 0; return wadlg_colors[idx]; } HBITMAP WADlg_getBitmap() { return wadlg_bitmap; } void WADlg_init(HWND hwndWinamp) // call this on init, or on WM_DISPLAYCHANGE { if (wadlg_bitmap) DeleteObject(wadlg_bitmap); wadlg_bitmap = (HBITMAP) SendMessage(hwndWinamp,WM_WA_IPC,0,IPC_GET_GENSKINBITMAP); if (wadlg_bitmap) { HDC tmpDC=CreateCompatibleDC(NULL); HGDIOBJ o=SelectObject(tmpDC,(HGDIOBJ)wadlg_bitmap); int defbgcol=GetPixel(tmpDC,111,0); for (int x = 0; x < WADLG_NUM_COLORS; x ++) { int a=GetPixel(tmpDC,48+x*2,0); if (a == CLR_INVALID || a == RGB(0,198,255) || a == defbgcol) { //defaults for old skins if (x == WADLG_SELBAR_FGCOLOR || x == WADLG_INACT_SELBAR_FGCOLOR) a=wadlg_colors[WADLG_WNDFG]; else if (x == WADLG_SELBAR_BGCOLOR || x == WADLG_INACT_SELBAR_BGCOLOR) { a=wadlg_colors[WADLG_SELCOLOR]; if (x == WADLG_INACT_SELBAR_BGCOLOR) a=((a/2)&0x7F7F7F)+(((wadlg_colors[WADLG_WNDBG])/2)&0x7F7F7F); } else if (x == WADLG_ITEMBG2) { a=wadlg_colors[WADLG_ITEMBG]; } else if (x == WADLG_ITEMFG2) { a=wadlg_colors[WADLG_ITEMFG]; } else a=wadlg_defcolors[x]; } wadlg_colors[x]=a; } SelectObject(tmpDC,o); DeleteDC(tmpDC); } } void WADlg_close() { if (wadlg_bitmap) DeleteObject(wadlg_bitmap); wadlg_bitmap=0; if (wadlg_lastbrush) DeleteObject(wadlg_lastbrush); wadlg_lastbrush=0; } void WADlg_dotLine(HDC hdc, int left, int top, int len, int vert) { for(int i=(top&1);iCtlType == ODT_BUTTON) { wchar_t wt[256]; RECT r; GetDlgItemTextW(hwndDlg,(INT)wParam,wt,sizeof(wt)/sizeof(*wt)); HDC hdc = CreateCompatibleDC(di->hDC); HBITMAP hbmpOld = (HBITMAP)SelectObject(hdc, wadlg_bitmap); r=di->rcItem; SetStretchBltMode(di->hDC,COLORONCOLOR); int yoffs = (di->itemState & ODS_SELECTED) ? 15 : 0; BitBlt(di->hDC,r.left,r.top,4,4,hdc,0,yoffs,SRCCOPY); // top left StretchBlt(di->hDC,r.left+4,r.top,r.right-r.left-4-4,4,hdc,4,yoffs,47-4-4,4,SRCCOPY); // top center BitBlt(di->hDC,r.right-4,r.top,4,4,hdc,47-4,yoffs,SRCCOPY); // top right StretchBlt(di->hDC,r.left,r.top+4,4,r.bottom-r.top-4-4,hdc,0,4+yoffs,4,15-4-4,SRCCOPY); // left edge StretchBlt(di->hDC,r.right-4,r.top+4,4,r.bottom-r.top-4-4,hdc,47-4,4+yoffs,4,15-4-4,SRCCOPY); // right edge // center StretchBlt(di->hDC,r.left+4,r.top+4,r.right-r.left-4-4,r.bottom-r.top-4-4,hdc,4,4+yoffs,47-4-4,15-4-4,SRCCOPY); BitBlt(di->hDC,r.left,r.bottom-4,4,4,hdc,0,15-4+yoffs,SRCCOPY); // bottom left StretchBlt(di->hDC,r.left+4,r.bottom-4,r.right-r.left-4-4,4,hdc,4,15-4+yoffs,47-4-4,4,SRCCOPY); // bottom center BitBlt(di->hDC,r.right-4,r.bottom-4,4,4,hdc,47-4,15-4+yoffs,SRCCOPY); // bottom right // draw text SetBkMode(di->hDC,TRANSPARENT); // this will do a different style for the button text depending on enabled state of the button COLORREF colour = wadlg_colors[WADLG_BUTTONFG]; if(!IsWindowEnabled(di->hwndItem)){ COLORREF fg = wadlg_colors[WADLG_WNDFG], bg = wadlg_colors[WADLG_WNDBG]; colour = RGB((GetRValue(fg)+GetRValue(bg))/2, (GetGValue(fg)+GetGValue(bg))/2, (GetBValue(fg)+GetBValue(bg))/2); } SetTextColor(di->hDC,colour); if (di->itemState & ODS_SELECTED) {r.left+=2; r.top+=2;} DrawTextW(di->hDC,wt,-1,&r,DT_VCENTER|DT_SINGLELINE|DT_CENTER); SelectObject(hdc, hbmpOld); DeleteDC(hdc); if(GetFocus()==di->hwndItem) { HPEN hpen, hpenOld; hpen =CreatePen(PS_SOLID,0,RGB(0,0,0)); hpenOld = (HPEN)SelectObject(di->hDC, hpen); WADlg_dotLine(di->hDC,r.left+2,r.top+2,r.right-r.left-3,0); WADlg_dotLine(di->hDC,r.right-3,r.top+2,r.bottom-r.top-3,1); WADlg_dotLine(di->hDC,r.left+2,r.top+2,r.bottom-r.top-3,1); WADlg_dotLine(di->hDC,r.left+2,r.bottom-3,r.right-r.left-3,0); SelectObject(di->hDC, hpenOld); DeleteObject(hpen); } } } switch(uMsg) { case WM_CTLCOLORLISTBOX: case WM_CTLCOLORDLG: case WM_CTLCOLORBTN: case WM_CTLCOLORSTATIC: case WM_CTLCOLOREDIT: { int bgcolor=(uMsg == WM_CTLCOLOREDIT || uMsg == WM_CTLCOLORLISTBOX) ? wadlg_colors[WADLG_ITEMBG] : (uMsg == WM_CTLCOLORBTN ? wadlg_colors[WADLG_ITEMBG] : wadlg_colors[WADLG_WNDBG]); LOGBRUSH lb={BS_SOLID,GetNearestColor((HDC)wParam,bgcolor)}; if (wadlg_lastbrush) DeleteObject(wadlg_lastbrush); wadlg_lastbrush=CreateBrushIndirect(&lb); SetTextColor((HDC)wParam,uMsg == WM_CTLCOLORSTATIC ? wadlg_colors[WADLG_WNDFG] : wadlg_colors[WADLG_ITEMFG]); SetBkColor((HDC)wParam,lb.lbColor); return (LRESULT)wadlg_lastbrush; } } return 0; } static int RectInRect(RECT *rect1, RECT *rect2) { // this has a bias towards true // this could probably be optimized a lot return ((rect1->top >= rect2->top && rect1->top <= rect2->bottom) || (rect1->bottom >= rect2->top && rect1->bottom <= rect2->bottom) || (rect2->top >= rect1->top && rect2->top <= rect1->bottom) || (rect2->bottom >= rect1->top && rect2->bottom <= rect1->bottom)) // vertical intersect && ((rect1->left >= rect2->left && rect1->left <= rect2->right) || (rect1->right >= rect2->left && rect1->right <= rect2->right) || (rect2->left >= rect1->left && rect2->left <= rect1->right) || (rect2->right >= rect1->left && rect2->right <= rect1->right)) // horiz intersect ; } static void WADlg_removeFromRgn(HRGN hrgn, int left, int top, int right, int bottom) { HRGN rgn2=CreateRectRgn(left,top,right,bottom); CombineRgn(hrgn,hrgn,rgn2,RGN_DIFF); DeleteObject(rgn2); } void WADlg_DrawChildWindowBorders(HWND hwndDlg, int *tab, int tabsize) { PAINTSTRUCT ps; BeginPaint(hwndDlg,&ps); HRGN hrgn = (ps.fErase) ? CreateRectRgnIndirect(&ps.rcPaint) : NULL; HPEN pen = CreatePen(PS_SOLID, 0, wadlg_colors[WADLG_HILITE]); HGDIOBJ o = SelectObject(ps.hdc, pen); while (tabsize--) { RECT r; int a = *tab++; GetWindowRect(GetDlgItem(hwndDlg, a & 0xffff),&r); MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&r, 2); if (RectInRect(&ps.rcPaint,&r)) { if ((a & 0xffff0000) == DCW_SUNKENBORDER) { MoveToEx(ps.hdc,r.left,r.bottom,NULL); LineTo(ps.hdc,r.right,r.bottom); LineTo(ps.hdc,r.right,r.top-1); if(hrgn) { WADlg_removeFromRgn(hrgn,r.left,r.bottom,r.right,r.bottom+1); WADlg_removeFromRgn(hrgn,r.right,r.top,r.right+1,r.bottom); } } else if ((a & 0xffff0000) == DCW_DIVIDER) { if (r.right - r.left < r.bottom - r.top) // vertical { int left=(r.left+r.right)/2; MoveToEx(ps.hdc,left,r.top,NULL); LineTo(ps.hdc,left,r.bottom+1); if(hrgn) WADlg_removeFromRgn(hrgn,left,r.top,left+1,r.bottom); } else // horiz { int top=(r.top+r.bottom)/2; MoveToEx(ps.hdc,r.left,top,NULL); LineTo(ps.hdc,r.right+1,top); if(hrgn) WADlg_removeFromRgn(hrgn,r.left,top,r.right,top+1); } } } } SelectObject(ps.hdc, o); DeleteObject(pen); if(hrgn) { //erase bkgnd while clipping out our own drawn stuff (for flickerless display) HBRUSH b = CreateSolidBrush(wadlg_colors[WADLG_WNDBG]); FillRgn(ps.hdc,hrgn,b); DeleteObject(b); DeleteObject(hrgn); } EndPaint(hwndDlg,&ps); } #endif #endif//_WA_DLG_H_ ================================================ FILE: in_vgm/Winamp/wa_ipc.h ================================================ /* ** Copyright (C) 1997-2008 Nullsoft, Inc. ** ** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held ** liable for any damages arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to ** alter it and redistribute it freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. ** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. ** ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. ** ** 3. This notice may not be removed or altered from any source distribution. ** */ #ifndef _WA_IPC_H_ #define _WA_IPC_H_ #include #include #if (_MSC_VER <= 1200) typedef int intptr_t; #endif /* ** This is the modern replacement for the classic 'frontend.h'. Most of these ** updates are designed for in-process use, i.e. from a plugin. ** */ /* Most of the IPC_* messages involve sending the message in the form of: ** result = SendMessage(hwnd_winamp,WM_WA_IPC,(parameter),IPC_*); ** Where different then this is specified (typically with WM_COPYDATA variants) ** ** When you use SendMessage(hwnd_winamp,WM_WA_IPC,(parameter),IPC_*) and specify a IPC_* ** which is not currently implemented/supported by the Winamp version being used then it ** will return 1 for 'result'. This is a good way of helping to check if an api being ** used which returns a function pointer, etc is even going to be valid. */ #define WM_WA_IPC WM_USER #define WINAMP_VERSION_MAJOR(winampVersion) ((winampVersion & 0x0000FF00) >> 12) #define WINAMP_VERSION_MINOR(winampVersion) (winampVersion & 0x000000FF) // returns, i.e. 0x12 for 5.12 and 0x10 for 5.1... #define IPC_GETVERSION 0 /* int version = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION); ** ** The version returned will be 0x20yx for Winamp 2.yx. ** Versions previous to Winamp 2.0 typically (but not always) use 0x1zyx for 1.zx. ** Just a bit weird but that's the way it goes. ** ** For Winamp 5.x it uses the format 0x50yx for Winamp 5.yx ** e.g. 5.01 -> 0x5001 ** 5.09 -> 0x5009 ** 5.1 -> 0x5010 ** ** Notes: For 5.02 this api will return the same value as for a 5.01 build. ** For 5.07 this api will return the same value as for a 5.06 build. */ #define IPC_GETVERSIONSTRING 1 #define IPC_GETREGISTEREDVERSION 770 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETREGISTEREDVERSION); ** ** This will open the Winamp Preferences and show the Winamp Pro page. */ typedef struct { const char *filename; const char *title; int length; } enqueueFileWithMetaStruct; // send this to a IPC_PLAYFILE in a non WM_COPYDATA, // and you get the nice desired result. if title is NULL, it is treated as a "thing", // otherwise it's assumed to be a file (for speed) typedef struct { const wchar_t *filename; const wchar_t *title; int length; } enqueueFileWithMetaStructW; #define IPC_PLAYFILE 100 // dont be fooled, this is really the same as enqueufile #define IPC_ENQUEUEFILE 100 #define IPC_PLAYFILEW 1100 #define IPC_ENQUEUEFILEW 1100 /* This is sent as a WM_COPYDATA with IPC_PLAYFILE as the dwData member and the string ** of the file / playlist to be enqueued into the playlist editor as the lpData member. ** This will just enqueue the file or files since you can use this to enqueue a playlist. ** It will not clear the current playlist or change the playback state. ** ** COPYDATASTRUCT cds = {0}; ** cds.dwData = IPC_ENQUEUEFILE; ** cds.lpData = (void*)"c:\\test\\folder\\test.mp3"; ** cds.cbData = lstrlen((char*)cds.lpData)+1; // include space for null char ** SendMessage(hwnd_winamp,WM_COPYDATA,0,(LPARAM)&cds); ** ** ** With 2.9+ and all of the 5.x versions you can send this as a normal WM_WA_IPC ** (non WM_COPYDATA) with an enqueueFileWithMetaStruct as the param. ** If the title member is null then it is treated as a "thing" otherwise it will be ** assumed to be a file (for speed). ** ** enqueueFileWithMetaStruct eFWMS = {0}; ** eFWMS.filename = "c:\\test\\folder\\test.mp3"; ** eFWMS.title = "Whipping Good"; ** eFWMS.length = 300; // this is the number of seconds for the track ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&eFWMS,IPC_ENQUEUEFILE); */ #define IPC_DELETE 101 #define IPC_DELETE_INT 1101 /* SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE); ** Use this api to clear Winamp's internal playlist. ** You should not need to use IPC_DELETE_INT since it is used internally by Winamp when ** it is dealing with some lame Windows Explorer issues (hard to believe that!). */ #define IPC_STARTPLAY 102 #define IPC_STARTPLAY_INT 1102 /* SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_STARTPLAY); ** Sending this will start playback and is almost the same as hitting the play button. ** The IPC_STARTPLAY_INT version is used internally and you should not need to use it ** since it won't be any fun. */ #define IPC_CHDIR 103 /* This is sent as a WM_COPYDATA type message with IPC_CHDIR as the dwData value and the ** directory you want to change to as the lpData member. ** ** COPYDATASTRUCT cds = {0}; ** cds.dwData = IPC_CHDIR; ** cds.lpData = (void*)"c:\\download"; ** cds.cbData = lstrlen((char*)cds.lpData)+1; // include space for null char ** SendMessage(hwnd_winamp,WM_COPYDATA,0,(LPARAM)&cds); ** ** The above example will make Winamp change to the directory 'C:\download'. */ #define IPC_ISPLAYING 104 /* int res = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING); ** This is sent to retrieve the current playback state of Winamp. ** If it returns 1, Winamp is playing. ** If it returns 3, Winamp is paused. ** If it returns 0, Winamp is not playing. */ #define IPC_GETOUTPUTTIME 105 /* int res = SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETOUTPUTTIME); ** This api can return two different sets of information about current playback status. ** ** If mode = 0 then it will return the position (in ms) of the currently playing track. ** Will return -1 if Winamp is not playing. ** ** If mode = 1 then it will return the current track length (in seconds). ** Will return -1 if there are no tracks (or possibly if Winamp cannot get the length). ** ** If mode = 2 then it will return the current track length (in milliseconds). ** Will return -1 if there are no tracks (or possibly if Winamp cannot get the length). */ #define IPC_JUMPTOTIME 106 /* (requires Winamp 1.60+) ** SendMessage(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME); ** This api sets the current position (in milliseconds) for the currently playing song. ** The resulting playback position may only be an approximate time since some playback ** formats do not provide exact seeking e.g. mp3 ** This returns -1 if Winamp is not playing, 1 on end of file, or 0 if it was successful. */ #define IPC_GETMODULENAME 109 #define IPC_EX_ISRIGHTEXE 666 /* usually shouldnt bother using these, but here goes: ** send a WM_COPYDATA with IPC_GETMODULENAME, and an internal ** flag gets set, which if you send a normal WM_WA_IPC message with ** IPC_EX_ISRIGHTEXE, it returns whether or not that filename ** matches. lame, I know. */ #define IPC_WRITEPLAYLIST 120 /* (requires Winamp 1.666+) ** int cur = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_WRITEPLAYLIST); ** ** IPC_WRITEPLAYLIST will write the current playlist to '\\Winamp.m3u' and ** will also return the current playlist position (see IPC_GETLISTPOS). ** ** This is kinda obsoleted by some of the newer 2.x api items but it still is good for ** use with a front-end program (instead of a plug-in) and you want to see what is in the ** current playlist. ** ** This api will only save out extended file information in the #EXTINF entry if Winamp ** has already read the data such as if the file was played of scrolled into view. If ** Winamp has not read the data then you will only find the file with its filepath entry ** (as is the base requirements for a m3u playlist). */ #define IPC_SETPLAYLISTPOS 121 /* (requires Winamp 2.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,position,IPC_SETPLAYLISTPOS) ** IPC_SETPLAYLISTPOS sets the playlist position to the specified 'position'. ** It will not change playback status or anything else. It will just set the current ** position in the playlist and will update the playlist view if necessary. ** ** If you use SendMessage(hwnd_winamp,WM_COMMAND,MAKEWPARAM(WINAMP_BUTTON2,0),0); ** after using IPC_SETPLAYLISTPOS then Winamp will start playing the file at 'position'. */ #define IPC_SETVOLUME 122 /* (requires Winamp 2.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,volume,IPC_SETVOLUME); ** IPC_SETVOLUME sets the volume of Winamp (between the range of 0 to 255). ** ** If you pass 'volume' as -666 then the message will return the current volume. ** int curvol = SendMessage(hwnd_winamp,WM_WA_IPC,-666,IPC_SETVOLUME); */ #define IPC_GETVOLUME(hwnd_winamp) SendMessage(hwnd_winamp,WM_WA_IPC,-666,IPC_SETVOLUME) /* (requires Winamp 2.0+) ** int curvol = IPC_GETVOLUME(hwnd_winamp); ** This will return the current volume of Winamp or */ #define IPC_SETPANNING 123 /* (requires Winamp 2.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,panning,IPC_SETPANNING); ** IPC_SETPANNING sets the panning of Winamp from 0 (left) to 255 (right). ** ** At least in 5.x+ this works from -127 (left) to 127 (right). ** ** If you pass 'panning' as -666 to this api then it will return the current panning. ** int curpan = SendMessage(hwnd_winamp,WM_WA_IPC,-666,IPC_SETPANNING); */ #define IPC_GETLISTLENGTH 124 /* (requires Winamp 2.0+) ** int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH); ** IPC_GETLISTLENGTH returns the length of the current playlist as the number of tracks. */ #define IPC_GETLISTPOS 125 /* (requires Winamp 2.05+) ** int pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS); ** IPC_GETLISTPOS returns the current playlist position (which is shown in the playlist ** editor as a differently coloured text entry e.g is yellow for the classic skin). ** ** This api is a lot like IPC_WRITEPLAYLIST but a lot faster since it does not have to ** write out the whole of the current playlist first. */ #define IPC_GETINFO 126 /* (requires Winamp 2.05+) ** int inf=SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETINFO); ** IPC_GETINFO returns info about the current playing song. The value ** it returns depends on the value of 'mode'. ** Mode Meaning ** ------------------ ** 0 Samplerate, in kilohertz (i.e. 44) ** 1 Bitrate (i.e. 128) ** 2 Channels (i.e. 2) ** 3 (5+) Video LOWORD=w HIWORD=h ** 4 (5+) > 65536, string (video description) ** 5 (5.25+) Samplerate, in hertz (i.e. 44100) */ #define IPC_GETEQDATA 127 /* (requires Winamp 2.05+) ** int data=SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA); ** IPC_GETEQDATA queries the status of the EQ. ** The value returned depends on what 'pos' is set to: ** Value Meaning ** ------------------ ** 0-9 The 10 bands of EQ data. 0-63 (+20db - -20db) ** 10 The preamp value. 0-63 (+20db - -20db) ** 11 Enabled. zero if disabled, nonzero if enabled. ** 12 Autoload. zero if disabled, nonzero if enabled. */ #define IPC_SETEQDATA 128 /* (requires Winamp 2.05+) ** SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA); ** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SETEQDATA); ** IPC_SETEQDATA sets the value of the last position retrieved ** by IPC_GETEQDATA. This is pretty lame, and we should provide ** an extended version that lets you do a MAKELPARAM(pos,value). ** someday... new (2.92+): if the high byte is set to 0xDB, then the third byte specifies which band, and the bottom word specifies the value. */ #define IPC_ADDBOOKMARK 129 #define IPC_ADDBOOKMARKW 131 /* (requires Winamp 2.4+) ** This is sent as a WM_COPYDATA using IPC_ADDBOOKMARK as the dwData value and the ** directory you want to change to as the lpData member. This will add the specified ** file / url to the Winamp bookmark list. ** ** COPYDATASTRUCT cds = {0}; ** cds.dwData = IPC_ADDBOOKMARK; ** cds.lpData = (void*)"http://www.blah.com/listen.pls"; ** cds.cbData = lstrlen((char*)cds.lpData)+1; // include space for null char ** SendMessage(hwnd_winamp,WM_COPYDATA,0,(LPARAM)&cds); ** ** ** In Winamp 5.0+ we use this as a normal WM_WA_IPC and the string is null separated as ** the filename and then the title of the entry. ** ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(char*)"filename\0title\0",IPC_ADDBOOKMARK); ** ** This will notify the library / bookmark editor that a bookmark was added. ** Note that using this message in this context does not actually add the bookmark. ** Do not use, it is essentially just a notification type message :) */ #define IPC_INSTALLPLUGIN 130 /* This is not implemented (and is very unlikely to be done due to safety concerns). ** If it was then you could do a WM_COPYDATA with a path to a .wpz and it would then ** install the plugin for you. ** ** COPYDATASTRUCT cds = {0}; ** cds.dwData = IPC_INSTALLPLUGIN; ** cds.lpData = (void*)"c:\\path\\to\\file.wpz"; ** cds.cbData = lstrlen((char*)cds.lpData)+1; // include space for null char ** SendMessage(hwnd_winamp,WM_COPYDATA,0,(LPARAM)&cds); */ #define IPC_RESTARTWINAMP 135 /* (requires Winamp 2.2+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP); ** IPC_RESTARTWINAMP will restart Winamp (isn't that obvious ? :) ) ** If this fails to make Winamp start after closing then there is a good chance one (or ** more) of the currently installed plugins caused Winamp to crash on exit (either as a ** silent crash or a full crash log report before it could call itself start again. */ #define IPC_ISFULLSTOP 400 /* (requires winamp 2.7+ I think) ** int ret=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISFULLSTOP); ** This is useful for when you're an output plugin and you want to see if the stop/close ** happening is a full stop or if you are just between tracks. This returns non zero if ** it is a full stop or zero if it is just a new track. ** benski> i think it's actually the other way around - ** !0 for EOF and 0 for user pressing stop */ #define IPC_INETAVAILABLE 242 /* (requires Winamp 2.05+) ** int val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INETAVAILABLE); ** IPC_INETAVAILABLE will return 1 if an Internet connection is available for Winamp and ** relates to the internet connection type setting on the main general preferences page ** in the Winamp preferences. */ #define IPC_UPDTITLE 243 /* (requires Winamp 2.2+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_UPDTITLE); ** IPC_UPDTITLE will ask Winamp to update the information about the current title and ** causes GetFileInfo(..) in the input plugin associated with the current playlist entry ** to be called. This can be called such as when an input plugin is buffering a file so ** that it can cause the buffer percentage to appear in the playlist. */ #define IPC_REFRESHPLCACHE 247 /* (requires Winamp 2.2+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REFRESHPLCACHE); ** IPC_REFRESHPLCACHE will flush the playlist cache buffer and you send this if you want ** Winamp to go refetch the titles for all of the entries in the current playlist. ** ** 5.3+: pass a wchar_t * string in wParam, and it'll do a strnicmp() before clearing the cache */ #define IPC_GET_SHUFFLE 250 /* (requires Winamp 2.4+) ** int val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE); ** IPC_GET_SHUFFLE returns the status of the shuffle option. ** If set then it will return 1 and if not set then it will return 0. */ #define IPC_GET_REPEAT 251 /* (requires Winamp 2.4+) ** int val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT); ** IPC_GET_REPEAT returns the status of the repeat option. ** If set then it will return 1 and if not set then it will return 0. */ #define IPC_SET_SHUFFLE 252 /* (requires Winamp 2.4+) ** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_SHUFFLE); ** IPC_SET_SHUFFLE sets the status of the shuffle option. ** If 'value' is 1 then shuffle is turned on. ** If 'value' is 0 then shuffle is turned off. */ #define IPC_SET_REPEAT 253 /* (requires Winamp 2.4+) ** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_REPEAT); ** IPC_SET_REPEAT sets the status of the repeat option. ** If 'value' is 1 then shuffle is turned on. ** If 'value' is 0 then shuffle is turned off. */ #define IPC_ENABLEDISABLE_ALL_WINDOWS 259 // 0xdeadbeef to disable /* (requires Winamp 2.9+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(enable?0:0xdeadbeef),IPC_ENABLEDISABLE_ALL_WINDOWS); ** Sending this message with 0xdeadbeef as the param will disable all winamp windows and ** any other values will enable all of the Winamp windows again. When disabled you won't ** get any response on clicking or trying to do anything to the Winamp windows. If the ** taskbar icon is shown then you may still have control ;) */ #define IPC_GETWND 260 /* (requires Winamp 2.9+) ** HWND h=SendMessage(hwnd_winamp,WM_WA_IPC,IPC_GETWND_xxx,IPC_GETWND); ** returns the HWND of the window specified. */ #define IPC_GETWND_EQ 0 // use one of these for the param #define IPC_GETWND_PE 1 #define IPC_GETWND_MB 2 #define IPC_GETWND_VIDEO 3 #define IPC_ISWNDVISIBLE 261 // same param as IPC_GETWND /************************************************************************ ***************** in-process only (WE LOVE PLUGINS) ************************************************************************/ #define IPC_SETSKINW 199 #define IPC_SETSKIN 200 /* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"skinname",IPC_SETSKIN); ** IPC_SETSKIN sets the current skin to "skinname". Note that skinname ** can be the name of a skin, a skin .zip file, with or without path. ** If path isn't specified, the default search path is the winamp skins ** directory. */ #define IPC_GETSKIN 201 #define IPC_GETSKINW 1201 /* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)skinname_buffer,IPC_GETSKIN); ** IPC_GETSKIN puts the directory where skin bitmaps can be found ** into skinname_buffer. ** skinname_buffer must be MAX_PATH characters in length. ** When using a .zip'd skin file, it'll return a temporary directory ** where the ZIP was decompressed. */ #define IPC_EXECPLUG 202 /* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"vis_file.dll",IPC_EXECPLUG); ** IPC_EXECPLUG executes a visualization plug-in pointed to by WPARAM. ** the format of this string can be: ** "vis_whatever.dll" ** "vis_whatever.dll,0" // (first mod, file in winamp plug-in dir) ** "C:\\dir\\vis_whatever.dll,1" */ #define IPC_GETPLAYLISTFILE 211 #define IPC_GETPLAYLISTFILEW 214 /* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) ** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE); ** IPC_GETPLAYLISTFILE gets the filename of the playlist entry [index]. ** returns a pointer to it. returns NULL on error. */ #define IPC_GETPLAYLISTTITLE 212 #define IPC_GETPLAYLISTTITLEW 213 /* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) ** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE); ** ** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index]. ** returns a pointer to it. returns NULL on error. */ #define IPC_GETHTTPGETTER 240 /* retrieves a function pointer to a HTTP retrieval function. ** if this is unsupported, returns 1 or 0. ** the function should be: ** int (*httpRetrieveFile)(HWND hwnd, char *url, char *file, char *dlgtitle); ** if you call this function, with a parent window, a URL, an output file, and a dialog title, ** it will return 0 on successful download, 1 on error. */ #define IPC_GETHTTPGETTERW 1240 /* int (*httpRetrieveFileW)(HWND hwnd, char *url, wchar_t *file, wchar_t *dlgtitle); */ #define IPC_MBOPEN 241 /* (requires Winamp 2.05+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_MBOPEN); ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPEN); ** IPC_MBOPEN will open a new URL in the minibrowser. if url is NULL, it will open the Minibrowser window. */ #define IPC_CHANGECURRENTFILE 245 /* (requires Winamp 2.05+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILE); ** IPC_CHANGECURRENTFILE will set the current playlist item. */ #define IPC_CHANGECURRENTFILEW 1245 /* (requires Winamp 5.3+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILEW); ** IPC_CHANGECURRENTFILEW will set the current playlist item. */ #define IPC_GETMBURL 246 /* (requires Winamp 2.2+) ** char buffer[4096]; // Urls can be VERY long ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)buffer,IPC_GETMBURL); ** IPC_GETMBURL will retrieve the current Minibrowser URL into buffer. ** buffer must be at least 4096 bytes long. */ #define IPC_MBBLOCK 248 /* (requires Winamp 2.4+) ** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_MBBLOCK); ** ** IPC_MBBLOCK will block the Minibrowser from updates if value is set to 1 */ #define IPC_MBOPENREAL 249 /* (requires Winamp 2.4+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPENREAL); ** ** IPC_MBOPENREAL works the same as IPC_MBOPEN except that it will works even if ** IPC_MBBLOCK has been set to 1 */ #define IPC_ADJUST_OPTIONSMENUPOS 280 /* (requires Winamp 2.9+) ** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_OPTIONSMENUPOS); ** moves where winamp expects the Options menu in the main menu. Useful if you wish to insert a ** menu item above the options/skins/vis menus. */ #define IPC_GET_HMENU 281 /* (requires Winamp 2.9+) ** HMENU hMenu=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU); ** values for data: ** 0 : main popup menu ** 1 : main menubar file menu ** 2 : main menubar options menu ** 3 : main menubar windows menu ** 4 : main menubar help menu ** other values will return NULL. */ #define IPC_GET_EXTENDED_FILE_INFO 290 //pass a pointer to the following struct in wParam #define IPC_GET_EXTENDED_FILE_INFO_HOOKABLE 296 /* (requires Winamp 2.9+) ** to use, create an extendedFileInfoStruct, point the values filename and metadata to the ** filename and metadata field you wish to query, and ret to a buffer, with retlen to the ** length of that buffer, and then SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_GET_EXTENDED_FILE_INFO); ** the results should be in the buffer pointed to by ret. ** returns 1 if the decoder supports a getExtendedFileInfo method */ typedef struct { const char *filename; const char *metadata; char *ret; size_t retlen; } extendedFileInfoStruct; #define IPC_GET_BASIC_FILE_INFO 291 //pass a pointer to the following struct in wParam typedef struct { const char *filename; int quickCheck; // set to 0 to always get, 1 for quick, 2 for default (if 2, quickCheck will be set to 0 if quick wasnot used) // filled in by winamp int length; char *title; int titlelen; } basicFileInfoStruct; #define IPC_GET_BASIC_FILE_INFOW 1291 //pass a pointer to the following struct in wParam typedef struct { const wchar_t *filename; int quickCheck; // set to 0 to always get, 1 for quick, 2 for default (if 2, quickCheck will be set to 0 if quick wasnot used) // filled in by winamp int length; wchar_t *title; int titlelen; } basicFileInfoStructW; #define IPC_GET_EXTLIST 292 //returns doublenull delimited. GlobalFree() it when done. if data is 0, returns raw extlist, if 1, returns something suitable for getopenfilename #define IPC_GET_EXTLISTW 1292 // wide char version of above #define IPC_INFOBOX 293 typedef struct { HWND parent; char *filename; } infoBoxParam; #define IPC_INFOBOXW 1293 typedef struct { HWND parent; const wchar_t *filename; } infoBoxParamW; #define IPC_SET_EXTENDED_FILE_INFO 294 //pass a pointer to the a extendedFileInfoStruct in wParam /* (requires Winamp 2.9+) ** to use, create an extendedFileInfoStruct, point the values filename and metadata to the ** filename and metadata field you wish to write in ret. (retlen is not used). and then ** SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_SET_EXTENDED_FILE_INFO); ** returns 1 if the metadata is supported ** Call IPC_WRITE_EXTENDED_FILE_INFO once you're done setting all the metadata you want to update */ #define IPC_WRITE_EXTENDED_FILE_INFO 295 /* (requires Winamp 2.9+) ** writes all the metadata set thru IPC_SET_EXTENDED_FILE_INFO to the file ** returns 1 if the file has been successfully updated, 0 if error */ #define IPC_FORMAT_TITLE 297 typedef struct { char *spec; // NULL=default winamp spec void *p; char *out; int out_len; char * (*TAGFUNC)(const char * tag, void * p); //return 0 if not found void (*TAGFREEFUNC)(char * tag,void * p); } waFormatTitle; #define IPC_FORMAT_TITLE_EXTENDED 298 // similiar to IPC_FORMAT_TITLE, but falls back to Winamp's %tags% if your passed tag function doesn't handle it typedef struct { const wchar_t *filename; int useExtendedInfo; // set to 1 if you want the Title Formatter to query the input plugins for any tags that your tag function fails on const wchar_t *spec; // NULL=default winamp spec void *p; wchar_t *out; int out_len; wchar_t * (*TAGFUNC)(const wchar_t * tag, void * p); //return 0 if not found, -1 for empty tag void (*TAGFREEFUNC)(wchar_t *tag, void *p); } waFormatTitleExtended; #define IPC_COPY_EXTENDED_FILE_INFO 299 typedef struct { const char *source; const char *dest; } copyFileInfoStruct; #define IPC_COPY_EXTENDED_FILE_INFOW 1299 typedef struct { const wchar_t *source; const wchar_t *dest; } copyFileInfoStructW; typedef struct { int (*inflateReset)(void *strm); int (*inflateInit_)(void *strm,const char *version, int stream_size); int (*inflate)(void *strm, int flush); int (*inflateEnd)(void *strm); unsigned long (*crc32)(unsigned long crc, const unsigned char *buf, unsigned int len); } wa_inflate_struct; #define IPC_GETUNCOMPRESSINTERFACE 331 /* returns a function pointer to uncompress(). ** int (*uncompress)(unsigned char *dest, unsigned long *destLen, const unsigned char *source, unsigned long sourceLen); ** right out of zlib, useful for decompressing zlibbed data. ** if you pass the parm of 0x10100000, it will return a wa_inflate_struct * to an inflate API. */ typedef struct _prefsDlgRec { HINSTANCE hInst; // dll instance containing the dialog resource int dlgID; // resource identifier of the dialog void *proc; // window proceedure for handling the dialog defined as // LRESULT CALLBACK PrefsPage(HWND,UINT,WPARAM,LPARAM) char *name; // name shown for the prefs page in the treelist intptr_t where; // section in the treelist the prefs page is to be added to // 0 for General Preferences // 1 for Plugins // 2 for Skins // 3 for Bookmarks (no longer in the 5.0+ prefs) // 4 for Prefs (the old 'Setup' section - no longer in 5.0+) intptr_t _id; struct _prefsDlgRec *next; // no longer implemented as a linked list, now used by Winamp for other means } prefsDlgRec; typedef struct _prefsDlgRecW { HINSTANCE hInst; // dll instance containing the dialog resource int dlgID; // resource identifier of the dialog void *proc; // window proceedure for handling the dialog defined as // LRESULT CALLBACK PrefsPage(HWND,UINT,WPARAM,LPARAM) wchar_t *name; // name shown for the prefs page in the treelist intptr_t where; // section in the treelist the prefs page is to be added to // 0 for General Preferences // 1 for Plugins // 2 for Skins // 3 for Bookmarks (no longer in the 5.0+ prefs) // 4 for Prefs (the old 'Setup' section - no longer in 5.0+) intptr_t _id; struct _prefsDlgRec *next; // no longer implemented as a linked list, now used by Winamp for other means } prefsDlgRecW; #define IPC_ADD_PREFS_DLG 332 #define IPC_ADD_PREFS_DLGW 1332 #define IPC_REMOVE_PREFS_DLG 333 /* (requires Winamp 2.9+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_ADD_PREFS_DLG); ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_REMOVE_PREFS_DLG); ** ** IPC_ADD_PREFS_DLG: ** To use this you need to allocate a prefsDlgRec structure (either on the heap or with ** some global data but NOT on the stack) and then initialise the members of the structure ** (see the definition of the prefsDlgRec structure above). ** ** hInst - dll instance of where the dialog resource is located. ** dlgID - id of the dialog resource. ** proc - dialog window procedure for the prefs dialog. ** name - name of the prefs page as shown in the preferences list. ** where - see above for the valid locations the page can be added. ** ** Then you do SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_ADD_PREFS_DLG); ** ** example: ** ** prefsDlgRec* prefsRec = 0; ** prefsRec = GlobalAlloc(GPTR,sizeof(prefsDlgRec)); ** prefsRec->hInst = hInst; ** prefsRec->dlgID = IDD_PREFDIALOG; ** prefsRec->name = "Pref Page"; ** prefsRec->where = 0; ** prefsRec->proc = PrefsPage; ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_ADD_PREFS_DLG); ** ** ** IPC_REMOVE_PREFS_DLG: ** To use you pass the address of the same prefsRec you used when adding the prefs page ** though you shouldn't really ever have to do this but it's good to clean up after you ** when you're plugin is being unloaded. ** ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_REMOVE_PREFS_DLG); ** ** IPC_ADD_PREFS_DLGW ** requires Winamp 5.53+ */ #define IPC_OPENPREFSTOPAGE 380 /* SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&prefsRec,IPC_OPENPREFSTOPAGE); ** ** There are two ways of opening a preferences page. ** ** The first is to pass an id of a builtin preferences page (see below for ids) or a ** &prefsDlgRec of the preferences page to open and this is normally done if you are ** opening a prefs page you added yourself. ** ** If the page id does not or the &prefsRec is not valid then the prefs dialog will be ** opened to the first page available (usually the Winamp Pro page). ** ** (requires Winamp 5.04+) ** Passing -1 for param will open the preferences dialog to the last page viewed. ** ** Note: v5.0 to 5.03 had a bug in this api ** ** On the first call then the correct prefs page would be opened to but on the next call ** the prefs dialog would be brought to the front but the page would not be changed to the ** specified. ** In 5.04+ it will change to the prefs page specified if the prefs dialog is already open. */ /* Builtin Preference page ids (valid for 5.0+) ** (stored in the lParam member of the TVITEM structure from the tree item) ** ** These can be useful if you want to detect a specific prefs page and add things to it ** yourself or something like that ;) ** ** Winamp Pro 20 ** General Preferences 0 ** File Types 1 ** Playlist 23 ** Titles 21 ** Playback 42 (added in 5.25) ** Station Info 41 (added in 5.11 & removed in 5.5) ** Video 24 ** Localization 25 (added in 5.5) ** Skins 40 ** Classic Skins 22 ** Plugins 30 ** Input 31 ** Output 32 ** Visualisation 33 ** DSP/Effect 34 ** General Purpose 35 ** ** Note: ** Custom page ids begin from 60 ** The value of the normal custom pages (Global Hotkeys, Jump To File, etc) is not ** guaranteed since it depends on the order in which the plugins are loaded which can ** change on different systems. ** ** Global Hotkeys, Jump To File, Media Library (under General Preferences and child pages), ** Media Library (under Plugins), Portables, CD Ripping and Modern Skins are custom pages ** created by the plugins shipped with Winamp. */ #define IPC_GETINIFILE 334 /* (requires Winamp 2.9+) ** char *ini=(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETINIFILE); ** This returns a pointer to the full file path of winamp.ini. ** ** char ini_path[MAX_PATH] = {0}; ** ** void GetIniFilePath(HWND hwnd){ ** if(SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION) >= 0x2900){ ** // this gets the string of the full ini file path ** lstrcpyn(ini_path,(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETINIFILE),sizeof(ini_path)); ** } ** else{ ** char* p = ini_path; ** p += GetModuleFileName(0,ini_path,sizeof(ini_path)) - 1; ** while(p && *p != '.'){p--;} ** lstrcpyn(p+1,"ini",sizeof(ini_path)); ** } ** } */ #define IPC_GETINIDIRECTORY 335 /* (requires Winamp 2.9+) ** char *dir=(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETINIDIRECTORY); ** This returns a pointer to the directory where winamp.ini can be found and is ** useful if you want store config files but you don't want to use winamp.ini. */ #define IPC_GETPLUGINDIRECTORY 336 /* (requires Winamp 5.11+) ** char *plugdir=(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETPLUGINDIRECTORY); ** This returns a pointer to the directory where Winamp has its plugins stored and is ** useful if you want store config files in plugins.ini in the plugins folder or for ** accessing any local files in the plugins folder. */ #define IPC_GETM3UDIRECTORY 337 /* (requires Winamp 5.11+) ** char *m3udir=(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETM3UDIRECTORY); ** This returns a pointer to the directory where winamp.m3u (and winamp.m3u8 if supported) is stored in. */ #define IPC_GETM3UDIRECTORYW 338 /* (requires Winamp 5.3+) ** wchar_t *m3udirW=(wchar_t*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETM3UDIRECTORYW); ** This returns a pointer to the directory where winamp.m3u (and winamp.m3u8 if supported) is stored in. */ #define IPC_SPAWNBUTTONPOPUP 361 // param = // 0 = eject // 1 = previous // 2 = next // 3 = pause // 4 = play // 5 = stop #define IPC_OPENURLBOX 360 /* (requires Winamp 5.0+) ** HGLOBAL hglobal = (HGLOBAL)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)parent,IPC_OPENURLBOX); ** You pass a hwnd for the dialog to be parented to (which modern skin support uses). ** This will return a HGLOBAL that needs to be freed with GlobalFree() if this worked. */ #define IPC_OPENFILEBOX 362 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)parent,IPC_OPENFILEBOX); ** You pass a hwnd for the dialog to be parented to (which modern skin support uses). */ #define IPC_OPENDIRBOX 363 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)parent,IPC_OPENDIRBOX); ** You pass a hwnd for the dialog to be parented to (which modern skin support uses). */ #define IPC_SETDIALOGBOXPARENT 364 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)parent,IPC_SETDIALOGBOXPARENT); ** Pass 'parent' as the window which will be used as the parent for a number of the built ** in Winamp dialogs and is useful when you are taking over the whole of the UI so that ** the dialogs will not appear at the bottom right of the screen since the main winamp ** window is located at 3000x3000 by gen_ff when this is used. Call this again with ** parent = null to reset the parent back to the orginal Winamp window. */ #define IPC_GETDIALOGBOXPARENT 365 /* (requires Winamp 5.51+) ** HWND hwndParent = SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)0, IPC_GETDIALOGBOXPARENT); ** hwndParent can/must be passed to all modal dialogs (including MessageBox) thats uses winamp as a parent */ #define IPC_UPDATEDIALOGBOXPARENT 366 /* (requires Winamp 5.53+) ** if you previous called IPC_SETDIALOGBOXPARENT, call this every time your window resizes */ #define IPC_DRO_MIN 401 // reserved for DrO #define IPC_SET_JTF_COMPARATOR 409 /* pass me an int (__cdecl *)(const char *, const char *) in wParam */ #define IPC_SET_JTF_COMPARATOR_W 410 /* pass me an int (__cdecl *)(const wchar_t *, const wchar_t *) in wParam ... maybe someday :) */ #define IPC_SET_JTF_DRAWTEXT 416 #define IPC_DRO_MAX 499 // pass 0 for a copy of the skin HBITMAP // pass 1 for name of font to use for playlist editor likeness // pass 2 for font charset // pass 3 for font size #define IPC_GET_GENSKINBITMAP 503 typedef struct { HWND me; //hwnd of the window #define EMBED_FLAGS_NORESIZE 0x1 // set this bit to keep window from being resizable #define EMBED_FLAGS_NOTRANSPARENCY 0x2 // set this bit to make gen_ff turn transparency off for this window #define EMBED_FLAGS_NOWINDOWMENU 0x4 // set this bit to prevent gen_ff from automatically adding your window to the right-click menu #define EMBED_FLAGS_GUID 0x8 // (5.31+) call SET_EMBED_GUID(yourEmbedWindowStateStruct, GUID) to define a GUID for this window #define SET_EMBED_GUID(windowState, windowGUID) { windowState->flags |= EMBED_FLAGS_GUID; *((GUID *)&windowState->extra_data[4])=windowGUID; } #define GET_EMBED_GUID(windowState) (*((GUID *)&windowState->extra_data[4])) int flags; // see above RECT r; void *user_ptr; // for application use int extra_data[64]; // for internal winamp use } embedWindowState; #define IPC_GET_EMBEDIF 505 /* (requires Winamp 2.9+) ** HWND myframe = (HWND)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&wa_wnd,IPC_GET_EMBEDIF); ** ** or ** ** HWND myframe = 0; ** HWND (*embed)(embedWindowState *params)=0; ** *(void**)&embed = (void*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_EMBEDIF); ** myframe = embed(&wa_wnd); ** ** You pass an embedWindowState* and it will return a hwnd for the frame window or if you ** pass wParam as null then it will return a HWND embedWindow(embedWindowState *); */ #define IPC_SKINWINDOW 534 typedef struct __SKINWINDOWPARAM { HWND hwndToSkin; GUID windowGuid; } SKINWINDOWPARAM; #define IPC_EMBED_ENUM 532 typedef struct embedEnumStruct { int (*enumProc)(embedWindowState *ws, struct embedEnumStruct *param); // return 1 to abort int user_data; // or more :) } embedEnumStruct; // pass #define IPC_EMBED_ISVALID 533 /* (requires Winamp 2.9+) ** int valid = SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)embedhwnd,IPC_EMBED_ISVALID); ** Pass a hwnd in the wParam to this to check if the hwnd is a valid embed window or not. */ #define IPC_CONVERTFILE 506 /* (requires Winamp 2.92+) ** Converts a given file to a different format (PCM, MP3, etc...) ** To use, pass a pointer to a waFileConvertStruct struct ** This struct can be either on the heap or some global ** data, but NOT on the stack. At least, until the conversion is done. ** ** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE); ** ** Return value: ** 0: Can't start the conversion. Look at myConvertStruct->error for details. ** 1: Conversion started. Status messages will be sent to the specified callbackhwnd. ** Be sure to call IPC_CONVERTFILE_END when your callback window receives the ** IPC_CB_CONVERT_DONE message. */ typedef struct { char *sourcefile; // "c:\\source.mp3" char *destfile; // "c:\\dest.pcm" intptr_t destformat[8]; // like 'PCM ',srate,nch,bps. //hack alert! you can set destformat[6]=mmioFOURCC('I','N','I',' '); and destformat[7]=(int)my_ini_file; (where my_ini_file is a char*) HWND callbackhwnd; // window that will receive the IPC_CB_CONVERT notification messages //filled in by winamp.exe char *error; //if IPC_CONVERTFILE returns 0, the reason will be here int bytes_done; //you can look at both of these values for speed statistics int bytes_total; int bytes_out; int killswitch; // don't set it manually, use IPC_CONVERTFILE_END intptr_t extra_data[64]; // for internal winamp use } convertFileStruct; #define IPC_CONVERTFILEW 515 // (requires Winamp 5.36+) typedef struct { wchar_t *sourcefile; // "c:\\source.mp3" wchar_t *destfile; // "c:\\dest.pcm" intptr_t destformat[8]; // like 'PCM ',srate,nch,bps. //hack alert! you can set destformat[6]=mmioFOURCC('I','N','I',' '); and destformat[7]=(int)my_ini_file; (where my_ini_file is a char*) HWND callbackhwnd; // window that will receive the IPC_CB_CONVERT notification messages //filled in by winamp.exe wchar_t *error; //if IPC_CONVERTFILE returns 0, the reason will be here int bytes_done; //you can look at both of these values for speed statistics int bytes_total; int bytes_out; int killswitch; // don't set it manually, use IPC_CONVERTFILE_END intptr_t extra_data[64]; // for internal winamp use } convertFileStructW; #define IPC_CONVERTFILE_END 507 /* (requires Winamp 2.92+) ** Stop/ends a convert process started from IPC_CONVERTFILE ** You need to call this when you receive the IPC_CB_CONVERTDONE message or when you ** want to abort a conversion process ** ** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE_END); ** ** No return value */ #define IPC_CONVERTFILEW_END 516 // (requires Winamp 5.36+) typedef struct { HWND hwndParent; int format; //filled in by winamp.exe HWND hwndConfig; int extra_data[8]; //hack alert! you can set extra_data[6]=mmioFOURCC('I','N','I',' '); and extra_data[7]=(int)my_ini_file; (where my_ini_file is a char*) } convertConfigStruct; #define IPC_CONVERT_CONFIG 508 #define IPC_CONVERT_CONFIG_END 509 typedef struct { void (*enumProc)(intptr_t user_data, const char *desc, int fourcc); intptr_t user_data; } converterEnumFmtStruct; #define IPC_CONVERT_CONFIG_ENUMFMTS 510 /* (requires Winamp 2.92+) */ typedef struct { char cdletter; char *playlist_file; HWND callback_hwnd; //filled in by winamp.exe char *error; } burnCDStruct; #define IPC_BURN_CD 511 /* (requires Winamp 5.0+) */ typedef struct { convertFileStruct *cfs; int priority; } convertSetPriority; #define IPC_CONVERT_SET_PRIORITY 512 typedef struct { convertFileStructW *cfs; int priority; } convertSetPriorityW; #define IPC_CONVERT_SET_PRIORITYW 517 // (requires Winamp 5.36+) typedef struct { unsigned int format; //fourcc value char *item; // config item, eg "bitrate" char *data; // buffer to recieve, or buffer that contains the data int len; // length of the data buffer (only used when getting a config item) char *configfile; // config file to read from } convertConfigItem; #define IPC_CONVERT_CONFIG_SET_ITEM 513 // returns TRUE if successful #define IPC_CONVERT_CONFIG_GET_ITEM 514 // returns TRUE if successful typedef struct { const char *filename; char *title; // 2048 bytes int length; int force_useformatting; // can set this to 1 if you want to force a url to use title formatting shit } waHookTitleStruct; #define IPC_HOOK_TITLES 850 /* (requires Winamp 5.0+) ** If you hook this message and modify the information then make sure to return TRUE. ** If you don't hook the message then make sure you pass it on through the subclass chain. ** ** LRESULT CALLBACK WinampWndProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) ** { ** LRESULT ret = CallWindowProc((WNDPROC)WinampProc,hwnd,umsg,wParam,lParam); ** ** if(message==WM_WA_IPC && lParam==IPC_HOOK_TITLES) ** { ** waHookTitleStruct *ht = (waHookTitleStruct *) wParam; ** // Doing ATF stuff with ht->title, whatever... ** return TRUE; ** } ** return ret; ** } */ typedef struct { const wchar_t *filename; wchar_t *title; // 2048 characters int length; int force_useformatting; // can set this to 1 if you want to force a url to use title formatting shit } waHookTitleStructW; #define IPC_HOOK_TITLESW 851 /* (requires Winamp 5.3+) ** See information on IPC_HOOK_TITLES for how to process this. */ #define IPC_GETSADATAFUNC 800 // 0: returns a char *export_sa_get() that returns 150 bytes of data // 1: returns a export_sa_setreq(int want); #define IPC_GETVUDATAFUNC 801 // 0: returns a int export_vu_get(int channel) that returns 0-255 (or -1 for bad channel) #define IPC_ISMAINWNDVISIBLE 900 /* (requires Winamp 5.0+) ** int visible=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISMAINWNDVISIBLE); ** You send this to Winamp to query if the main window is visible or not such as by ** unchecking the option in the main right-click menu. If the main window is visible then ** this will return 1 otherwise it returns 0. */ typedef struct { int numElems; int *elems; HBITMAP bm; // set if you want to override } waSetPlColorsStruct; #define IPC_SETPLEDITCOLORS 920 /* (requires Winamp 5.0+) ** This is sent by gen_ff when a modern skin is being loaded to set the colour scheme for ** the playlist editor. When sent numElems is usually 6 and matches with the 6 possible ** colours which are provided be pledit.txt from the classic skins. The elems array is ** defined as follows: ** ** elems = 0 => normal text ** elems = 1 => current text ** elems = 2 => normal background ** elems = 3 => selected background ** elems = 4 => minibroswer foreground ** elems = 5 => minibroswer background ** ** if(uMsg == WM_WA_IPC && lParam == IPC_SETPLEDITCOLORS) ** { ** waSetPlColorsStruct* colStr = (waSetPlColorsStruct*)wp; ** if(colStr) ** { ** // set or inspect the colours being used (basically for gen_ff's benefit) ** } ** } */ typedef struct { HWND wnd; int xpos; // in screen coordinates int ypos; } waSpawnMenuParms; // waSpawnMenuParms2 is used by the menubar submenus typedef struct { HWND wnd; int xpos; // in screen coordinates int ypos; int width; int height; } waSpawnMenuParms2; // the following IPC use waSpawnMenuParms as parameter #define IPC_SPAWNEQPRESETMENU 933 #define IPC_SPAWNFILEMENU 934 //menubar #define IPC_SPAWNOPTIONSMENU 935 //menubar #define IPC_SPAWNWINDOWSMENU 936 //menubar #define IPC_SPAWNHELPMENU 937 //menubar #define IPC_SPAWNPLAYMENU 938 //menubar #define IPC_SPAWNPEFILEMENU 939 //menubar #define IPC_SPAWNPEPLAYLISTMENU 940 //menubar #define IPC_SPAWNPESORTMENU 941 //menubar #define IPC_SPAWNPEHELPMENU 942 //menubar #define IPC_SPAWNMLFILEMENU 943 //menubar #define IPC_SPAWNMLVIEWMENU 944 //menubar #define IPC_SPAWNMLHELPMENU 945 //menubar #define IPC_SPAWNPELISTOFPLAYLISTS 946 #define WM_WA_SYSTRAY WM_USER+1 /* This is sent by the system tray when an event happens (you might want to simulate it). ** ** if(uMsg == WM_WA_SYSTRAY) ** { ** switch(lParam) ** { ** // process the messages sent from the tray ** } ** } */ #define WM_WA_MPEG_EOF WM_USER+2 /* Input plugins send this when they are done playing back the current file to inform ** Winamp or anyother installed plugins that the current ** ** if(uMsg == WM_WA_MPEG_EOF) ** { ** // do what is needed here ** } */ //// video stuff #define IPC_IS_PLAYING_VIDEO 501 // returns >1 if playing, 0 if not, 1 if old version (so who knows):) #define IPC_GET_IVIDEOOUTPUT 500 // see below for IVideoOutput interface #define VIDEO_MAKETYPE(A,B,C,D) ((A) | ((B)<<8) | ((C)<<16) | ((D)<<24)) #define VIDUSER_SET_INFOSTRING 0x1000 #define VIDUSER_GET_VIDEOHWND 0x1001 #define VIDUSER_SET_VFLIP 0x1002 #define VIDUSER_SET_TRACKSELINTERFACE 0x1003 // give your ITrackSelector interface as param2 #define VIDUSER_OPENVIDEORENDERER 0x1004 #define VIDUSER_CLOSEVIDEORENDERER 0x1005 #define VIDUSER_GETPOPUPMENU 0x1006 #define VIDUSER_SET_INFOSTRINGW 0x1007 typedef struct { int w; int h; int vflip; double aspectratio; unsigned int fmt; } VideoOpenStruct; #ifndef NO_IVIDEO_DECLARE #ifdef __cplusplus class VideoOutput; class SubsItem; #ifndef _NSV_DEC_IF_H_ struct YV12_PLANE { unsigned char* baseAddr; long rowBytes; } ; struct YV12_PLANES { YV12_PLANE y; YV12_PLANE u; YV12_PLANE v; }; #endif class IVideoOutput { public: virtual ~IVideoOutput() { } virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0; virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { (void)token; (void)msgcallback; /* to eliminate warning C4100 */ } virtual void close()=0; virtual void draw(void *frame)=0; virtual void drawSubtitle(SubsItem *item) {UNREFERENCED_PARAMETER(item); } virtual void showStatusMsg(const char *text) {UNREFERENCED_PARAMETER(text); } virtual int get_latency() { return 0; } virtual void notifyBufferState(int bufferstate) { UNREFERENCED_PARAMETER(bufferstate); } /* 0-255*/ virtual INT_PTR extended(INT_PTR param1, INT_PTR param2, INT_PTR param3) { UNREFERENCED_PARAMETER(param1); UNREFERENCED_PARAMETER(param2); UNREFERENCED_PARAMETER(param3); return 0; } // Dispatchable, eat this! }; class ITrackSelector { public: virtual int getNumAudioTracks()=0; virtual void enumAudioTrackName(int n, const char *buf, int size)=0; virtual int getCurAudioTrack()=0; virtual int getNumVideoTracks()=0; virtual void enumVideoTrackName(int n, const char *buf, int size)=0; virtual int getCurVideoTrack()=0; virtual void setAudioTrack(int n)=0; virtual void setVideoTrack(int n)=0; }; #endif //cplusplus #endif//NO_IVIDEO_DECLARE // these messages are callbacks that you can grab by subclassing the winamp window // wParam = #define IPC_CB_WND_EQ 0 // use one of these for the param #define IPC_CB_WND_PE 1 #define IPC_CB_WND_MB 2 #define IPC_CB_WND_VIDEO 3 #define IPC_CB_WND_MAIN 4 #define IPC_CB_ONSHOWWND 600 #define IPC_CB_ONHIDEWND 601 #define IPC_CB_GETTOOLTIP 602 #define IPC_CB_MISC 603 #define IPC_CB_MISC_TITLE 0 // start of playing/stop/pause #define IPC_CB_MISC_VOLUME 1 // volume/pan #define IPC_CB_MISC_STATUS 2 // start playing/stop/pause/ffwd/rwd #define IPC_CB_MISC_EQ 3 #define IPC_CB_MISC_INFO 4 #define IPC_CB_MISC_VIDEOINFO 5 #define IPC_CB_MISC_TITLE_RATING 6 // (5.5+ for when the rating is changed via the songticker menu on current file) /* Example of using IPC_CB_MISC_STATUS to detect the start of track playback with 5.x ** ** if(lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS) ** { ** if(SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING) == 1 && ** !SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME)) ** { ** char* file = (char*)SendMessage(hwnd_winamp,WM_WA_IPC, ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS),IPC_GETPLAYLISTFILE); ** // only output if a valid file was found ** if(file) ** { ** MessageBox(hwnd_winamp,file,"starting",0); ** // or do something else that you need to do ** } ** } ** } */ #define IPC_CB_CONVERT_STATUS 604 // param value goes from 0 to 100 (percent) #define IPC_CB_CONVERT_DONE 605 #define IPC_ADJUST_FFWINDOWSMENUPOS 606 /* (requires Winamp 2.9+) ** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFWINDOWSMENUPOS); ** This will move where Winamp expects the freeform windows in the menubar windows main ** menu. This is useful if you wish to insert a menu item above extra freeform windows. */ #define IPC_ISDOUBLESIZE 608 /* (requires Winamp 5.0+) ** int dsize=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISDOUBLESIZE); ** You send this to Winamp to query if the double size mode is enabled or not. ** If it is on then this will return 1 otherwise it will return 0. */ #define IPC_ADJUST_FFOPTIONSMENUPOS 609 /* (requires Winamp 2.9+) ** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFOPTIONSMENUPOS); ** moves where winamp expects the freeform preferences item in the menubar windows main ** menu. This is useful if you wish to insert a menu item above the preferences item. ** ** Note: This setting was ignored by gen_ff until it was fixed in 5.1 ** gen_ff would assume thatthe menu position was 11 in all cases and so when you ** had two plugins attempting to add entries into the main right click menu it ** would cause the 'colour themes' submenu to either be incorrectly duplicated or ** to just disappear.instead. */ #define IPC_GETTIMEDISPLAYMODE 610 /* (requires Winamp 5.0+) ** int mode=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETTIMEDISPLAYMODE); ** This will return the status of the time display i.e. shows time elapsed or remaining. ** This returns 0 if Winamp is displaying time elapsed or 1 for the time remaining. */ #define IPC_SETVISWND 611 /* (requires Winamp 5.0+) ** int viswnd=(HWND)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)viswnd,IPC_SETVISWND); ** This allows you to set a window to receive the following message commands (which are ** used as part of the modern skin integration). ** When you have finished or your visualisation is closed then send wParam as zero to ** ensure that things are correctly tidied up. */ /* The following messages are received as the LOWORD(wParam) of the WM_COMMAND message. ** See %SDK%\winamp\wa5vis.txt for more info about visualisation integration in Winamp. */ #define ID_VIS_NEXT 40382 #define ID_VIS_PREV 40383 #define ID_VIS_RANDOM 40384 #define ID_VIS_FS 40389 #define ID_VIS_CFG 40390 #define ID_VIS_MENU 40391 #define IPC_GETVISWND 612 /* (requires Winamp 5.0+) ** int viswnd=(HWND)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVISWND); ** This returns a HWND to the visualisation command handler window if set by IPC_SETVISWND. */ #define IPC_ISVISRUNNING 613 /* (requires Winamp 5.0+) ** int visrunning=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISVISRUNNING); ** This will return 1 if a visualisation is currently running and 0 if one is not running. */ #define IPC_CB_VISRANDOM 628 // param is status of random #define IPC_SETIDEALVIDEOSIZE 614 /* (requires Winamp 5.0+) ** This is sent by Winamp back to itself so it can be trapped and adjusted as needed with ** the desired width in HIWORD(wParam) and the desired height in LOWORD(wParam). ** ** if(uMsg == WM_WA_IPC){ ** if(lParam == IPC_SETIDEALVIDEOSIZE){ ** wParam = MAKEWPARAM(height,width); ** } ** } */ #define IPC_GETSTOPONVIDEOCLOSE 615 /* (requires Winamp 5.0+) ** int sovc=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETSTOPONVIDEOCLOSE); ** This will return 1 if 'stop on video close' is enabled and 0 if it is disabled. */ #define IPC_SETSTOPONVIDEOCLOSE 616 /* (requires Winamp 5.0+) ** int sovc=SendMessage(hwnd_winamp,WM_WA_IPC,enabled,IPC_SETSTOPONVIDEOCLOSE); ** Set enabled to 1 to enable and 0 to disable the 'stop on video close' option. */ typedef struct { HWND hwnd; int uMsg; WPARAM wParam; LPARAM lParam; } transAccelStruct; #define IPC_TRANSLATEACCELERATOR 617 /* (requires Winamp 5.0+) ** (deprecated as of 5.53x+) */ typedef struct { int cmd; int x; int y; int align; } windowCommand; // send this as param to an IPC_PLCMD, IPC_MBCMD, IPC_VIDCMD #define IPC_CB_ONTOGGLEAOT 618 #define IPC_GETPREFSWND 619 /* (requires Winamp 5.0+) ** HWND prefs = (HWND)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETPREFSWND); ** This will return a handle to the preferences dialog if it is open otherwise it will ** return zero. A simple check with the OS api IsWindow(..) is a good test if it's valid. ** ** e.g. this will open (or close if already open) the preferences dialog and show if we ** managed to get a valid ** SendMessage(hwnd_winamp,WM_COMMAND,MAKEWPARAM(WINAMP_OPTIONS_PREFS,0),0); ** MessageBox(hwnd_winamp,(IsWindow((HWND)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETPREFSWND))?"Valid":"Not Open"),0,MB_OK); */ #define IPC_SET_PE_WIDTHHEIGHT 620 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)&point,IPC_SET_PE_WIDTHHEIGHT); ** You pass a pointer to a POINT structure which holds the width and height and Winamp ** will set the playlist editor to that size (this is used by gen_ff on skin changes). ** There does not appear to be any bounds limiting with this so it is possible to create ** a zero size playlist editor window (which is a pretty silly thing to do). */ #define IPC_GETLANGUAGEPACKINSTANCE 621 /* (requires Winamp 5.0+) ** HINSTANCE hInst = (HINSTANCE)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLANGUAGEPACKINSTANCE); ** This will return the HINSTANCE to the currently used language pack file for winamp.exe ** ** (5.5+) ** If you pass 1 in wParam then you will have zero returned if a language pack is in use. ** if(!SendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_GETLANGUAGEPACKINSTANCE)){ ** // winamp is currently using a language pack ** } ** ** If you pass 2 in wParam then you will get the path to the language pack folder. ** wchar_t* lngpackfolder = (wchar_t*)SendMessage(hwnd_winamp,WM_WA_IPC,2,IPC_GETLANGUAGEPACKINSTANCE); ** ** If you pass 3 in wParam then you will get the path to the currently extracted language pack. ** wchar_t* lngpack = (wchar_t*)SendMessage(hwnd_winamp,WM_WA_IPC,3,IPC_GETLANGUAGEPACKINSTANCE); ** ** If you pass 4 in wParam then you will get the name of the currently used language pack. ** wchar_t* lngname = (char*)SendMessage(hwnd_winamp,WM_WA_IPC,4,IPC_GETLANGUAGEPACKINSTANCE); */ #define LANG_IDENT_STR 0 #define LANG_LANG_CODE 1 #define LANG_COUNTRY_CODE 2 /* ** (5.51+) ** If you pass 5 in LOWORD(wParam) then you will get the ident string/code string ** (based on the param passed in the HIWORD(wParam) of the currently used language pack. ** The string returned with LANG_IDENT_STR is used to represent the language that the ** language pack is intended for following ISO naming conventions for consistancy. ** ** wchar_t* ident_str = (wchar_t*)SendMessage(hwnd_winamp,WM_WA_IPC,MAKEWPARAM(5,LANG_XXX),IPC_GETLANGUAGEPACKINSTANCE); ** ** e.g. ** For the default language it will return the following for the different LANG_XXX codes ** LANG_IDENT_STR -> "en-US" (max buffer size of this is 9 wchar_t) ** LANG_LANG_CODE -> "en" (language code) ** LANG_COUNTRY_CODE -> "US" (country code) ** ** On pre 5.51 installs you can get LANG_IDENT_STR using the following method ** (you'll have to custom process the string returned if you want the langugage or country but that's easy ;) ) ** ** #define LANG_PACK_LANG_ID 65534 (if you don't have lang.h) ** HINSTANCE hInst = (HINSTANCE)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLANGUAGEPACKINSTANCE); ** TCHAR buffer[9] = {0}; ** LoadString(hInst,LANG_PACK_LANG_ID,buffer,sizeof(buffer)); ** ** ** ** The following example shows how using the basic api will allow you to load the playlist ** context menu resource from the currently loaded language pack or it will fallback to ** the default winamp.exe instance. ** ** HINSTANCE lang = (HINSTANCE)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLANGUAGEPACKINSTANCE); ** HMENU popup = GetSubMenu(GetSubMenu((LoadMenu(lang?lang:GetModuleHandle(0),MAKEINTRESOURCE(101))),2),5); ** // do processing as needed on the menu before displaying it ** TrackPopupMenuEx(orig,TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON,rc.left,rc.bottom,hwnd_owner,0); ** DestroyMenu(popup); ** ** If you need a specific menu handle then look at IPC_GET_HMENU for more information. */ #define IPC_CB_PEINFOTEXT 622 // data is a string, ie: "04:21/45:02" #define IPC_CB_OUTPUTCHANGED 623 // output plugin was changed in config #define IPC_GETOUTPUTPLUGIN 625 /* (requires Winamp 5.0+) ** char* outdll = (char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTPLUGIN); ** This returns a string of the current output plugin's dll name. ** e.g. if the directsound plugin was selected then this would return 'out_ds.dll'. */ #define IPC_SETDRAWBORDERS 626 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,enabled,IPC_SETDRAWBORDERS); ** Set enabled to 1 to enable and 0 to disable drawing of the playlist editor and winamp ** gen class windows (used by gen_ff to allow it to draw its own window borders). */ #define IPC_DISABLESKINCURSORS 627 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,enabled,IPC_DISABLESKINCURSORS); ** Set enabled to 1 to enable and 0 to disable the use of skinned cursors. */ #define IPC_GETSKINCURSORS 628 /* (requires Winamp 5.36+) ** data = (WACURSOR)cursorId. (check wa_dlg.h for values) */ #define IPC_CB_RESETFONT 629 #define IPC_IS_FULLSCREEN 630 /* (requires Winamp 5.0+) ** int val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_IS_FULLSCREEN); ** This will return 1 if the video or visualisation is in fullscreen mode or 0 otherwise. */ #define IPC_SET_VIS_FS_FLAG 631 /* (requires Winamp 5.0+) ** A vis should send this message with 1/as param to notify winamp that it has gone to or has come back from fullscreen mode */ #define IPC_SHOW_NOTIFICATION 632 #define IPC_GETSKININFO 633 #define IPC_GETSKININFOW 1633 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window by itself whenever it ** needs to get information to be shown about the current skin in the 'Current skin ** information' box on the main Skins page in the Winamp preferences. ** ** When this notification is received and the current skin is one you are providing the ** support for then you return a valid buffer for Winamp to be able to read from with ** information about it such as the name of the skin file. ** ** if(uMsg == WM_WA_IPC && lParam == IPC_GETSKININFO){ ** if(is_our_skin()){ ** return is_our_skin_name(); ** } ** } */ #define IPC_GET_MANUALPLADVANCE 634 /* (requires Winamp 5.03+) ** int val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_MANUALPLADVANCE); ** IPC_GET_MANUALPLADVANCE returns the status of the Manual Playlist Advance. ** If enabled this will return 1 otherwise it will return 0. */ #define IPC_SET_MANUALPLADVANCE 635 /* (requires Winamp 5.03+) ** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_MANUALPLADVANCE); ** IPC_SET_MANUALPLADVANCE sets the status of the Manual Playlist Advance option. ** Set value = 1 to turn it on and value = 0 to turn it off. */ #define IPC_GET_NEXT_PLITEM 636 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_EOF_GET_NEXT_PLITEM); ** ** Sent to Winamp's main window when an item has just finished playback or the next ** button has been pressed and requesting the new playlist item number to go to. ** ** Subclass this message in your application to return the new item number. ** Return -1 for normal Winamp operation (default) or the new item number in ** the playlist to be played instead of the originally selected next track. ** ** This is primarily provided for the JTFE plugin (gen_jumpex.dll). */ #define IPC_GET_PREVIOUS_PLITEM 637 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_EOF_GET_PREVIOUS_PLITEM); ** ** Sent to Winamp's main window when the previous button has been pressed and Winamp is ** requesting the new playlist item number to go to. ** ** Return -1 for normal Winamp operation (default) or the new item number in ** the playlist to be played instead of the originally selected previous track. ** ** This is primarily provided for the JTFE plugin (gen_jumpex.dll). */ #define IPC_IS_WNDSHADE 638 /* (requires Winamp 5.04+) ** int is_shaded=SendMessage(hwnd_winamp,WM_WA_IPC,wnd,IPC_IS_WNDSHADE); ** Pass 'wnd' as an id as defined for IPC_GETWND or pass -1 to query the status of the ** main window. This returns 1 if the window is in winshade mode and 0 if it is not. ** Make sure you only test for this on a 5.04+ install otherwise you get a false result. ** (See the notes about unhandled WM_WA_IPC messages). */ #define IPC_SETRATING 639 /* (requires Winamp 5.04+ with ML) ** int rating=SendMessage(hwnd_winamp,WM_WA_IPC,rating,IPC_SETRATING); ** This will allow you to set the 'rating' on the current playlist entry where 'rating' ** is an integer value from 0 (no rating) to 5 (5 stars). ** ** The following example should correctly allow you to set the rating for any specified ** playlist entry assuming of course that you're trying to get a valid playlist entry. ** ** void SetPlaylistItemRating(int item_to_set, int rating_to_set){ ** int cur_pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS); ** SendMessage(hwnd_winamp,WM_WA_IPC,item_to_set,IPC_SETPLAYLISTPOS); ** SendMessage(hwnd_winamp,WM_WA_IPC,rating_to_set,IPC_SETRATING); ** SendMessage(hwnd_winamp,WM_WA_IPC,cur_pos,IPC_SETPLAYLISTPOS); ** } */ #define IPC_GETRATING 640 /* (requires Winamp 5.04+ with ML) ** int rating=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETRATING); ** This returns the current playlist entry's rating between 0 (no rating) to 5 (5 stars). ** ** The following example should correctly allow you to get the rating for any specified ** playlist entry assuming of course that you're trying to get a valid playlist entry. ** ** int GetPlaylistItemRating(int item_to_get, int rating_to_set){ ** int cur_pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS), rating = 0; ** SendMessage(hwnd_winamp,WM_WA_IPC,item_to_get,IPC_SETPLAYLISTPOS); ** rating = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETRATING); ** SendMessage(hwnd_winamp,WM_WA_IPC,cur_pos,IPC_SETPLAYLISTPOS); ** return rating; ** } */ #define IPC_GETNUMAUDIOTRACKS 641 /* (requires Winamp 5.04+) ** int n = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETNUMAUDIOTRACKS); ** This will return the number of audio tracks available from the currently playing item. */ #define IPC_GETNUMVIDEOTRACKS 642 /* (requires Winamp 5.04+) ** int n = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETNUMVIDEOTRACKS); ** This will return the number of video tracks available from the currently playing item. */ #define IPC_GETAUDIOTRACK 643 /* (requires Winamp 5.04+) ** int cur = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETAUDIOTRACK); ** This will return the id of the current audio track for the currently playing item. */ #define IPC_GETVIDEOTRACK 644 /* (requires Winamp 5.04+) ** int cur = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVIDEOTRACK); ** This will return the id of the current video track for the currently playing item. */ #define IPC_SETAUDIOTRACK 645 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,track,IPC_SETAUDIOTRACK); ** This allows you to switch to a new audio track (if supported) in the current playing file. */ #define IPC_SETVIDEOTRACK 646 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,track,IPC_SETVIDEOTRACK); ** This allows you to switch to a new video track (if supported) in the current playing file. */ #define IPC_PUSH_DISABLE_EXIT 647 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_PUSH_DISABLE_EXIT); ** This will let you disable or re-enable the UI exit functions (close button, context ** menu, alt-f4). Remember to call IPC_POP_DISABLE_EXIT when you are done doing whatever ** was required that needed to prevent exit otherwise you have to kill the Winamp process. */ #define IPC_POP_DISABLE_EXIT 648 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_POP_DISABLE_EXIT); ** See IPC_PUSH_DISABLE_EXIT */ #define IPC_IS_EXIT_ENABLED 649 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_IS_EXIT_ENABLED); ** This will return 0 if the 'exit' option of Winamp's menu is disabled and 1 otherwise. */ #define IPC_IS_AOT 650 /* (requires Winamp 5.04+) ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_IS_AOT); ** This will return the status of the always on top flag. ** Note: This may not match the actual TOPMOST window flag while another fullscreen ** application is focused if the user has the 'Disable always on top while fullscreen ** applications are focused' option under the General Preferences page is checked. */ #define IPC_USES_RECYCLEBIN 651 /* (requires Winamp 5.09+) ** int use_bin=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_USES_RECYCLEBIN); ** This will return 1 if the deleted file should be sent to the recycle bin or ** 0 if deleted files should be deleted permanently (default action for < 5.09). ** ** Note: if you use this on pre 5.09 installs of Winamp then it will return 1 which is ** not correct but is due to the way that SendMessage(..) handles un-processed messages. ** Below is a quick case for checking if the returned value is correct. ** ** if(SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_USES_RECYCLEBIN) && ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION)>=0x5009) ** { ** // can safely follow the option to recycle the file ** } ** else * { ** // need to do a permanent delete of the file ** } */ #define IPC_FLUSHAUDITS 652 /* ** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_FLUSHAUDITS); ** ** Will flush any pending audits in the global audits queue ** */ #define IPC_GETPLAYITEM_START 653 #define IPC_GETPLAYITEM_END 654 #define IPC_GETVIDEORESIZE 655 #define IPC_SETVIDEORESIZE 656 #define IPC_INITIAL_SHOW_STATE 657 /* (requires Winamp 5.36+) ** int show_state = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INITIAL_SHOW_STATE); ** returns the processed value of nCmdShow when Winamp was started ** (see MSDN documentation the values passed to WinMain(..) for what this should be) ** ** e.g. ** if(SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INITIAL_SHOW_STATE) == SW_SHOWMINIMIZED){ ** // we are starting minimised so process as needed (keep our window hidden) ** } ** ** Useful for seeing if winamp was run minimised on startup so you can act accordingly. ** On pre-5.36 versions this will effectively return SW_NORMAL/SW_SHOWNORMAL due to the ** handling of unknown apis returning 1 from Winamp. */ // >>>>>>>>>>> Next is 658 #define IPC_PLCMD 1000 #define PLCMD_ADD 0 #define PLCMD_REM 1 #define PLCMD_SEL 2 #define PLCMD_MISC 3 #define PLCMD_LIST 4 //#define IPC_MBCMD 1001 #define MBCMD_BACK 0 #define MBCMD_FORWARD 1 #define MBCMD_STOP 2 #define MBCMD_RELOAD 3 #define MBCMD_MISC 4 #define IPC_VIDCMD 1002 #define VIDCMD_FULLSCREEN 0 #define VIDCMD_1X 1 #define VIDCMD_2X 2 #define VIDCMD_LIB 3 #define VIDPOPUP_MISC 4 //#define IPC_MBURL 1003 //sets the URL //#define IPC_MBGETCURURL 1004 //copies the current URL into wParam (have a 4096 buffer ready) //#define IPC_MBGETDESC 1005 //copies the current URL description into wParam (have a 4096 buffer ready) //#define IPC_MBCHECKLOCFILE 1006 //checks that the link file is up to date (otherwise updates it). wParam=parent HWND //#define IPC_MBREFRESH 1007 //refreshes the "now playing" view in the library //#define IPC_MBGETDEFURL 1008 //copies the default URL into wParam (have a 4096 buffer ready) #define IPC_STATS_LIBRARY_ITEMCNT 1300 // updates library count status /* ** IPC's in the message range 2000 - 3000 are reserved internally for freeform messages. ** These messages are taken from ff_ipc.h which is part of the Modern skin integration. */ #define IPC_FF_FIRST 2000 #define IPC_FF_COLOURTHEME_CHANGE IPC_FF_ONCOLORTHEMECHANGED #define IPC_FF_ONCOLORTHEMECHANGED IPC_FF_FIRST + 3 /* ** This is a notification message sent when the user changes the colour theme in a Modern ** skin and can also be detected when the Modern skin is first loaded as the gen_ff plugin ** applies relevant settings and styles (like the colour theme). ** ** The value of wParam is the name of the new color theme being switched to. ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(const char*)colour_theme_name,IPC_FF_ONCOLORTHEMECHANGED); ** ** (IPC_FF_COLOURTHEME_CHANGE is the name i (DrO) was using before getting a copy of ** ff_ipc.h with the proper name in it). */ #define IPC_FF_ISMAINWND IPC_FF_FIRST + 4 /* ** int ismainwnd = (HWND)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)test_wnd,IPC_FF_ISMAINWND); ** ** This allows you to determine if the window handle passed to it is a modern skin main ** window or not. If it is a main window or any of its windowshade variants then it will ** return 1. ** ** Because of the way modern skins are implemented, it is possible for this message to ** return a positive test result for a number of window handles within the current Winamp ** process. This appears to be because you can have a visible main window, a compact main ** window and also a winshaded version. ** ** The following code example below is one way of seeing how this api works since it will ** enumerate all windows related to Winamp at the time and allows you to process as ** required when a detection happens. ** ** ** EnumThreadWindows(GetCurrentThreadId(),enumWndProc,0); ** ** BOOL CALLBACK enumWndProc(HWND hwnd, LPARAM lParam){ ** ** if(SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)hwnd,IPC_FF_ISMAINWND)){ ** // do processing in here ** // or continue the enum for other main windows (if they exist) ** // and just comment out the line below ** return 0; ** } ** return 1; ** } */ #define IPC_FF_GETCONTENTWND IPC_FF_FIRST + 5 /* ** HWND wa2embed = (HWND)SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(HWND)test_wnd,IPC_FF_GETCONTENTWND); ** ** This will return the Winamp 2 window that is embedded in the window's container ** i.e. if hwnd is the playlist editor windowshade hwnd then it will return the Winamp 2 ** playlist editor hwnd. ** ** If no content is found such as the window has nothing embedded then this will return ** the hwnd passed to it. */ #define IPC_FF_NOTIFYHOTKEY IPC_FF_FIRST + 6 /* ** This is a notification message sent when the user uses a global hotkey combination ** which had been registered with the gen_hotkeys plugin. ** ** The value of wParam is the description of the hotkey as passed to gen_hotkeys. ** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(const char*)hotkey_desc,IPC_FF_NOTIFYHOTKEY); */ #define IPC_FF_LAST 3000 /* ** General IPC messages in Winamp ** ** All notification messages appear in the lParam of the main window message proceedure. */ #define IPC_GETDROPTARGET 3001 /* (requires Winamp 5.0+) ** IDropTarget* IDrop = (IDropTarget*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETDROPTARGET); ** ** You call this to retrieve a copy of the IDropTarget interface which Winamp created for ** handling external drag and drop operations on to it's Windows. This is only really ** useful if you're providing an alternate interface and want your Windows to provide the ** same drag and drop support as Winamp normally provides the user. Check out MSDN or ** your prefered search facility for more information about the IDropTarget interface and ** what's needed to handle it in your own instance. */ #define IPC_PLAYLIST_MODIFIED 3002 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window whenever the playlist is ** modified in any way e.g. the addition/removal of a playlist entry. ** ** It is not a good idea to do large amounts of processing in this notification since it ** will slow down Winamp as playlist entries are modified (especially when you're adding ** in a large playlist). ** ** if(uMsg == WM_WA_IPC && lParam == IPC_PLAYLIST_MODIFIED) ** { ** // do what you need to do here ** } */ #define IPC_PLAYING_FILE 3003 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window when playback begins for ** a file. This passes the full filepath in the wParam of the message received. ** ** if(uMsg == WM_WA_IPC && lParam == IPC_PLAYING_FILE) ** { ** // do what you need to do here, e.g. ** process_file((char*)wParam); ** } */ #define IPC_PLAYING_FILEW 13003 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window when playback begins for ** a file. This passes the full filepath in the wParam of the message received. ** ** if(uMsg == WM_WA_IPC && lParam == IPC_PLAYING_FILEW) ** { ** // do what you need to do here, e.g. ** process_file((wchar_t*)wParam); ** } */ #define IPC_FILE_TAG_MAY_HAVE_UPDATED 3004 #define IPC_FILE_TAG_MAY_HAVE_UPDATEDW 3005 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window when a file's tag ** (e.g. id3) may have been updated. This appears to be sent when the InfoBox(..) function ** of the associated input plugin returns a 1 (which is the file information dialog/editor ** call normally). ** ** if(uMsg == WM_WA_IPC && lParam == IPC_FILE_TAG_MAY_HAVE_UPDATED) ** { ** // do what you need to do here, e.g. ** update_info_on_file_update((char*)wParam); ** } */ #define IPC_ALLOW_PLAYTRACKING 3007 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,allow,IPC_ALLOW_PLAYTRACKING); ** Send allow as nonzero to allow play tracking and zero to disable the mode. */ #define IPC_HOOK_OKTOQUIT 3010 /* (requires Winamp 5.0+) ** This is a notification message sent to the main Winamp window asking if it's okay to ** close or not. Return zero to prevent Winamp from closing or return anything non-zero ** to allow Winamp to close. ** ** The best implementation of this option is to let the message pass through to the ** original window proceedure since another plugin may want to have a say in the matter ** with regards to Winamp closing. ** ** if(uMsg == WM_WA_IPC && lParam == IPC_HOOK_OKTOQUIT) ** { ** // do what you need to do here, e.g. ** if(no_shut_down()) ** { ** return 1; ** } ** } */ #define IPC_WRITECONFIG 3011 /* (requires Winamp 5.0+) ** SendMessage(hwnd_winamp,WM_WA_IPC,write_type,IPC_WRITECONFIG); ** ** Send write_type as 2 to write all config settings and the current playlist. ** ** Send write_type as 1 to write the playlist and common settings. ** This won't save the following ini settings:: ** ** defext, titlefmt, proxy, visplugin_name, dspplugin_name, check_ft_startup, ** visplugin_num, pe_fontsize, visplugin_priority, visplugin_autoexec, dspplugin_num, ** sticon, splash, taskbar, dropaotfs, ascb_new, ttips, riol, minst, whichicon, ** whichicon2, addtolist, snap, snaplen, parent, hilite, disvis, rofiob, shownumsinpl, ** keeponscreen, eqdsize, usecursors, fixtitles, priority, shuffle_morph_rate, ** useexttitles, bifont, inet_mode, ospb, embedwnd_freesize, no_visseh ** (the above was valid for 5.1) ** ** Send write_type as 0 to write the common and less common settings and no playlist. */ #define IPC_UPDATE_URL 3012 // pass the URL (char *) in lparam, will be free()'d on done. #define IPC_GET_RANDFUNC 3015 // returns a function to get a random number /* (requires Winamp 5.1+) ** int (*randfunc)(void) = (int(*)(void))SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GET_RANDFUNC); ** if(randfunc && randfunc != 1){ ** randfunc(); ** } ** ** This will return a positive 32-bit number (essentially 31-bit). ** The check for a returned value of 1 is because that's the default return value from ** SendMessage(..) when it is not handled so is good to check for it in this situation. */ #define IPC_METADATA_CHANGED 3017 /* (requires Winamp 5.1+) ** int changed=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)(char*)field,IPC_METADATA_CHANGED); ** a plugin can SendMessage this to winamp if internal metadata has changes. ** wParam should be a char * of what field changed ** ** Currently used for internal actions (and very few at that) the intent of this api is ** to allow a plugin to call it when metadata has changed in the current playlist entry ** e.g.a new id3v2 tag was found in a stream ** ** The wparam value can either be NULL or a pointer to an ansi string for the metadata ** which has changed. This can be thought of as an advanced version of IPC_UPDTITLE and ** could be used to allow for update of the current title when a specific tag changed. ** ** Not recommended to be used since there is not the complete handling implemented in ** Winamp for it at the moment. */ #define IPC_SKIN_CHANGED 3018 /* (requires Winamp 5.1+) ** This is a notification message sent to the main Winamp window by itself whenever ** the skin in use is changed. There is no information sent by the wParam for this. ** ** if(message == WM_WA_IPC && lparam == IPC_SKIN_CHANGED) ** { ** // do what you need to do to handle skin changes, e.g. call WADlg_init(hwnd_winamp); ** } */ #define IPC_REGISTER_LOWORD_COMMAND 3019 /* (requires Winamp 5.1+) ** WORD id = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REGISTER_LOWORD_COMMAND); ** This will assign you a unique id for making your own commands such as for extra menu ** entries. The starting value returned by this message will potentially change as and ** when the main resource file of Winamp is updated with extra data so assumptions cannot ** be made on what will be returned and plugin loading order will affect things as well. ** 5.33+ ** If you want to reserve more than one id, you can pass the number of ids required in wParam */ #define IPC_GET_DISPATCH_OBJECT 3020 // gets winamp main IDispatch * (for embedded webpages) #define IPC_GET_UNIQUE_DISPATCH_ID 3021 // gives you a unique dispatch ID that won't conflict with anything in winamp's IDispatch * #define IPC_ADD_DISPATCH_OBJECT 3022 // add your own dispatch object into winamp's. This lets embedded webpages access your functions // pass a pointer to DispatchInfo (see below). Winamp makes a copy of all this data so you can safely delete it later typedef struct { wchar_t *name; // filled in by plugin, make sure it's a unicode string!! (i.e. L"myObject" instead of "myObject). struct IDispatch *dispatch; // filled in by plugin DWORD id; // filled in by winamp on return } DispatchInfo; // see IPC_JSAPI2_GET_DISPATCH_OBJECT for version 2 of the Dispatchable scripting interface #define IPC_GET_PROXY_STRING 3023 /* (requires Winamp 5.11+) ** char* proxy_string=(char*)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_PROXY_STRING); ** This will return the same string as is shown on the General Preferences page. */ #define IPC_USE_REGISTRY 3024 /* (requires Winamp 5.11+) ** int reg_enabled=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_USE_REGISTRY); ** This will return 0 if you should leave your grubby hands off the registry (i.e. for ** lockdown mode). This is useful if Winamp is run from a USB drive and you can't alter ** system settings, etc. */ #define IPC_GET_API_SERVICE 3025 /* (requires Winamp 5.12+) ** api_service* api_service = (api_service)SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_API_SERVICE); ** This api will return Winamp's api_service pointer (which is what Winamp3 used, heh). ** If this api is not supported in the Winamp version that is being used at the time then ** the returned value from this api will be 1 which is a good version check. ** ** As of 5.12 there is support for .w5s plugins which reside in %WinampDir%\System and ** are intended for common code that can be shared amongst other plugins e.g. jnetlib.w5s ** which contains jnetlib in one instance instead of being duplicated multiple times in ** all of the plugins which need internet access. ** ** Details on the .w5s specifications will come at some stage (possibly). */ typedef struct { const wchar_t *filename; const wchar_t *metadata; wchar_t *ret; size_t retlen; } extendedFileInfoStructW; #define IPC_GET_EXTENDED_FILE_INFOW 3026 /* (requires Winamp 5.13+) ** Pass a pointer to the above struct in wParam */ #define IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE 3027 #define IPC_SET_EXTENDED_FILE_INFOW 3028 /* (requires Winamp 5.13+) ** Pass a pointer to the above struct in wParam */ #define IPC_PLAYLIST_GET_NEXT_SELECTED 3029 /* (requires 5.2+) ** int pl_item = SendMessage(hwnd_winamp,WM_WA_IPC,start,IPC_PLAYLIST_GET_NEXT_SELECTED); ** ** This works just like the ListView_GetNextItem(..) macro for ListView controls. ** 'start' is the index of the playlist item that you want to begin the search after or ** set this as -1 for the search to begin with the first item of the current playlist. ** ** This will return the index of the selected playlist according to the 'start' value or ** it returns -1 if there is no selection or no more selected items according to 'start'. ** ** Quick example: ** ** int sel = -1; ** // keep incrementing the start of the search so we get all of the selected entries ** while((sel = SendMessage(hwnd_winamp,WM_WA_IPC,sel,IPC_PLAYLIST_GET_NEXT_SELECTED))!=-1){ ** // show the playlist file entry of the selected item(s) if there were any ** MessageBox(hwnd_winamp,(char*)SendMessage(hwnd_winamp,WM_WA_IPC,sel,IPC_GETPLAYLISTFILE),0,0); ** } */ #define IPC_PLAYLIST_GET_SELECTED_COUNT 3030 /* (requires 5.2+) ** int selcnt = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_PLAYLIST_GET_SELECTED_COUNT); ** This will return the number of selected playlist entries. */ #define IPC_GET_PLAYING_FILENAME 3031 // returns wchar_t * of the currently playing filename #define IPC_OPEN_URL 3032 // send either ANSI or Unicode string (it'll figure it out, but it MUST start with "h"!, so don't send ftp:// or anything funny) // you can also send this one from another process via WM_COPYDATA (unicode only) #define IPC_USE_UXTHEME_FUNC 3033 /* (requires Winamp 5.35+) ** int ret = SendMessage(hwnd_winamp,WM_WA_IPC,param,IPC_USE_UXTHEME_FUNC); ** param can be IPC_ISWINTHEMEPRESENT or IPC_ISAEROCOMPOSITIONACTIVE or a valid hwnd. ** ** If you pass a hwnd then it will apply EnableThemeDialogTexture(ETDT_ENABLETAB) ** so your tabbed dialogs can use the correct theme (on supporting OSes ie XP+). ** ** Otherwise this will return a value based on the param passed (as defined below). ** For compatability, the return value will be zero on success (as 1 is returned ** for unsupported ipc calls on older Winamp versions) */ #define IPC_ISWINTHEMEPRESENT 0 /* This will return 0 if uxtheme.dll is present ** int isthemethere = !SendMessage(hwnd_winamp,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC); */ #define IPC_ISAEROCOMPOSITIONACTIVE 1 /* This will return 0 if aero composition is active ** int isaero = !SendMessage(hwnd_winamp,WM_WA_IPC,IPC_ISAEROCOMPOSITIONACTIVE,IPC_USE_UXTHEME_FUNC); */ #define IPC_GET_PLAYING_TITLE 3034 // returns wchar_t * of the current title #define IPC_CANPLAY 3035 // pass const wchar_t *, returns an in_mod * or 0 typedef struct { // fill these in... size_t size; // init to sizeof(artFetchData) HWND parent; // parent window of the dialogue // fill as much of these in as you can, otherwise leave them 0 const wchar_t *artist; const wchar_t *album; int year, amgArtistId, amgAlbumId; int showCancelAll; // if set to 1, this shows a "Cancel All" button on the dialogue // winamp will fill these in if the call returns successfully: void* imgData; // a buffer filled with compressed image data. free with WASABI_API_MEMMGR->sysFree() int imgDataLen; // the size of the buffer wchar_t type[10]; // eg: "jpg" const wchar_t *gracenoteFileId; // if you know it } artFetchData; #define IPC_FETCH_ALBUMART 3036 /* pass an artFetchData*. This will show a dialog guiding the use through choosing art, and return when it's finished ** return values: ** 1: error showing dialog ** 0: success ** -1: cancel was pressed ** -2: cancel all was pressed */ #define IPC_JSAPI2_GET_DISPATCH_OBJECT 3037 /* pass your service's unique ID, as a wchar_t * string, in wParam ** Winamp will copy the string, so don't worry about keeping it around ** An IDispatch * object will be returned (cast the return value from SendMessage) ** This IDispatch can be used for scripting/automation/VB interaction ** Pass to IE via IDocHostUIHandler::GetExternal and it will become window.external in javscript */ #define IPC_REGISTER_WINAMP_IPCMESSAGE 65536 /* (requires Winamp 5.0+) ** DWORD id = SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)name,IPC_REGISTER_WINAMP_IPCMESSAGE); ** The value 'id' returned is > 65536 and is incremented on subsequent calls for unique ** 'name' values passed to it. By using the same 'name' in different plugins will allow a ** common runtime api to be provided for the currently running instance of Winamp ** e.g. ** PostMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)my_param,registered_ipc); ** Have a look at wa_hotkeys.h for an example on how this api is used in practice for a ** custom WM_WA_IPC message. ** ** if(uMsg == WM_WA_IPC && lParam == id_from_register_winamp_ipcmessage){ ** // do things ** } */ #endif//_WA_IPC_H_ ================================================ FILE: in_vgm/dlg_cfg.c ================================================ /*3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999*/ #include #include #include #include #include #include "stdbool.h" #include "chips/mamedef.h" // for (U)INTxx types #include "VGMPlay.h" #include "VGMPlay_Intf.h" #include "in_vgm.h" #define HIDE_VGM7Z #define TAB_ICON_WIDTH 16 #define TAB_ICON_TRANSP 0xFF00FF // Dialogue box tabsheet handler #define NUM_CFG_TABS 5 #define CfgPlayback hWndCfgTab[0] #define CfgTags hWndCfgTab[1] #define Cfg7z hWndCfgTab[2] #define CfgMuting hWndCfgTab[3] #define CfgOptPan hWndCfgTab[4] // sadly I can't use DLG_ITEM in another define //#define DLG_ITEM GetDlgItem(hWnd, DlgID) #define SLIDER_GETPOS(hWnd, DlgID) (UINT16) \ SendDlgItemMessage(hWnd, DlgID, TBM_GETPOS, 0, 0) #define SLIDER_SETPOS(hWnd, DlgID, Pos) SendDlgItemMessage(hWnd, DlgID, TBM_SETPOS, TRUE, Pos) #define CTRL_DISABLE(hWnd, DlgID) EnableWindow(GetDlgItem(hWnd, DlgID), FALSE) #define CTRL_ENABLE(hWnd, DlgID) EnableWindow(GetDlgItem(hWnd, DlgID), TRUE) #define CTRL_HIDE(hWnd, DlgID) ShowWindow(GetDlgItem(hWnd, DlgID), SW_HIDE) #define CTRL_SHOW(hWnd, DlgID) ShowWindow(GetDlgItem(hWnd, DlgID), SW_SHOW) #define CTRL_SET_ENABLE(hWnd, DlgID, Enable) EnableWindow(GetDlgItem(hWnd, DlgID), Enable) #define CTRL_IS_ENABLED(hWnd, DlgID, Enable) IsWindowEnabled(GetDlgItem(hWnd, DlgID)) #define SETCHECKBOX(hWnd, DlgID, Check) SendDlgItemMessage(hWnd, DlgID, BM_SETCHECK, Check, 0) #define CREATE_CHILD(DlgID, DlgProc) \ CreateDialog(hPluginInst, (LPCTSTR)DlgID, hWndMain, DlgProc) #define COMBO_ADDSTR(hWnd, DlgID, String) \ SendDlgItemMessage(hWnd, DlgID, CB_ADDSTRING, 0, (LPARAM)String) #define COMBO_GETPOS(hWnd, DlgID) SendDlgItemMessage(hWnd, DlgID, CB_GETCURSEL, 0, 0) #define COMBO_SETPOS(hWnd, DlgID, Pos) SendDlgItemMessage(hWnd, DlgID, CB_SETCURSEL, Pos, 0) #define CHECK2BOOL(hWnd, DlgID) (IsDlgButtonChecked(hWnd, DlgID) == BST_CHECKED) // Function Prototypes from in_vgm.c void UpdatePlayback(void); // Function Prototypes from dlg_fileinfo.c void QueueInfoReload(void); // Function Prototypes void InitConfigDialog(HWND hWndMain); INLINE void AddTab(HWND tabCtrlWnd, int ImgIndex, char* TabTitle); static void EnableWinXPVisualStyles(HWND hWndMain); static void Slider_Setup(HWND hWndDlg, int DlgID, int Min, int Max, int BigStep, int TickFreq); static BOOL SetDlgItemFloat(HWND hDlg, int nIDDlgItem, double Value, int Precision); static int LoadConfigDialogInfo(HWND hWndDlg); BOOL CALLBACK ConfigDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); BOOL CALLBACK CfgDlgPlaybackProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); BOOL CALLBACK CfgDlgTagsProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); BOOL CALLBACK CfgDlgMutingProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); BOOL CALLBACK CfgDlgOptPanProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); BOOL CALLBACK CfgDlgChildProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); static bool IsChipAvailable(UINT8 ChipID, UINT8 ChipSet); static void ShowMutingCheckBoxes(UINT8 ChipID, UINT8 ChipSet); static void SetMutingData(UINT32 CheckBox, bool MuteOn); static void ShowOptPanBoxes(UINT8 ChipID, UINT8 ChipSet); static void SetPanningData(UINT32 Slider, UINT16 Value, bool NoRefresh); void Dialogue_TrackChange(void); extern UINT32 VGMMaxLoop; extern UINT32 VGMPbRate; extern UINT32 FadeTime; extern float VolumeLevel; extern bool SurroundSound; extern bool FadeRAWLog; extern UINT8 ResampleMode; extern UINT8 CHIP_SAMPLING_MODE; extern CHIPS_OPTION ChipOpts[0x02]; extern VGM_HEADER VGMHead; extern UINT8 PlayingMode; extern HANDLE hPluginInst; static HWND hWndCfgTab[NUM_CFG_TABS]; static bool LoopTimeMode; static UINT8 MuteChipID = 0x00; static UINT8 MuteChipSet = 0x00; static CHIP_OPTS* MuteCOpts; void InitConfigDialog(HWND hWndMain) { HWND TabCtrlWnd; RECT TabDispRect; RECT TabRect; HIMAGELIST hImgList; unsigned int CurTab; TabCtrlWnd = GetDlgItem(hWndMain, TabCollection); InitCommonControls(); // Load images for tabs hImgList = ImageList_LoadImage(hPluginInst, (LPCSTR)TabIcons, TAB_ICON_WIDTH, 0, TAB_ICON_TRANSP, IMAGE_BITMAP, LR_CREATEDIBSECTION); TabCtrl_SetImageList(TabCtrlWnd, hImgList); // Add tabs AddTab(TabCtrlWnd, 0, "Playback"); AddTab(TabCtrlWnd, 1, "Tags"); #ifndef HIDE_VGM7Z AddTab(TabCtrlWnd, 2, "VGM7z"); #endif AddTab(TabCtrlWnd, 3, "Muting"); AddTab(TabCtrlWnd, 5, "Other Opts"); // Get display rect GetWindowRect(TabCtrlWnd, &TabDispRect); GetWindowRect(hWndMain, &TabRect); OffsetRect(&TabDispRect, -TabRect.left - GetSystemMetrics(SM_CXDLGFRAME), -TabRect.top - GetSystemMetrics(SM_CYDLGFRAME) - GetSystemMetrics(SM_CYCAPTION)); TabCtrl_AdjustRect(TabCtrlWnd, FALSE, &TabDispRect); // Create child windows CfgPlayback = CREATE_CHILD(DlgCfgPlayback, CfgDlgPlaybackProc); CfgTags = CREATE_CHILD(DlgCfgTags, CfgDlgTagsProc); Cfg7z = CREATE_CHILD(DlgCfgVgm7z, CfgDlgChildProc); CfgMuting = CREATE_CHILD(DlgCfgMuting, CfgDlgMutingProc); CfgOptPan = CREATE_CHILD(DlgCfgOptPan, CfgDlgOptPanProc); EnableWinXPVisualStyles(hWndMain); // position tabs TabDispRect.right -= TabDispRect.left; // .right gets Tab Width TabDispRect.bottom -= TabDispRect.top; // .bottom gets Tab Height for (CurTab = 0; CurTab < NUM_CFG_TABS; CurTab ++) { SetWindowPos(hWndCfgTab[CurTab], HWND_TOP, TabDispRect.left, TabDispRect.top, TabDispRect.right, TabDispRect.bottom, SWP_NOZORDER); } // show first tab, hide the others ShowWindow(hWndCfgTab[0], SW_SHOW); for (CurTab = 1; CurTab < NUM_CFG_TABS; CurTab ++) ShowWindow(hWndCfgTab[CurTab], SW_HIDE); return; } INLINE void AddTab(HWND tabCtrlWnd, int ImgIndex, char* TabTitle) { TC_ITEM newTab; int tabIndex; tabIndex = TabCtrl_GetItemCount(tabCtrlWnd); newTab.mask = TCIF_TEXT; newTab.mask |= (ImgIndex >= 0) ? TCIF_IMAGE : 0; newTab.pszText = TabTitle; newTab.iImage = ImgIndex; TabCtrl_InsertItem(tabCtrlWnd, tabIndex, &newTab); return; } static void EnableWinXPVisualStyles(HWND hWndMain) { HINSTANCE dllinst; FARPROC EnThemeDlgTex; FARPROC ThemeDlgTexIsEn; unsigned int CurWnd; dllinst = LoadLibrary("uxtheme.dll"); if (dllinst == NULL) return; EnThemeDlgTex = GetProcAddress(dllinst, "EnableThemeDialogTexture"); ThemeDlgTexIsEn = GetProcAddress(dllinst, "IsThemeDialogTextureEnabled"); if (ThemeDlgTexIsEn == NULL || EnThemeDlgTex == NULL) goto CancelXPStyles; if (ThemeDlgTexIsEn(hWndMain)) { #ifndef ETDT_ENABLETAB #define ETDT_ENABLETAB 6 #endif // draw pages with theme texture for (CurWnd = 0; CurWnd < NUM_CFG_TABS; CurWnd ++) EnThemeDlgTex(hWndCfgTab[CurWnd], ETDT_ENABLETAB); } CancelXPStyles: FreeLibrary(dllinst); return; } static void Slider_Setup(HWND hWndDlg, int DlgID, int Min, int Max, int BigStep, int TickFreq) { LONG RetVal; RetVal = SendDlgItemMessage(hWndDlg, DlgID, TBM_SETRANGE, 0, MAKELONG(Min, Max)); // Note to TBM_SETTICFREQ: // Needs Automatic Ticks enabled, draw a tick mark every x ticks. RetVal = SendDlgItemMessage(hWndDlg, DlgID, TBM_SETTICFREQ, TickFreq, 0); RetVal = SendDlgItemMessage(hWndDlg, DlgID, TBM_SETLINESIZE, 0, 1); RetVal = SendDlgItemMessage(hWndDlg, DlgID, TBM_SETPAGESIZE, 0, BigStep); return; } static BOOL SetDlgItemFloat(HWND hDlg, int nIDDlgItem, double Value, int Precision) { char TempStr[0x10]; sprintf(TempStr, "%.*f", Precision, Value); return SetDlgItemText(hDlg, nIDDlgItem, TempStr); } static int LoadConfigDialogInfo(HWND hWndDlg) { UINT8 CurChp; float dbVol; INT32 TempSLng; char TempStr[0x18]; const char* TempPnt; // --- Main Dialog --- CheckDlgButton(hWndDlg, ImmediateUpdCheck, Options.ImmediateUpdate ? BST_CHECKED : BST_UNCHECKED); // --- Playback Tab --- COMBO_ADDSTR(CfgPlayback, ResmpModeList, "HQ resampling"); COMBO_ADDSTR(CfgPlayback, ResmpModeList, "HQ up, LQ down"); COMBO_ADDSTR(CfgPlayback, ResmpModeList, "LQ resampling"); COMBO_ADDSTR(CfgPlayback, ChipSmpModeList, "native"); COMBO_ADDSTR(CfgPlayback, ChipSmpModeList, "highest (nat./cust.)"); COMBO_ADDSTR(CfgPlayback, ChipSmpModeList, "custom"); COMBO_ADDSTR(CfgPlayback, ChipSmpModeList, "highest, FM native"); LoopTimeMode = false; SetDlgItemInt(CfgPlayback, LoopText, VGMMaxLoop, FALSE); SetDlgItemInt(CfgPlayback, FadeText, FadeTime, FALSE ); SetDlgItemInt(CfgPlayback, PauseNlText, Options.PauseNL, FALSE); SetDlgItemInt(CfgPlayback, PauseLpText, Options.PauseLp, FALSE); // Playback rate switch(VGMPbRate) { case 0: CheckRadioButton(CfgPlayback, RateRecRadio, RateOtherRadio, RateRecRadio); break; case 60: CheckRadioButton(CfgPlayback, RateRecRadio, RateOtherRadio, Rate60HzRadio); break; case 50: CheckRadioButton(CfgPlayback, RateRecRadio, RateOtherRadio, Rate50HzRadio); break; default: CheckRadioButton(CfgPlayback, RateRecRadio, RateOtherRadio, RateOtherRadio); break; } CTRL_SET_ENABLE(CfgPlayback, RateText, CHECK2BOOL(CfgPlayback, RateOtherRadio)); CTRL_SET_ENABLE(CfgPlayback, RateLabel, CHECK2BOOL(CfgPlayback, RateOtherRadio)); SetDlgItemInt(CfgPlayback, RateText, VGMPbRate, FALSE); // Slider from -12.0 db to +12.0 db, 0.1 db Steps (large Steps 2.0 db) Slider_Setup(CfgPlayback, VolumeSlider, 0, 240, 20, 10); dbVol = (float)(6.0 * log(VolumeLevel) / log(2.0)); TempSLng = (INT32)(floor(dbVol * 10 + 0.5)) + 120; if (TempSLng < 0) TempSLng = 0; else if (TempSLng > 240) TempSLng = 240; SLIDER_SETPOS(CfgPlayback, VolumeSlider, TempSLng); sprintf(TempStr, "%.3f (%+.1f db)", VolumeLevel, dbVol); SetDlgItemText(CfgPlayback, VolumeText, TempStr); SetDlgItemInt(CfgPlayback, SmplPbRateText, Options.SampleRate, FALSE); COMBO_SETPOS(CfgPlayback, ResmpModeList, ResampleMode); SetDlgItemInt(CfgPlayback, SmplChipRateText, Options.ChipRate, FALSE); COMBO_SETPOS(CfgPlayback, ChipSmpModeList, CHIP_SAMPLING_MODE); CheckDlgButton(CfgPlayback, SurroundCheck, SurroundSound ? BST_CHECKED : BST_UNCHECKED); /*// Filter switch(filter_type) { case FILTER_NONE: CheckRadioButton(CfgPlayback, rbFilterNone, rbFilterWeighted, rbFilterNone); break; case FILTER_LOWPASS: CheckRadioButton(CfgPlayback, rbFilterNone, rbFilterWeighted, rbFilterLowPass); break; case FILTER_WEIGHTED: CheckRadioButton(CfgPlayback, rbFilterNone, rbFilterWeighted, rbFilterWeighted); break; }*/ // --- Tags Tab --- SetDlgItemText(CfgTags, TitleFormatText, Options.TitleFormat); CheckDlgButton(CfgTags, PreferJapCheck, Options.JapTags); CheckDlgButton(CfgTags, FM2413Check, Options.AppendFM2413); CheckDlgButton(CfgTags, TrimWhitespcCheck, Options.TrimWhitespc); CheckDlgButton(CfgTags, StdSeparatorsCheck, Options.StdSeparators); CheckDlgButton(CfgTags, TagFallbackCheck, Options.TagFallback); CheckDlgButton(CfgTags, NoInfoCacheCheck, Options.NoInfoCache); //CheckDlgButton(CfgTags, cbTagsStandardiseDates, TagsStandardiseDates); SetDlgItemInt(CfgTags, MLTypeText, Options.MLFileType, FALSE); // --- Muting Tab and Options & Pan Tab --- for (CurChp = 0x00; CurChp < CHIP_COUNT; CurChp ++) { TempPnt = GetAccurateChipName(CurChp, 0xFF); COMBO_ADDSTR(CfgMuting, MutingChipList, TempPnt); COMBO_ADDSTR(CfgOptPan, EmuOptChipList, TempPnt); } COMBO_ADDSTR(CfgMuting, MutingChipNumList, "Chip #1"); COMBO_ADDSTR(CfgOptPan, EmuOptChipNumList, "Chip #1"); COMBO_ADDSTR(CfgMuting, MutingChipNumList, "Chip #2"); COMBO_ADDSTR(CfgOptPan, EmuOptChipNumList, "Chip #2"); COMBO_SETPOS(CfgMuting, MutingChipList, MuteChipID); COMBO_SETPOS(CfgOptPan, EmuOptChipList, MuteChipID); COMBO_SETPOS(CfgMuting, MutingChipNumList, MuteChipSet); COMBO_SETPOS(CfgOptPan, EmuOptChipNumList, MuteChipSet); CheckDlgButton(CfgMuting, ResetMuteCheck, Options.ResetMuting); for (TempSLng = PanChn1Slider; TempSLng <= PanChn15Slider; TempSLng ++) Slider_Setup(CfgOptPan, TempSLng, 0x00, 0x40, 0x08, 0x08); return 0; } // Dialogue box callback function BOOL CALLBACK ConfigDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: // do nothing if this is a child window (tab page) callback, pass to the parent if (GetWindowLong(hWndDlg, GWL_STYLE) & WS_CHILD) assert(false); //return FALSE; InitConfigDialog(hWndDlg); SetWindowText(hWndDlg, INVGM_TITLE " configuration"); LoadConfigDialogInfo(hWndDlg); SendMessage(hWndDlg, WM_HSCROLL, 0, 0); // trigger slider value change handlers SendMessage(CfgPlayback, WM_COMMAND, MAKEWPARAM(ResmpModeList, CBN_SELCHANGE), 0); SendMessage(CfgPlayback, WM_COMMAND, MAKEWPARAM(ChipSmpModeList, CBN_SELCHANGE), 0); SendMessage(CfgMuting, WM_COMMAND, MAKEWPARAM(MutingChipList, CBN_SELCHANGE), 0); SendMessage(CfgOptPan, WM_COMMAND, MAKEWPARAM(EmuOptChipList, CBN_SELCHANGE), 0); SetFocus(hWndDlg); return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: { UINT8 CurTab; for (CurTab = 0; CurTab < NUM_CFG_TABS; CurTab ++) SendMessage(hWndCfgTab[CurTab], WM_COMMAND, IDOK, lParam); } EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: // [X] button, Alt+F4, etc { UINT8 CurTab; for (CurTab = 0; CurTab < NUM_CFG_TABS; CurTab ++) SendMessage(hWndCfgTab[CurTab], WM_COMMAND, IDCANCEL, lParam); } EndDialog(hWndDlg, 1); return TRUE; /* // Stuff not in tabs -------------------------------------------------------- case btnReadMe: { char FileName[MAX_PATH]; char *PChar; GetModuleFileName(hPluginInst,FileName,MAX_PATH); // get *dll* path GetFullPathName(FileName,MAX_PATH,FileName,&PChar); // make it fully qualified plus find the filename bit strcpy(PChar,"in_vgm.html"); if ((int)ShellExecute(mod.hMainWindow,NULL,FileName,NULL,NULL,SW_SHOWNORMAL)<=32) MessageBox(hWndDlg,"Error opening in_vgm.html from plugin folder",mod.description,MB_ICONERROR+MB_OK); } break;*/ case ImmediateUpdCheck: if (HIWORD(wParam) == BN_CLICKED) Options.ImmediateUpdate = CHECK2BOOL(hWndDlg, ImmediateUpdCheck); break; } break; // end WM_COMMAND handling case WM_NOTIFY: switch(LOWORD(wParam)) { case TabCollection: { int CurTab; CurTab = TabCtrl_GetCurSel(GetDlgItem(hWndDlg, TabCollection)); if (CurTab == -1) break; #ifdef HIDE_VGM7Z if (CurTab >= 2) CurTab ++; // skip hidden VGM7Z tab #endif switch(((LPNMHDR)lParam)->code) { case TCN_SELCHANGING: // hide current window ShowWindow(hWndCfgTab[CurTab], SW_HIDE); EnableWindow(hWndCfgTab[CurTab], FALSE); break; case TCN_SELCHANGE: // show current window if (hWndCfgTab[CurTab] == CfgMuting) { COMBO_SETPOS(CfgMuting, MutingChipList, MuteChipID); COMBO_SETPOS(CfgMuting, MutingChipNumList, MuteChipSet); ShowMutingCheckBoxes(MuteChipID, MuteChipSet); } else if (hWndCfgTab[CurTab] == CfgOptPan) { COMBO_SETPOS(CfgOptPan, EmuOptChipList, MuteChipID); COMBO_SETPOS(CfgOptPan, EmuOptChipNumList, MuteChipSet); ShowOptPanBoxes(MuteChipID, MuteChipSet); } EnableWindow(hWndCfgTab[CurTab], TRUE); ShowWindow(hWndCfgTab[CurTab], SW_SHOW); break; } break; } } return TRUE; } return FALSE; // message not processed } BOOL CALLBACK CfgDlgPlaybackProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: VGMMaxLoop = GetDlgItemInt(CfgPlayback, LoopText, NULL, FALSE); FadeTime = GetDlgItemInt(CfgPlayback, FadeText, NULL, FALSE); Options.PauseNL = GetDlgItemInt(CfgPlayback, PauseNlText, NULL, FALSE); Options.PauseLp = GetDlgItemInt(CfgPlayback, PauseLpText, NULL, FALSE); if (IsDlgButtonChecked(CfgPlayback, RateRecRadio)) VGMPbRate = 0; else if (IsDlgButtonChecked(CfgPlayback, Rate60HzRadio)) VGMPbRate = 60; else if (IsDlgButtonChecked(CfgPlayback, Rate50HzRadio)) VGMPbRate = 50; else if (IsDlgButtonChecked(CfgPlayback, RateOtherRadio)) VGMPbRate = GetDlgItemInt(CfgPlayback, RateText, NULL, FALSE); Options.SampleRate = GetDlgItemInt(CfgPlayback, SmplPbRateText, NULL, FALSE); ResampleMode = (UINT8)COMBO_GETPOS(CfgPlayback, ResmpModeList); Options.ChipRate = GetDlgItemInt(CfgPlayback, SmplChipRateText, NULL, FALSE); CHIP_SAMPLING_MODE = (UINT8)COMBO_GETPOS(CfgPlayback, ChipSmpModeList); QueueInfoReload(); // if the Playback Rate was changed, a refresh is needed EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: EndDialog(hWndDlg, 1); return TRUE; case RateRecRadio: case Rate60HzRadio: case Rate50HzRadio: case RateOtherRadio: // Windows already does the CheckRadioButton by itself //CheckRadioButton(CfgPlayback, RateRecRadio, RateOtherRadio, LOWORD(wParam)); if (LOWORD(wParam) == RateOtherRadio) { CTRL_ENABLE(CfgPlayback, RateText); CTRL_ENABLE(CfgPlayback, RateLabel); //SetFocus(GetDlgItem(CfgPlayback, RateText)); } else { CTRL_DISABLE(CfgPlayback, RateText); CTRL_DISABLE(CfgPlayback, RateLabel); } break; case ResmpModeList: break; case ChipSmpModeList: if (HIWORD(wParam) == CBN_SELCHANGE) { BOOL CtrlEnable; CtrlEnable = (COMBO_GETPOS(CfgPlayback, ChipSmpModeList) > 0); CTRL_SET_ENABLE(CfgPlayback, SmplChipRateLabel, CtrlEnable); CTRL_SET_ENABLE(CfgPlayback, SmplChipRateText, CtrlEnable); CTRL_SET_ENABLE(CfgPlayback, SmplChipRateHzLabel, CtrlEnable); } break; case LoopText: if (HIWORD(wParam) == EN_CHANGE) { // for correct grammar: "Play loop x time(s)" BOOL Translated; bool NewLTMode; NewLTMode = (GetDlgItemInt(CfgPlayback, LoopText, &Translated, FALSE) == 1); if (Translated && NewLTMode != LoopTimeMode) { char TimeStr[0x08]; LoopTimeMode = NewLTMode; strcpy(TimeStr, "times"); if (LoopTimeMode) TimeStr[0x04] = '\0'; SetDlgItemText(CfgPlayback, LoopTimesLabel, TimeStr); } } break; case SurroundCheck: SurroundSound = CHECK2BOOL(CfgPlayback, SurroundCheck); UpdatePlayback(); break; } break; case WM_HSCROLL: if (GetDlgCtrlID((HWND)lParam) == VolumeSlider) { float dbVol; UINT16 TempSht; char TempStr[0x18]; if (LOWORD(wParam) == TB_THUMBPOSITION || LOWORD(wParam) == TB_THUMBTRACK) TempSht = HIWORD(wParam); else TempSht = SLIDER_GETPOS(CfgPlayback, VolumeSlider); dbVol = (TempSht - 120) / 10.0f; VolumeLevel = (float)pow(2.0, dbVol / 6.0); sprintf(TempStr, "%.3f (%+.1f db)", VolumeLevel, dbVol); SetDlgItemText(CfgPlayback, VolumeText, TempStr); RefreshPlaybackOptions(); if (LOWORD(wParam) != TB_THUMBTRACK) // prevent too many skips UpdatePlayback(); } break; case WM_NOTIFY: break; } return FALSE; } BOOL CALLBACK CfgDlgTagsProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: GetDlgItemText(CfgTags, TitleFormatText, Options.TitleFormat, 0x7F); //TagsStandardiseDates = CHECK2BOOL(CfgTags, cbTagsStandardiseDates, ); SetDlgItemInt(CfgTags, MLTypeText, Options.MLFileType, FALSE); EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: EndDialog(hWndDlg, 1); return TRUE; case PreferJapCheck: Options.JapTags = CHECK2BOOL(CfgTags, PreferJapCheck); break; case FM2413Check: Options.AppendFM2413 = CHECK2BOOL(CfgTags, FM2413Check); break; case TrimWhitespcCheck: Options.TrimWhitespc = CHECK2BOOL(CfgTags, TrimWhitespcCheck); QueueInfoReload(); break; case StdSeparatorsCheck: Options.StdSeparators = CHECK2BOOL(CfgTags, StdSeparatorsCheck); QueueInfoReload(); break; case TagFallbackCheck: Options.TagFallback = CHECK2BOOL(CfgTags, TagFallbackCheck); break; case NoInfoCacheCheck: Options.NoInfoCache = CHECK2BOOL(CfgTags, NoInfoCacheCheck); break; } break; case WM_NOTIFY: break; } return FALSE; } BOOL CALLBACK CfgDlgMutingProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: EndDialog(hWndDlg, 1); return TRUE; case MutingChipList: case MutingChipNumList: if (HIWORD(wParam) == CBN_SELCHANGE) { ShowMutingCheckBoxes((UINT8)COMBO_GETPOS(CfgMuting, MutingChipList), (UINT8)COMBO_GETPOS(CfgMuting, MutingChipNumList)); } break; case ResetMuteCheck: if (HIWORD(wParam) == BN_CLICKED) Options.ResetMuting = CHECK2BOOL(CfgMuting, ResetMuteCheck); break; case MuteChipCheck: MuteCOpts->Disabled = CHECK2BOOL(CfgMuting, MuteChipCheck); UpdatePlayback(); break; } // I'm NOT going to have 24 case-lines! if (LOWORD(wParam) >= MuteChn1Check && LOWORD(wParam) <= MuteChn24Check) { if (HIWORD(wParam) == BN_CLICKED) { SetMutingData(LOWORD(wParam) - MuteChn1Check, (bool)CHECK2BOOL(CfgMuting, LOWORD(wParam))); } } break; case WM_NOTIFY: break; } return FALSE; } BOOL CALLBACK CfgDlgOptPanProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: EndDialog(hWndDlg, 1); return TRUE; case EmuCoreRadio1: case EmuCoreRadio2: { CHIP_OPTS* TempCOpts; // EmuCore is only available for Chip #1 TempCOpts = (CHIP_OPTS*)&ChipOpts[0x00] + MuteChipID; if (LOWORD(wParam) == EmuCoreRadio1) { TempCOpts->EmuCore = 0x00; } else //if (LOWORD(wParam) == EmuCoreRadio2) { TempCOpts->EmuCore = 0x01; } RefreshPlaybackOptions(); ShowOptPanBoxes(MuteChipID, MuteChipSet); break; } case EmuOptChipList: case EmuOptChipNumList: if (HIWORD(wParam) == CBN_SELCHANGE) { ShowOptPanBoxes((UINT8)COMBO_GETPOS(CfgOptPan, EmuOptChipList), (UINT8)COMBO_GETPOS(CfgOptPan, EmuOptChipNumList)); } break; } break; case WM_HSCROLL: { int SliderID; SliderID = GetDlgCtrlID((HWND)lParam); if (SliderID >= PanChn1Slider && SliderID <= PanChn15Slider) { UINT16 TempSht; bool Dragging; Dragging = (LOWORD(wParam) == TB_THUMBTRACK); if (Dragging || LOWORD(wParam) == TB_THUMBPOSITION) TempSht = HIWORD(wParam); else TempSht = SLIDER_GETPOS(CfgOptPan, SliderID); SetPanningData(SliderID - PanChn1Slider, TempSht, Dragging); } break; } case WM_NOTIFY: break; } return FALSE; } BOOL CALLBACK CfgDlgChildProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { // Generic Procedure for unused/unfinished tabs switch(wMessage) { case WM_INITDIALOG: break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: EndDialog(hWndDlg, 0); return TRUE; case IDCANCEL: EndDialog(hWndDlg, 1); return TRUE; } break; case WM_NOTIFY: break; } return FALSE; } static bool IsChipAvailable(UINT8 ChipID, UINT8 ChipSet) { UINT32 Clock; if (PlayingMode == 0xFF) return true; Clock = GetChipClock(&VGMHead, (ChipSet << 7) | ChipID, NULL); if (! Clock) return false; else return true; } static void ShowMutingCheckBoxes(UINT8 ChipID, UINT8 ChipSet) { UINT8 ChnCount; UINT8 ChnCntS[0x04]; const char* SpcName[0x40]; // Special Channel Names UINT8 CurChn; UINT8 SpcModes; bool EnableChk; bool Checked; UINT8 CurMode; UINT8 ChnBase; UINT8 FnlChn; char TempName[0x18]; for (CurChn = 0x00; CurChn < 0x40; CurChn ++) SpcName[CurChn] = NULL; EnableChk = IsChipAvailable(ChipID, ChipSet); SpcModes = 0; switch(ChipID) { case 0x00: // SN76496 ChnCount = 4; SpcName[3] = "&Noise"; break; case 0x01: // YM2413 case 0x09: // YM3812 case 0x0A: // YM3526 case 0x0B: // Y8950 ChnCount = 14; // 9 + 5 SpcName[ 9] = "&Bass Drum"; SpcName[10] = "S&nare Drum"; SpcName[11] = "&Tom Tom"; SpcName[12] = "C&ymbal"; SpcName[13] = "&Hi-Hat"; if (ChipID == 0x0B) { ChnCount = 15; SpcName[14] = "&Delta-T"; } break; case 0x02: // YM2612 ChnCount = 7; // 6 + DAC SpcName[6] = "&DAC"; break; case 0x03: // YM2151 ChnCount = 8; break; case 0x04: // Sega PCM ChnCount = 16; EnableChk &= ! ChipSet; break; case 0x05: // RF5C68 case 0x10: // RF5C164 ChnCount = 8; EnableChk &= ! ChipSet; break; case 0x06: // YM2203 ChnCount = 6; // 3 FM + 3 AY8910 SpcModes = 3; ChnCntS[0] = 3; SpcName[0] = "FM Chn"; ChnCntS[1] = 0; ChnCntS[2] = 3; SpcName[2] = "PSG Chn"; break; case 0x07: // YM2608 case 0x08: // YM2610 ChnCount = 16; // 6 FM + 6 ADPCM + 1 DeltaT + 3 AY8910 SpcModes = 3; ChnCntS[0] = 6; SpcName[0] = "FM Chn"; ChnCntS[1] = 7; SpcName[1] = "PCM Chn"; SpcName[14] = "&Delta-T"; ChnCntS[2] = 3; SpcName[2] = "PSG Chn"; break; case 0x0C: // YMF262 ChnCount = 23; // 18 + 5 SpcName[18] = "&Bass Drum"; SpcName[19] = "S&nare Drum"; SpcName[20] = "&Tom Tom"; SpcName[21] = "C&ymbal"; SpcName[22] = "&Hi-Hat"; break; case 0x0D: // YMF278B ChnCount = 24; break; case 0x0E: // YMF271 ChnCount = 12; break; case 0x0F: // YMZ280B ChnCount = 8; break; case 0x11: // PWM ChnCount = 0; EnableChk &= ! ChipSet; break; case 0x12: // AY8910 ChnCount = 3; break; case 0x13: // GB DMG SpcName[0] = "Square &1"; SpcName[1] = "Square &2"; SpcName[2] = "Progr. &Wave"; SpcName[3] = "&Noise"; ChnCount = 4; break; case 0x14: // NES APU SpcName[0] = "Square &1"; SpcName[1] = "Square &2"; SpcName[2] = "&Triangle"; SpcName[3] = "&Noise"; SpcName[4] = "&DPCM"; SpcName[5] = "&FDS"; ChnCount = 6; break; case 0x15: // Multi PCM ChnCount = 28; break; case 0x16: // UPD7759 ChnCount = 0; break; case 0x17: // OKIM6258 ChnCount = 0; break; case 0x18: // OKIM6295 ChnCount = 4; break; case 0x19: // K051649 ChnCount = 5; break; case 0x1A: // K054539 ChnCount = 8; break; case 0x1B: // HuC6280 ChnCount = 6; break; case 0x1C: // C140 ChnCount = 24; break; case 0x1D: // K053260 ChnCount = 4; break; case 0x1E: // Pokey ChnCount = 4; break; case 0x1F: // Q-Sound ChnCount = 16; EnableChk &= ! ChipSet; break; case 0x20: // SCSP ChnCount = 32; break; case 0x21: // WonderSwan ChnCount = 4; break; case 0x22: // VSU ChnCount = 6; break; case 0x23: // SAA1099 ChnCount = 6; break; case 0x24: // ES5503 ChnCount = 32; break; case 0x25: // ES5506 ChnCount = 32; break; case 0x26: // X1-010 ChnCount = 16; break; case 0x27: // C352 ChnCount = 32; break; case 0x28: // GA20 ChnCount = 4; break; default: ChnCount = 0; EnableChk &= ! ChipSet; break; } MuteCOpts = (CHIP_OPTS*)&ChipOpts[ChipSet] + ChipID; if (ChnCount > 24) ChnCount = 24; // currently only 24 checkboxes are supported SETCHECKBOX(CfgMuting, MuteChipCheck, MuteCOpts->Disabled); CTRL_SET_ENABLE(CfgMuting, MuteChipCheck, EnableChk); if (! SpcModes) { for (CurChn = 0; CurChn < ChnCount; CurChn ++) { if (SpcName[CurChn] == NULL) { if (1 + CurChn < 10) sprintf(TempName, "Channel &%u", 1 + CurChn); else sprintf(TempName, "Channel %u (&%c)", 1 + CurChn, 'A' + (CurChn - 9)); SetDlgItemText(CfgMuting, MuteChn1Check + CurChn, TempName); } else { SetDlgItemText(CfgMuting, MuteChn1Check + CurChn, SpcName[CurChn]); } if (ChipID == 0x0D) Checked = (MuteCOpts->ChnMute2 >> CurChn) & 0x01; else Checked = (MuteCOpts->ChnMute1 >> CurChn) & 0x01; SETCHECKBOX(CfgMuting, MuteChn1Check + CurChn, Checked); CTRL_SET_ENABLE(CfgMuting, MuteChn1Check + CurChn, EnableChk); CTRL_SHOW(CfgMuting, MuteChn1Check + CurChn); } } else { for (CurMode = 0; CurMode < SpcModes; CurMode ++) { ChnBase = CurMode * 8; if (SpcName[CurMode] == NULL) SpcName[CurMode] = "Channel"; for (CurChn = 0, FnlChn = ChnBase; CurChn < ChnCntS[CurMode]; CurChn ++, FnlChn ++) { if (FnlChn < SpcModes || SpcName[FnlChn] == NULL) { if (1 + CurChn < 10) sprintf(TempName, "%s &%u", SpcName[CurMode], 1 + CurChn); else sprintf(TempName, "%s %u (&%c)", SpcName[CurMode], 1 + CurChn, 'A' + (CurChn - 9)); SetDlgItemText(CfgMuting, MuteChn1Check + FnlChn, TempName); } else { SetDlgItemText(CfgMuting, MuteChn1Check + FnlChn, SpcName[FnlChn]); } switch(CurMode) { case 0: Checked = (MuteCOpts->ChnMute1 >> CurChn) & 0x01; break; case 1: Checked = (MuteCOpts->ChnMute2 >> CurChn) & 0x01; break; case 2: Checked = (MuteCOpts->ChnMute3 >> CurChn) & 0x01; break; } SETCHECKBOX(CfgMuting, MuteChn1Check + FnlChn, Checked); CTRL_SET_ENABLE(CfgMuting, MuteChn1Check + FnlChn, EnableChk); CTRL_SHOW(CfgMuting, MuteChn1Check + FnlChn); } for (; CurChn < 8; CurChn ++, FnlChn ++) { CTRL_HIDE(CfgMuting, MuteChn1Check + FnlChn); CTRL_DISABLE(CfgMuting, MuteChn1Check + FnlChn); SetDlgItemText(CfgMuting, MuteChn1Check + FnlChn, ""); } } CurChn = FnlChn; } for (; CurChn < 24; CurChn ++) { // I thought that disabling a window should prevent it from catching other // windows' keyboard shortcuts. // But NO, it seems that you have to make its text EMPTY to make it work! CTRL_HIDE(CfgMuting, MuteChn1Check + CurChn); CTRL_DISABLE(CfgMuting, MuteChn1Check + CurChn); SetDlgItemText(CfgMuting, MuteChn1Check + CurChn, ""); } MuteChipID = ChipID; MuteChipSet = ChipSet; return; } static void SetMutingData(UINT32 CheckBox, bool MuteOn) { //UINT8 ChnCount; //UINT8 ChnCntS[0x04]; UINT8 CurChn; UINT8 SpcModes; UINT8 CurMode; SpcModes = 0; switch(MuteChipID) { case 0x06: // YM2203 //ChnCount = 6; // 3 FM + 3 AY8910 SpcModes = 2; //ChnCntS[0] = 3; //ChnCntS[1] = 3; break; case 0x07: // YM2608 case 0x08: // YM2610 //ChnCount = 16; // 6 FM + 6 ADPCM + 1 DeltaT + 3 AY8910 SpcModes = 3; //ChnCntS[0] = 6; //ChnCntS[1] = 7; //ChnCntS[2] = 3; break; } if (! SpcModes) { CurMode = 0; if (MuteChipID == 0x0D) CurMode = 1; CurChn = CheckBox; } else { CurMode = CheckBox / 8; CurChn = CheckBox % 8; } switch(CurMode) { case 0: MuteCOpts->ChnMute1 &= ~(1 << CurChn); MuteCOpts->ChnMute1 |= (MuteOn << CurChn); break; case 1: MuteCOpts->ChnMute2 &= ~(1 << CurChn); MuteCOpts->ChnMute2 |= (MuteOn << CurChn); break; case 2: MuteCOpts->ChnMute3 &= ~(1 << CurChn); MuteCOpts->ChnMute3 |= (MuteOn << CurChn); break; } RefreshMuting(); UpdatePlayback(); return; } static void ShowOptPanBoxes(UINT8 ChipID, UINT8 ChipSet) { UINT8 ChnCount; UINT8 CurChn; const char* SpcName[0x20]; // Special Channel Names const char* CoreName[0x02]; // Names for the Emulation Cores bool EnableChk; bool MultiCore; bool CanPan; INT16 PanPos; char TempName[0x20]; const char* TempStr; CHIP_OPTS* TempCOpts; // for getting EmuCore only for (CurChn = 0x00; CurChn < 0x20; CurChn ++) SpcName[CurChn] = NULL; CoreName[0x00] = "MAME"; CoreName[0x01] = NULL; // ChipOpts[0x01] contains the EmuCore that's currently used TempCOpts = (CHIP_OPTS*)&ChipOpts[0x01] + ChipID; EnableChk = IsChipAvailable(ChipID, ChipSet); MultiCore = false; CanPan = false; ChnCount = 0; switch(ChipID) { case 0x00: // SN76496 MultiCore = true; CoreName[0x01] = "Maxim"; CanPan = (TempCOpts->EmuCore == 0x01); ChnCount = 4; SpcName[3] = "&N"; break; case 0x01: // YM2413 MultiCore = true; CoreName[0x00] = "EMU2413"; CoreName[0x01] = "MAME"; CanPan = (TempCOpts->EmuCore == 0x00); ChnCount = 14; // 9 + 5 SpcName[ 9] = "&BD"; SpcName[10] = "S&D"; SpcName[11] = "&TT"; SpcName[12] = "C&Y"; SpcName[13] = "&HH"; break; case 0x02: // YM2612 MultiCore = true; CoreName[0x01] = "Nuked OPN2"; //CoreName[0x02] = "Gens"; break; case 0x06: // YM2203 case 0x07: // YM2608 case 0x08: // YM2610 MultiCore = true; CoreName[0x00] = "EMU2149"; CoreName[0x01] = "MAME"; break; /*case 0x06: // YM2203 ChnCount = 6; // 3 FM + 3 AY8910 SpcModes = 2; ChnCntS[0] = 3; SpcName[0] = "FM Chn"; ChnCntS[1] = 3; SpcName[1] = "PSG Chn"; break;*/ case 0x09: // YM3812 case 0x0C: // YMF262 MultiCore = true; CoreName[0x00] = "AdLibEmu"; CoreName[0x01] = "MAME"; break; case 0x0D: // YMF272B CoreName[0x00] = "openMSX"; break; case 0x10: // RF5C164 case 0x11: // PWM CoreName[0x00] = "Gens"; break; case 0x12: // AY8910 MultiCore = true; CoreName[0x00] = "EMU2149"; CoreName[0x01] = "MAME"; break; case 0x14: // NES APU MultiCore = true; CoreName[0x00] = "NSFPlay"; CoreName[0x01] = "MAME"; break; case 0x1B: // HuC6280 MultiCore = true; CoreName[0x00] = "Ootake"; CoreName[0x01] = "MAME"; break; case 0x1F: // QSound MultiCore = true; CoreName[0x00] = "superctr"; CoreName[0x01] = "MAME"; break; case 0x27: // C352 CoreName[0x00] = "superctr"; break; default: ChnCount = 0; EnableChk = false; break; } TempCOpts = (CHIP_OPTS*)&ChipOpts[0x00] + ChipID; MuteCOpts = (CHIP_OPTS*)&ChipOpts[ChipSet] + ChipID; if (! MuteCOpts->ChnCnt) CanPan = false; if (ChnCount > 15) ChnCount = 15; // there are only 15 sliders for (CurChn = 0x00; CurChn < 0x02; CurChn ++) { CTRL_SET_ENABLE(CfgOptPan, EmuCoreRadio1 + CurChn, MultiCore && EnableChk); TempStr = (! CurChn) ? "Default" : "Alternative"; if (CoreName[CurChn] == NULL) sprintf(TempName, "%s Core", TempStr); else sprintf(TempName, "%s (%s)", TempStr, CoreName[CurChn]); PanPos = strlen(TempName); SetDlgItemText(CfgOptPan, EmuCoreRadio1 + CurChn, TempName); } CheckRadioButton(CfgOptPan, EmuCoreRadio1, EmuCoreRadio2, EmuCoreRadio1 + TempCOpts->EmuCore); for (CurChn = 0; CurChn < ChnCount; CurChn ++) { if (SpcName[CurChn] == NULL) { if (1 + CurChn < 10) sprintf(TempName, "&%u", 1 + CurChn); else sprintf(TempName, "&%c", 'A' + (CurChn - 9)); SetDlgItemText(CfgOptPan, PanChn1Label + CurChn, TempName); } else { SetDlgItemText(CfgOptPan, PanChn1Label + CurChn, SpcName[CurChn]); } CTRL_SET_ENABLE(CfgOptPan, PanChn1Label + CurChn, EnableChk && CanPan); PanPos = MuteCOpts->Panning[CurChn] / 0x08 + 0x20; SLIDER_SETPOS(CfgOptPan, PanChn1Slider + CurChn, PanPos); CTRL_SET_ENABLE(CfgOptPan, PanChn1Slider + CurChn, EnableChk && CanPan); } PanPos = 0x20; for (; CurChn < 15; CurChn ++) { if (1 + CurChn < 10) sprintf(TempName, "&%u", 1 + CurChn); else sprintf(TempName, "&%c", 'A' + (CurChn - 9)); SetDlgItemText(CfgOptPan, PanChn1Label + CurChn, TempName); CTRL_DISABLE(CfgOptPan, PanChn1Label + CurChn); CTRL_DISABLE(CfgOptPan, PanChn1Slider + CurChn); SLIDER_SETPOS(CfgOptPan, PanChn1Slider + CurChn, PanPos); } MuteChipID = ChipID; MuteChipSet = ChipSet; return; } static void SetPanningData(UINT32 Slider, UINT16 Value, bool NoRefresh) { if (Slider >= MuteCOpts->ChnCnt) return; MuteCOpts->Panning[Slider] = (Value - 0x20) * 0x08; RefreshPanning(); if (! NoRefresh) UpdatePlayback(); return; } void Dialogue_TrackChange(void) { // redraw Muting Boxes and Panning Sliders ShowMutingCheckBoxes(MuteChipID, MuteChipSet); ShowOptPanBoxes(MuteChipID, MuteChipSet); return; } ================================================ FILE: in_vgm/dlg_fileinfo.c ================================================ /*3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999*/ #include #include #include #include #include #include "Winamp/wa_ipc.h" #include "Winamp/in2.h" #include "stdbool.h" #include "chips/mamedef.h" #include "VGMPlay.h" #include "VGMPlay_Intf.h" #include "in_vgm.h" #define DllExport __declspec(dllexport) #define SETCHECKBOX(hWnd, DlgID, Check) SendDlgItemMessage(hWnd, DlgID, BM_SETCHECK, Check, 0) typedef struct fileinfo_data { UINT32 FileNameAlloc; wchar_t* FileName; bool ForceReload; FILETIME FileTime; UINT32 FileSize; VGM_HEADER Head; GD3_TAG Tag; UINT32 TrackLen; // length without loops UINT32 TotalLen; // including loops UINT32 LoopLen; UINT32 DataSize; float BitRate; float VolGain; } FINF_DATA; // Function Prototypes from in_vgm.c void Config(HWND hWndParent); // Function Prototypes void SetInfoDlgFile(const char* FileName); void SetInfoDlgFileW(const wchar_t* FileName); UINT32 GetVGZFileSize(const char* FileName); UINT32 GetVGZFileSizeW(const wchar_t* FileName); static void CopyWStr(wchar_t** DstStr, const wchar_t* SrcStr); static void CopyTagData(GD3_TAG* DstTag, const GD3_TAG* SrcTag); static bool LoadInfoA(const char* FileName, FINF_DATA* FileInf); static bool LoadInfoW(const wchar_t* FileName, FINF_DATA* FileInf); static void FixNewLine(wchar_t** TextData); static void FixSeparators(wchar_t** TextData); static void TrimWhitespaces(wchar_t* TextData); static bool CheckFM2413Text(VGM_HEADER* FileHead); UINT32 FormatVGMTag(const UINT32 BufLen, in_char* Buffer, GD3_TAG* FileTag, VGM_HEADER* FileHead); static void AppendToStr(char* Buffer, const char* AppendStr, UINT8 Seperator); static void MakeChipStr(char* Buffer, UINT8 ChipID, UINT8 SubType, UINT32 Clock); static void PrintTime(char* Buffer, UINT32 MSecTime); static void GetChipUsageText(char* Buffer, VGM_HEADER* FileHead); void FormatVGMLength(char* Buffer, FINF_DATA* FileInf); void PrintTime(char* Buffer, UINT32 MSecTime); //int GetTrackNumber(const char *filename); bool LoadPlayingVGMInfo(const char* FileName); bool LoadPlayingVGMInfoW(const wchar_t* FileName); void QueueInfoReload(void); bool GetExtendedFileInfoW(const wchar_t* FileName, const char* MetaType, wchar_t* RetBuffer, int RetBufLen); const wchar_t* GetTagStringEngJap(const wchar_t* TextEng, const wchar_t* TextJap, bool LangMode); void DisplayTagString(HWND hWndDlg, int DlgItem, const wchar_t* TextEng, const wchar_t* TextJap, bool LangMode); BOOL CALLBACK FileInfoDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); DllExport int winampGetExtendedFileInfoW(const wchar_t* wfilename, const char* metadata, wchar_t* ret, int retlen); DllExport int winampGetExtendedFileInfo(const char* filename, const char* metadata, char* ret, int retlen); #define METATAG_AUTHOR 0x01 #define METATAG_LENGTH 0x02 #define METATAG_TITLE 0x03 #define METATAG_ALBUM 0x04 #define METATAG_COMMENT 0x05 #define METATAG_YEAR 0x06 #define METATAG_GENRE 0x07 #define METATAG_UNKNOWN 0xFF extern VGM_HEADER VGMHead; extern GD3_TAG VGMTag; extern UINT32 VGMMaxLoop; extern UINT32 FadeTime; static FINF_DATA VGMInfo; static FINF_DATA PlayVGMInfo; static const char* FileNameToLoad; static const wchar_t* FileNameToLoadW; void SetInfoDlgFile(const char* FileName) { FileNameToLoadW = NULL; FileNameToLoad = FileName; return; } void SetInfoDlgFileW(const wchar_t* FileName) { FileNameToLoad = NULL; FileNameToLoadW = FileName; return; } UINT32 GetVGZFileSize(const char* FileName) { // returns the size of a compressed VGZ file // or 0 it the file is uncompressed or not found FILE* hFile; UINT32 FileSize; UINT16 gzHead; hFile = fopen(FileName, "rb"); if (hFile == NULL) return 0x00; fread(&gzHead, 0x02, 0x01, hFile); if (gzHead == 0x8B1F) { // .gz File fseek(hFile, 0x00, SEEK_END); FileSize = ftell(hFile); } else { FileSize = 0x00; } fclose(hFile); return FileSize; } UINT32 GetVGZFileSizeW(const wchar_t* FileName) { // returns the size of a compressed VGZ file // or 0 it the file is uncompressed or not found FILE* hFile; UINT32 FileSize; UINT16 gzHead; hFile = _wfopen(FileName, L"rb"); if (hFile == NULL) return 0x00; fread(&gzHead, 0x02, 0x01, hFile); if (gzHead == 0x8B1F) { // .gz File fseek(hFile, 0x00, SEEK_END); FileSize = ftell(hFile); } else { FileSize = 0x00; } fclose(hFile); return FileSize; } static void CopyWStr(wchar_t** DstStr, const wchar_t* SrcStr) { size_t StrLen; if (SrcStr == NULL) { *DstStr = NULL; return; } StrLen = wcslen(SrcStr) + 0x01; *DstStr = (wchar_t*)malloc(StrLen * sizeof(wchar_t)); wcscpy(*DstStr, SrcStr); return; } static void CopyTagData(GD3_TAG* DstTag, const GD3_TAG* SrcTag) { DstTag->fccGD3 = SrcTag->fccGD3; DstTag->lngVersion = SrcTag->lngVersion; DstTag->lngTagLength = SrcTag->lngTagLength; CopyWStr(&DstTag->strTrackNameE, SrcTag->strTrackNameE); CopyWStr(&DstTag->strTrackNameJ, SrcTag->strTrackNameJ); CopyWStr(&DstTag->strGameNameE, SrcTag->strGameNameE); CopyWStr(&DstTag->strGameNameJ, SrcTag->strGameNameJ); CopyWStr(&DstTag->strSystemNameE, SrcTag->strSystemNameE); CopyWStr(&DstTag->strSystemNameJ, SrcTag->strSystemNameJ); CopyWStr(&DstTag->strAuthorNameE, SrcTag->strAuthorNameE); CopyWStr(&DstTag->strAuthorNameJ, SrcTag->strAuthorNameJ); CopyWStr(&DstTag->strReleaseDate, SrcTag->strReleaseDate); CopyWStr(&DstTag->strCreator, SrcTag->strCreator); CopyWStr(&DstTag->strNotes, SrcTag->strNotes); return; } static bool LoadInfoA(const char* FileName, FINF_DATA* FileInf) { size_t FNSize; wchar_t* FileNameW; bool RetVal; FNSize = mbstowcs(NULL, FileName, 0); if (FNSize == -1) return false; FNSize ++; FileNameW = (wchar_t*)malloc(FNSize * sizeof(wchar_t)); mbstowcs(FileNameW, FileName, FNSize); RetVal = LoadInfoW(FileNameW, FileInf); free(FileNameW); return RetVal; } static bool LoadInfoW(const wchar_t* FileName, FINF_DATA* FileInf) { HANDLE hFile; FILETIME fileWrtTime; VGM_HEADER* FH; UINT32 StrSize; INT32 TempSLng; hFile = CreateFileW(FileName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { GetFileTime(hFile, NULL, NULL, &fileWrtTime); CloseHandle(hFile); hFile = NULL; } else { fileWrtTime.dwLowDateTime = 0x00; fileWrtTime.dwHighDateTime = 0x00; } if (! FileInf->ForceReload && ! Options.NoInfoCache) { if (FileInf->FileName != NULL && ! _wcsicmp(FileInf->FileName, FileName)) { // We just loaded that file. if (CompareFileTime(&fileWrtTime, &FileInf->FileTime) == 0) return true; // The file wasn't changed, so don't reload. } } FileInf->ForceReload = false; StrSize = wcslen(FileName) + 1; if (FileInf->FileNameAlloc < StrSize) { FileInf->FileName = (wchar_t*)realloc(FileInf->FileName, StrSize * sizeof(wchar_t)); FileInf->FileNameAlloc = StrSize; } wcscpy(FileInf->FileName, FileName); FreeGD3Tag(&FileInf->Tag); if (FileInf != &PlayVGMInfo) { if (PlayVGMInfo.FileName != NULL && ! _wcsicmp(PlayVGMInfo.FileName, FileName)) { if (PlayVGMInfo.ForceReload || Options.NoInfoCache) LoadInfoW(PlayVGMInfo.FileName, &PlayVGMInfo); // copy all info from PlayVGMInfo to current structure // (advanced caching) ;) FileInf->FileTime = PlayVGMInfo.FileTime; FileInf->FileSize = PlayVGMInfo.FileSize; FileInf->Head = PlayVGMInfo.Head; CopyTagData(&FileInf->Tag, &PlayVGMInfo.Tag); FileInf->TrackLen = PlayVGMInfo.TrackLen; FileInf->TotalLen = PlayVGMInfo.TotalLen; FileInf->LoopLen = PlayVGMInfo.LoopLen; FileInf->DataSize = PlayVGMInfo.DataSize; FileInf->BitRate = PlayVGMInfo.BitRate; FileInf->VolGain = PlayVGMInfo.VolGain; return true; } FileInf->FileSize = GetVGMFileInfoW(FileInf->FileName, &FileInf->Head, &FileInf->Tag); } else { FileInf->FileSize = VGMHead.lngEOFOffset; FileInf->Head = VGMHead; CopyTagData(&FileInf->Tag, &VGMTag); } if (! FileInf->FileSize) { FileInf->FileTime.dwLowDateTime = 0x00; FileInf->FileTime.dwHighDateTime = 0x00; FileInf->FileSize = 0x00; memset(&FileInf->Head, 0x00, sizeof(VGM_HEADER)); memset(&FileInf->Tag, 0x00, sizeof(GD3_TAG)); FileInf->TrackLen = 0x00; FileInf->TotalLen = 0x00; FileInf->LoopLen = 0x00; FileInf->DataSize = 0x00; FileInf->BitRate = 0.0f; FileInf->VolGain = 0.0f; FileInf->Tag.strNotes = (wchar_t*)malloc(16 * sizeof(wchar_t)); if (GetGZFileLengthW(FileName) == 0xFFFFFFFF) wcscpy(FileInf->Tag.strNotes, L"File not found!"); else wcscpy(FileInf->Tag.strNotes, L"File invalid!"); return false; } FileInf->FileTime = fileWrtTime; FH = &FileInf->Head; FileInf->TrackLen = CalcSampleMSecExt(FH->lngTotalSamples, 0x02, FH); FileInf->LoopLen = CalcSampleMSecExt(FH->lngLoopSamples, 0x02, FH); if (! FH->lngLoopSamples) { FileInf->TotalLen = CalcSampleMSecExt(FH->lngTotalSamples, 0x02, FH) + Options.PauseNL; } else { if (! VGMMaxLoop) // infinite looping { FileInf->TotalLen = -1000; } else { TempSLng = (VGMMaxLoop * FH->bytLoopModifier + 0x08) / 0x10 - FH->bytLoopBase; if (TempSLng < 0x01) TempSLng = 0x01; FileInf->TotalLen = CalcSampleMSecExt(FH->lngTotalSamples + (INT64)(TempSLng - 1) * FH->lngLoopSamples, 0x02, FH) + FadeTime + Options.PauseLp; } } StrSize = GetVGZFileSizeW(FileInf->FileName); if (! StrSize) { if (FH->lngGD3Offset) FileInf->DataSize = FH->lngGD3Offset - FH->lngDataOffset; else FileInf->DataSize = FH->lngEOFOffset - FH->lngDataOffset; } else { FileInf->FileSize = StrSize; FileInf->DataSize = StrSize; } // 1 Bytes = 8 Bits // 1 sec = 1000 ms // -> (bytes * 8) / (ms / 1000) = bytes * 8000 / ms FileInf->BitRate = (INT64)FileInf->DataSize * 8000 / (float)FileInf->TrackLen; if (FH->bytVolumeModifier <= VOLUME_MODIF_WRAP) TempSLng = FH->bytVolumeModifier; else if (FH->bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) TempSLng = VOLUME_MODIF_WRAP - 0x100; else TempSLng = FH->bytVolumeModifier - 0x100; FileInf->VolGain = (float)pow(2.0, TempSLng / (double)0x20); if (Options.TrimWhitespc) { TrimWhitespaces(FileInf->Tag.strTrackNameE); TrimWhitespaces(FileInf->Tag.strTrackNameJ); TrimWhitespaces(FileInf->Tag.strGameNameE); TrimWhitespaces(FileInf->Tag.strGameNameJ); TrimWhitespaces(FileInf->Tag.strSystemNameE); TrimWhitespaces(FileInf->Tag.strSystemNameJ); TrimWhitespaces(FileInf->Tag.strAuthorNameE); TrimWhitespaces(FileInf->Tag.strAuthorNameJ); TrimWhitespaces(FileInf->Tag.strReleaseDate); TrimWhitespaces(FileInf->Tag.strCreator); TrimWhitespaces(FileInf->Tag.strNotes); } if (Options.StdSeparators) { FixSeparators(&FileInf->Tag.strAuthorNameE); FixSeparators(&FileInf->Tag.strAuthorNameJ); } if (! FileInf->Tag.lngVersion) { FileInf->Tag.strNotes = (wchar_t*)malloc(22 * sizeof(wchar_t)); wcscpy(FileInf->Tag.strNotes, L"No GD3 Tag available."); } else { FixNewLine(&FileInf->Tag.strNotes); } return true; } static void FixNewLine(wchar_t** TextData) { // Note: Reallocates memory for final string wchar_t* TempStr; wchar_t* SrcStr; wchar_t* DstStr; UINT32 StrSize; if (TextData == NULL || *TextData == NULL || ! wcslen(*TextData)) return; // fix Notes-Tag (CR -> CRLF) SrcStr = *TextData; StrSize = 0x00; while(*SrcStr != L'\0') { if (*SrcStr == L'\n') StrSize ++; // count CR characters twice (for additional LF character) SrcStr ++; StrSize ++; } StrSize ++; // final \0 character TempStr = (wchar_t*)malloc(StrSize * sizeof(wchar_t)); SrcStr = *TextData; DstStr = TempStr; while(*SrcStr != L'\0') { if (*SrcStr == L'\n') { *DstStr = L'\r'; DstStr ++; } *DstStr = *SrcStr; SrcStr ++; DstStr ++; } *DstStr = L'\0'; free(*TextData); *TextData = TempStr; return; } #define GOODSEP_CHR L',' static void FixSeparators(wchar_t** TextData) { // Note: Reallocates memory for final string const wchar_t BADSEPS[] = { L';', L'/', L'\\', L'&', L'\xFF0C', L'\xFF0F', L'\xFF3C', 0x0000}; wchar_t* TempStr; wchar_t* SrcStr; wchar_t* DstStr; const wchar_t* ChkStr; UINT32 StrSize; UINT32 SpcWrt; bool WroteSep; if (TextData == NULL || *TextData == NULL || ! wcslen(*TextData)) return; // fix Author-Tag (a;b;c -> a, b, c) SrcStr = *TextData; StrSize = 0x00; WroteSep = false; while(*SrcStr != L'\0') { if (WroteSep && ! (*(SrcStr + 1) == L' ' || *(SrcStr + 1) == 0x3000)) StrSize ++; // need additional Space character ChkStr = BADSEPS; while(*ChkStr != 0x0000) { if (*SrcStr == *ChkStr) { // replace bad with good chars *SrcStr = GOODSEP_CHR; break; } ChkStr ++; } if (*SrcStr == GOODSEP_CHR) WroteSep = true; SrcStr ++; StrSize ++; } StrSize ++; // final \0 character TempStr = (wchar_t*)malloc(StrSize * sizeof(wchar_t)); SrcStr = *TextData; DstStr = TempStr; SpcWrt = 0; WroteSep = false; while(*SrcStr != L'\0') { if (*SrcStr == GOODSEP_CHR) { WroteSep = true; // trim spaces left of the seperator DstStr -= SpcWrt; SpcWrt = 0; } else { if (*SrcStr == L' ' || *SrcStr == 0x3000) SpcWrt ++; else SpcWrt = 0; if (WroteSep && ! SpcWrt) { // insert space after the seperator *DstStr = L' '; DstStr ++; SpcWrt ++; } WroteSep = false; } *DstStr = *SrcStr; SrcStr ++; DstStr ++; } *DstStr = L'\0'; free(*TextData); *TextData = TempStr; return; } static void TrimWhitespaces(wchar_t* TextData) { // Note: Writes to argument string wchar_t* CurStr; if (TextData == NULL || ! wcslen(TextData)) return; // trim whitespace at end CurStr = TextData + wcslen(TextData); CurStr --; while(iswspace(*CurStr)) CurStr --; CurStr ++; *CurStr = L'\0'; // trim whitespace at start CurStr = TextData; while(iswspace(*CurStr)) CurStr ++; wcscpy(TextData, CurStr); return; } static bool CheckFM2413Text(VGM_HEADER* FileHead) { UINT8 CurChip; UINT32 Clocks; if (! Options.AppendFM2413 || FileHead == NULL || ! GetChipClock(FileHead, 0x01, NULL)) return false; Clocks = 0x00; for (CurChip = 0x02; CurChip < CHIP_COUNT; CurChip ++) Clocks |= GetChipClock(FileHead, CurChip, NULL); return (! Clocks); } UINT32 FormatVGMTag(const UINT32 BufLen, in_char* Buffer, GD3_TAG* FileTag, VGM_HEADER* FileHead) { const in_char* BufEnd; in_char* CurBuf; const char* FormatStr; const wchar_t* TagStr; const wchar_t* TagStrE; const wchar_t* TagStrJ; bool TempFlag; bool AppendFM; size_t CnvBytes; BufEnd = Buffer + BufLen - 1; CurBuf = Buffer; FormatStr = Options.TitleFormat; while(CurBuf < BufEnd && *FormatStr != '\0') { if (*FormatStr == '%') { FormatStr ++; TempFlag = false; // Is Unknown Char AppendFM = false; switch(toupper(*FormatStr)) { case 'T': // Track Title TagStrE = FileTag->strTrackNameE; TagStrJ = FileTag->strTrackNameJ; break; case 'A': // Author TagStrE = FileTag->strAuthorNameE; TagStrJ = FileTag->strAuthorNameJ; break; case 'G': // Game Name TagStrE = FileTag->strGameNameE; TagStrJ = FileTag->strGameNameJ; AppendFM = CheckFM2413Text(FileHead); break; case 'D': // Release Date TagStrE = FileTag->strReleaseDate; TagStrJ = NULL; break; case 'S': // System Name TagStrE = FileTag->strSystemNameE; TagStrJ = FileTag->strSystemNameJ; break; case 'C': // File Creator TagStrE = FileTag->strCreator; TagStrJ = NULL; break; default: TempFlag = true; break; } if (TempFlag) { #ifndef UNICODE_INPUT_PLUGIN *CurBuf = *FormatStr; FormatStr ++; CurBuf ++; #else CnvBytes = MultiByteToWideChar(CP_THREAD_ACP, 0x00, FormatStr, 1, CurBuf, BufEnd - CurBuf); FormatStr ++; CurBuf += CnvBytes; #endif } else { FormatStr ++; if (toupper(*FormatStr) == 'J') { TempFlag = true; FormatStr ++; } TagStr = GetTagStringEngJap(TagStrE, TagStrJ, TempFlag); if (TagStr == NULL) TagStr = L""; #ifndef UNICODE_INPUT_PLUGIN CnvBytes = WideCharToMultiByte(CP_THREAD_ACP, 0x00, TagStr, -1, CurBuf, BufEnd - CurBuf, NULL, NULL); if (CnvBytes) CurBuf += CnvBytes - 1; // It counts the \0 ternimator, too. // The problem with the ANSI C function is, that it simply stops // converting the string, instead of inserting '?', which would be nicer, // at least in this case. /*TagStr = GetTagStringEngJap(TagStrE, TagStrJ, TempFlag); CnvBytes = wcstombs(CurBuf, TagStr, BufEnd - CurBuf); if (CnvBytes == -1) { TagStr = GetTagStringEngJap(TagStrE, TagStrJ, ! TempFlag); CnvBytes = wcstombs(CurBuf, TagStr, BufEnd - CurBuf); } if (CnvBytes != -1) CurBuf += CnvBytes - 1;*/ if (AppendFM && (BufEnd - CurBuf) >= 0x05+1) { strcpy(CurBuf, " (FM)"); CurBuf += 0x05; } #else CnvBytes = wcslen(TagStr); if (CnvBytes < BufEnd - CurBuf) { wcscpy(CurBuf, TagStr); CurBuf += CnvBytes; } else { CnvBytes = (BufEnd - CurBuf) - 1; wcsncpy(CurBuf, TagStr, CnvBytes); CurBuf += CnvBytes; } if (AppendFM && (BufEnd - CurBuf) >= 0x05+1) { wcscpy(CurBuf, L" (FM)"); CurBuf += 0x05; } #endif } } else { #ifndef UNICODE_INPUT_PLUGIN *CurBuf = *FormatStr; FormatStr ++; CurBuf ++; #else CnvBytes = MultiByteToWideChar(CP_THREAD_ACP, 0x00, FormatStr, 1, CurBuf, BufEnd - CurBuf); FormatStr ++; CurBuf += CnvBytes; #endif } } #ifndef UNICODE_INPUT_PLUGIN *CurBuf = '\0'; #else *CurBuf = L'\0'; #endif return Buffer - CurBuf; } static void AppendToStr(char* Buffer, const char* AppendStr, UINT8 Seperator) { // Seperator Mask: // 01 - Space // 02 - Comma char* BufPnt; if (AppendStr == NULL) return; if (*Buffer == '\0') { strcpy(Buffer, AppendStr); } else { BufPnt = Buffer + strlen(Buffer); if (Seperator & 0x02) { *BufPnt = ','; BufPnt ++; } if (Seperator & 0x01) { *BufPnt = ' '; BufPnt ++; } strcpy(BufPnt, AppendStr); } return; } static void MakeChipStr(char* Buffer, UINT8 ChipID, UINT8 SubType, UINT32 Clock) { if (! Clock) return; if (ChipID == 0x00 && (Clock & 0x80000000)) Clock &= ~0x40000000; if (Clock & 0x80000000) { Clock &= ~0x80000000; ChipID |= 0x80; } if (! (Clock & 0x40000000)) { AppendToStr(Buffer, GetAccurateChipName(ChipID, SubType), 0x03); } else { AppendToStr(Buffer, "2x", 0x03); AppendToStr(Buffer, GetAccurateChipName(ChipID, SubType), 0x00); } return; } static void GetChipUsageText(char* Buffer, VGM_HEADER* FileHead) { UINT8 CurChip; UINT32 ChpClk; UINT8 ChpType; *Buffer = '\0'; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { ChpClk = GetChipClock(FileHead, CurChip, &ChpType); if (ChpClk && GetChipClock(FileHead, 0x80 | CurChip, NULL)) ChpClk |= 0x40000000; MakeChipStr(Buffer, CurChip, ChpType, ChpClk); } return; } static void PrintTime(char* Buffer, UINT32 MSecTime) { UINT16 MSecs; UINT16 Secs; UINT16 Mins; UINT16 Hours; UINT32 Days; MSecs = MSecTime % 1000; MSecTime /= 1000; Secs = MSecTime % 60; MSecTime /= 60; Mins = MSecTime % 60; MSecTime /= 60; Hours = MSecTime % 24; MSecTime /= 24; Days = MSecTime; // Maximum Output: 4 294 967 295 -> "49d + 17:02:47.29" (17 chars + \0) if (Days) { // just for fun // 1d + 2 : 34 : 56 . 78 sprintf(Buffer, "%ud + %hu:%02hu:%02hu.%02hu", Days, Hours, Mins, Secs, MSecs / 10); } else if (Hours) { // unlikely for single VGMs, but possible // 1 : 23 : 45 . 67 sprintf(Buffer, "%hu:%02hu:%02hu.%02hu", Hours, Mins, Secs, MSecs / 10); } else if (Mins) { // 1 : 23 . 45 sprintf(Buffer, "%hu:%02hu.%02hu", Mins, Secs, MSecs / 10); } else { // 1 . 23 sprintf(Buffer, "%hu.%02hu", Secs, MSecs / 10); } return; } void FormatVGMLength(char* Buffer, FINF_DATA* FileInf) { UINT32 IntroLen; char TempBuf1[0x20]; char TempBuf2[0x20]; PrintTime(Buffer, FileInf->TrackLen); if (! FileInf->LoopLen) { strcat(Buffer, " (no loop)"); } else { IntroLen = FileInf->TrackLen - FileInf->LoopLen; if (IntroLen < 500) { // no intro (or only a very small intro for song init) strcat(Buffer, " (looped)"); } else { PrintTime(TempBuf1, IntroLen); PrintTime(TempBuf2, FileInf->LoopLen); sprintf(Buffer, "%s (%s intro and %s loop)", Buffer, TempBuf1, TempBuf2); } } return; } //----------------------------------------------------------------- // Get the track number for a given filename // by trying to find its M3U playlist //----------------------------------------------------------------- /*int GetTrackNumber(const char *filename) { return 0; // assumes a filename "Streets of Rage II - Never Return Alive.vgz" // will have a playlist "Streets of Rage II.m3u" // returns track number, or 0 for not found FILE *f; char *playlist; char *p; char *fn; char buff[1024]; // buffer for reading lines from file int number=0; int linenumber; playlist=malloc(strlen(filename)+16); // plenty of space in all weird cases if(playlist) { p=strrchr(filename,'\\'); // p points to the last \ in the filename if(p){ // isolate filename fn=malloc(strlen(p)); if(fn) { strcpy(fn,p+1); while(number==0) { p=strstr(p," - "); // find first " - " after current position of p if(p) { strncpy(playlist,filename,p-filename); // copy filename up until the " - " strcpy(playlist+(p-filename),".m3u"); // terminate it with a ".m3u\0" f=fopen(playlist,"r"); // try to open file if(f) { linenumber=1; // read through file, a line at a time while(fgets(buff,1024,f) && (number==0)) { // fgets will read in all characters up to and including the line break if(strnicmp(buff,fn,strlen(fn))==0) { // found it! number=linenumber; } if((strlen(buff)>3)&&(buff[0]!='#')) linenumber++; // count lines that are (probably) valid but not #EXTINF lines } fclose(f); } p++; // make it not find this " - " next iteration } else break; } free(fn); } } free(playlist); } return number; }*/ bool LoadPlayingVGMInfo(const char* FileName) { size_t FNSize; wchar_t* FileNameW; bool RetVal; if (FileName == NULL) { RetVal = LoadPlayingVGMInfoW(NULL); } else { FNSize = mbstowcs(NULL, FileName, 0); if (FNSize == -1) return LoadPlayingVGMInfoW(NULL); FNSize ++; FileNameW = (wchar_t*)malloc(FNSize * sizeof(wchar_t)); mbstowcs(FileNameW, FileName, FNSize); RetVal = LoadPlayingVGMInfoW(FileNameW); free(FileNameW); } return true; } bool LoadPlayingVGMInfoW(const wchar_t* FileName) { bool RetVal; if (FileName == NULL && PlayVGMInfo.FileName != NULL) { PlayVGMInfo.FileName[0x00] = L'\0'; return true; } // load new file only if neccessary if (PlayVGMInfo.FileName == NULL || _wcsicmp(PlayVGMInfo.FileName, FileName)) RetVal = LoadInfoW(FileName, &PlayVGMInfo); else RetVal = true; if (! PlayVGMInfo.FileSize) return false; return true; } void QueueInfoReload(void) { PlayVGMInfo.ForceReload = true; VGMInfo.ForceReload = true; return; } // GetExtendedFileInfoW worker function bool GetExtendedFileInfoW(const wchar_t* FileName, const char* MetaType, wchar_t* RetBuffer, int RetBufLen) { /* Metadata Tag List: "track", "title", "artist", "album", "year", "comment", "genre", "length", "type", "family", "formatinformation", "gain", "bitrate", "vbr", "stereo", ... */ int TagIdx; FINF_DATA* UseInfo; GD3_TAG* FileTag; if (RetBuffer == NULL || ! RetBufLen) return false; // default to a blank string *RetBuffer = L'\0'; // General Meta Tags if (! stricmp(MetaType, "type")) { // Data Type (Audio/Video/...) _snwprintf(RetBuffer, RetBufLen, L"%d", Options.MLFileType); return true; } else if (! stricmp(MetaType, "family")) // Winamp 5.5+ only { const wchar_t* FileExt; FileExt = wcsrchr(FileName, L'.'); if (FileExt != NULL) { FileExt ++; if (! wcsicmp(FileExt, L"vgm") || ! wcsicmp(FileExt, L"vgz")) { wcsncpy(RetBuffer, L"Video Game Music File", RetBufLen); return true; } } return false; } else if (! stricmp(MetaType, "track")) { return false; /*// Track Number int TrackNo; TrackNo = GetTrackNumber(FileName); if (TrackNo) { _snwprintf(RetBuffer, RetBufLen, L"%d", TrackNo); return true; } else { return false; }*/ } if (PlayVGMInfo.FileName != NULL && ! _wcsicmp(PlayVGMInfo.FileName, FileName)) { UseInfo = &PlayVGMInfo; } else { // load new file only if neccessary if (VGMInfo.FileName == NULL || _wcsicmp(VGMInfo.FileName, FileName)) { if (! LoadInfoW(FileName, &VGMInfo)) return false; // Loading failed } UseInfo = &VGMInfo; } if (! UseInfo->FileSize) return false; if (! stricmp(MetaType, "artist")) TagIdx = METATAG_AUTHOR; else if (! stricmp(MetaType, "length")) TagIdx = METATAG_LENGTH; else if (! stricmp(MetaType, "title")) TagIdx = METATAG_TITLE; else if (! stricmp(MetaType, "album")) TagIdx = METATAG_ALBUM; // return Game Name else if (! stricmp(MetaType, "comment")) TagIdx = METATAG_COMMENT; else if (! stricmp(MetaType, "year") || ! stricmp(MetaType, "date")) TagIdx = METATAG_YEAR; else if (! stricmp(MetaType, "genre")) TagIdx = METATAG_GENRE; // return System (?) else { TagIdx = METATAG_UNKNOWN; #ifdef _DEBUG { char DebugStr[0x80]; // debug: get metadata types sprintf(DebugStr, "GetExtendedFileInfo: Unknown metadata type: \"%s\"\n", MetaType); MessageBox(NULL, DebugStr, "in_vgm Warning", MB_ICONEXCLAMATION); } #endif } switch(TagIdx) { case METATAG_LENGTH: _snwprintf(RetBuffer, RetBufLen, L"%d", UseInfo->TotalLen); break; /*case METATAG_GENRE: wcsncpy(RetBuffer, L"Game", RetBufLen); break;*/ case METATAG_TITLE: case METATAG_ALBUM: case METATAG_GENRE: case METATAG_AUTHOR: case METATAG_YEAR: //case METATAG_RIPPER: case METATAG_COMMENT: FileTag = &UseInfo->Tag; if (TagIdx == METATAG_YEAR) { wchar_t* lastslash; if (FileTag->strReleaseDate == NULL || ! wcslen(FileTag->strReleaseDate)) return false; // Note: Date formatting is almost unchanged from Maxim's old in_vgm 0.35 // try to detect various date formats // Not yyyy/mm/dd: // nn/nn/yy n/n/yy nn/n/yy n/nn/yy // nn/nn/yyyy n/n/yyyy nn/n/yyyy n/nn/yyyy // Should be: // yyyy // yyyy/mm // yyyy/mm/dd lastslash = wcsrchr(FileTag->strReleaseDate, L'/'); if (lastslash != NULL) { long year = wcstol(lastslash + 1, NULL, 10); if (year > 31) // looks like a year { if (year < 100) // 2-digit, yuck { year += 1900; if (year < 1960) // not many sound chips around then, due to lack of transistors year += 100; } _snwprintf(RetBuffer, RetBufLen, L"%d", year); } } if (*RetBuffer == L'\0') { // else, try the first bit wcsncpy(RetBuffer, FileTag->strReleaseDate, 4); RetBuffer[4] = L'\0'; } } else { const wchar_t* TagStr; switch(TagIdx) { case METATAG_TITLE: TagStr = GetTagStringEngJap(FileTag->strTrackNameE, FileTag->strTrackNameJ, Options.JapTags); break; case METATAG_ALBUM: TagStr = GetTagStringEngJap(FileTag->strGameNameE, FileTag->strGameNameJ, Options.JapTags); break; case METATAG_GENRE: TagStr = GetTagStringEngJap(FileTag->strSystemNameE, FileTag->strSystemNameJ, Options.JapTags); break; case METATAG_AUTHOR: TagStr = GetTagStringEngJap(FileTag->strAuthorNameE, FileTag->strAuthorNameJ, Options.JapTags); break; case METATAG_COMMENT: TagStr = FileTag->strNotes; break; default: TagStr = NULL; break; } if (TagStr != NULL) wcsncpy(RetBuffer, TagStr, RetBufLen); if (TagIdx == METATAG_ALBUM && CheckFM2413Text(&UseInfo->Head)) wcscat(RetBuffer, L" (FM)"); break; } break; default: // do nothing break; } // empty buffer is a bad result return (*RetBuffer != L'\0') ? true : false; } const wchar_t* GetTagStringEngJap(const wchar_t* TextEng, const wchar_t* TextJap, bool LangMode) { const wchar_t* Text; if (TextEng == NULL || ! wcslen(TextEng)) { Text = TextJap; } else if (TextJap == NULL || ! wcslen(TextJap)) { Text = TextEng; } else { if (! LangMode) Text = TextEng; else Text = TextJap; } if (Text == NULL || ! wcslen(Text)) return NULL; else return Text; } void DisplayTagString(HWND hWndDlg, int DlgItem, const wchar_t* TextEng, const wchar_t* TextJap, bool LangMode) { const wchar_t* Text; BOOL RetVal; if (Options.TagFallback) Text = GetTagStringEngJap(TextEng, TextJap, LangMode); else Text = LangMode ? TextJap : TextEng; if (Text == NULL) { RetVal = SetDlgItemTextA(hWndDlg, DlgItem, ""); return; } RetVal = SetDlgItemTextW(hWndDlg, DlgItem, Text); if (! RetVal) { // Unicode version failed, try ANSI version char ANSIStr[0x400]; wcstombs(ANSIStr, Text, 0x400); /*RetVal = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, Text, -1, ANSIStr, 0x400, NULL, NULL); if (! RetVal) return; // Unicode -> ANSI failed*/ RetVal = SetDlgItemTextA(hWndDlg, DlgItem, ANSIStr); } return; } BOOL CALLBACK FileInfoDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam) { switch(wMessage) { case WM_INITDIALOG: { char TempStr[0x100]; //---------------------------------------------------------------- // Initialise dialogue //---------------------------------------------------------------- /*int i; char tempstr[1024]; if ( IsURL( FileInfo_Name ) ) { // Filename is a URL if ( CurrentURLFilename && CurrentURL && ( ( strcmp( FileInfo_Name, CurrentURLFilename ) == 0) || ( strcmp( FileInfo_Name, CurrentURL ) == 0) ) ) { // If it's the current file, look at the temp file // but display the URL SetDlgItemText(hWndDlg, ebFilename, CurrentURL ); FileInfo_Name = CurrentURLFilename; } else { // If it's not the current file, no info SetDlgItemText(hWndDlg, ebFilename, FileInfo_Name ); SetDlgItemText(hWndDlg, ebNotes, "Information unavailable for this URL" ); DISABLECONTROL(hWndDlg, BrwsrInfoButton ); return TRUE; } } else { // Filename is not a URL SetDlgItemText(hWndDlg, ebFilename, FileInfo_Name ); }*/ // Load File Info if (FileNameToLoadW != NULL) LoadInfoW(FileNameToLoadW, &VGMInfo); else LoadInfoA(FileNameToLoad, &VGMInfo); SetDlgItemTextW(hWndDlg, VGMFileText, VGMInfo.FileName); if (VGMInfo.FileSize) { sprintf(TempStr, "%X.%02X", VGMInfo.Head.lngVersion >> 8, VGMInfo.Head.lngVersion & 0xFF); SetDlgItemText(hWndDlg, VGMVerText, TempStr); sprintf(TempStr, "%.2f", VGMInfo.VolGain); SetDlgItemText(hWndDlg, VGMGainText, TempStr); GetChipUsageText(TempStr, &VGMInfo.Head); SetDlgItemText(hWndDlg, ChipUseText, TempStr); sprintf(TempStr, "%d bytes (%.2f kbps)", VGMInfo.FileSize, VGMInfo.BitRate / 1000); SetDlgItemText(hWndDlg, VGMSizeText, TempStr); FormatVGMLength(TempStr, &VGMInfo); SetDlgItemText(hWndDlg, VGMLenText, TempStr); } else { SetDlgItemText(hWndDlg, VGMVerText, ""); SetDlgItemText(hWndDlg, VGMGainText, ""); SetDlgItemText(hWndDlg, ChipUseText, ""); SetDlgItemText(hWndDlg, VGMSizeText, ""); SetDlgItemText(hWndDlg, VGMLenText, ""); } // display language independent tags DisplayTagString(hWndDlg, GameDateText, VGMInfo.Tag.strReleaseDate, NULL, false); DisplayTagString(hWndDlg, VGMCrtText, VGMInfo.Tag.strCreator, NULL, false); DisplayTagString(hWndDlg, VGMNotesText, VGMInfo.Tag.strNotes, NULL, false); // trigger English or Japanese for other fields SETCHECKBOX(hWndDlg, LangEngCheck + Options.JapTags, TRUE); PostMessage(hWndDlg, WM_COMMAND, LangEngCheck + Options.JapTags, 0); return TRUE; } case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: //Options.JapTags = IsDlgButtonChecked(hWndDlg, LangJapCheck); //EndDialog(hWndDlg, 0); // return 0 = OK EndDialog(hWndDlg, 1); // returning 0 can cause bugs with the playlist return TRUE; case IDCANCEL: // [X] button, ESC, Alt + F4 EndDialog(hWndDlg, 1); // return 1 = Cancel, stops further dialogues being opened return TRUE; case ConfigPluginButton: Config(hWndDlg); break; case LangEngCheck: case LangJapCheck: { bool JapMode; JapMode = (LOWORD(wParam) == LangJapCheck); DisplayTagString(hWndDlg, TrkTitleText, VGMInfo.Tag.strTrackNameE, VGMInfo.Tag.strTrackNameJ, JapMode); DisplayTagString(hWndDlg, TrkAuthorText, VGMInfo.Tag.strAuthorNameE, VGMInfo.Tag.strAuthorNameJ, JapMode); DisplayTagString(hWndDlg, GameNameText, VGMInfo.Tag.strGameNameE, VGMInfo.Tag.strGameNameJ, JapMode); DisplayTagString(hWndDlg, GameSysText, VGMInfo.Tag.strSystemNameE, VGMInfo.Tag.strSystemNameJ, JapMode); break; } case BrwsrInfoButton: //InfoInBrowser(FileInfo_Name, UseMB, TRUE); break; default: break; } break; } return FALSE; // FALSE to signify message not processed } // ------------------------ // Winamp Export Functions // ------------------------ DllExport int winampGetExtendedFileInfoW(const wchar_t* wfilename, const char* metadata, wchar_t* ret, int retlen) { // called by Winamp 5.3 and higher bool RetVal; RetVal = GetExtendedFileInfoW(wfilename, metadata, ret, retlen); #if 0 { wchar_t MsgStr[MAX_PATH * 2]; swprintf(MsgStr, L"file: %s\nMetadata: %hs\nResult: %ls", wfilename, metadata, ret); MessageBoxW(NULL, MsgStr, L"GetExtFileInfoW", MB_ICONINFORMATION | MB_OK); } #endif return RetVal; } DllExport int winampGetExtendedFileInfo(const char* filename, const char* metadata, char* ret, int retlen) { // called by Winamp versions until 5.24 size_t FNSize; wchar_t* FileNameW; wchar_t* wRetStr; bool RetVal; FNSize = mbstowcs(NULL, filename, 0); if (FNSize == -1) return 0; FNSize ++; FileNameW = (wchar_t*)malloc(FNSize * sizeof(wchar_t)); mbstowcs(FileNameW, filename, FNSize); wRetStr = (wchar_t*)malloc(retlen * sizeof(wchar_t)); RetVal = GetExtendedFileInfoW(FileNameW, metadata, wRetStr, retlen); if (RetVal) //WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, wret, -1, ret, retlen, NULL, NULL); wcstombs(ret, wRetStr, retlen); free(FileNameW); free(wRetStr); #if 0 { char MsgStr[MAX_PATH * 2]; sprintf(MsgStr, "file: %s\nMetadata: %s\nResult: %s", filename, metadata, ret); MessageBoxA(NULL, MsgStr, "GetExtFileInfoA", MB_ICONINFORMATION | MB_OK); } #endif return RetVal; } ================================================ FILE: in_vgm/in_vgm.c ================================================ // Winamp VGM input plug-in // ------------------------ // Programmed by Valley Bell, 2011-2017 // // Made using the Example .RAW plug-in by Justin Frankel and // small parts (mainly dialogues) of the old in_vgm 0.35 by Maxim. /*3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999*/ // #defines used by the Visual C++ project file: // ENABLE_ALL_CORES // DISABLE_HW_SUPPORT // UNICODE_INPUT_PLUGIN (Unicode build only) #include #include #include // for setlocale #include "Winamp/in2.h" #include "Winamp/wa_ipc.h" #include // for info in About message #include "stdbool.h" #include "chips/mamedef.h" // for (U)INTxx types #include "VGMPlay.h" #include "VGMPlay_Intf.h" #include "in_vgm.h" #include "ini_func.h" #ifndef WM_WA_MPEG_EOF // post this to the main window at end of file (after playback as stopped) #define WM_WA_MPEG_EOF WM_USER+2 #endif // configuration. #define NUM_CHN 2 #define BIT_PER_SEC 16 #define SMPL_BYTES (NUM_CHN * (BIT_PER_SEC / 8)) // Function Prototypes from dlg_cfg.c BOOL CALLBACK ConfigDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); void Dialogue_TrackChange(void); // Function Protorypes from dlg_fileinfo.c void SetInfoDlgFile(const char* FileName); void SetInfoDlgFileW(const wchar_t* FileName); UINT32 GetVGZFileSize(const char* FileName); UINT32 GetVGZFileSizeW(const wchar_t* FileName); wchar_t* GetTagStringEngJap(wchar_t* TextEng, wchar_t* TextJap, bool LangMode); UINT32 FormatVGMTag(const UINT32 BufLen, in_char* Buffer, GD3_TAG* FileTag, VGM_HEADER* FileHead); bool LoadPlayingVGMInfo(const char* FileName); bool LoadPlayingVGMInfoW(const wchar_t* FileName); BOOL CALLBACK FileInfoDialogProc(HWND hWndDlg, UINT wMessage, WPARAM wParam, LPARAM lParam); // Function Prototypes BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved); void Config(HWND hWndParent); void About(HWND hWndParent); void Init(void); void FindIniFile(void); void LoadConfigurationFile(void); static void ReadIntoBitfield2(const char* Section, const char* Key, UINT16* Value, UINT8 BitStart, UINT8 BitCount); void SaveConfigurationFile(void); static void WriteFromBitfield(const char* Section, const char* Key, UINT32 Value, UINT8 BitStart, UINT8 BitCount); void Deinit(void); int IsOurFile(const in_char* fn); INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator); int Play(const in_char* FileName); void Pause(void); void Unpause(void); int IsPaused(void); void Stop(void); int GetFileLength(VGM_HEADER* FileHead); int GetLength(void); int GetOutputTime(void); void SetOutputTime(int time_in_ms); void SetVolume(int volume); void SetPan(int pan); void UpdatePlayback(void); int InfoDialog(const in_char* FileName, HWND hWnd); const in_char* GetFileNameTitle(const in_char* FileName); void GetFileInfo(const in_char* filename, in_char* title, int* length_in_ms); void EQ_Set(int on, char data[10], int preamp); DWORD WINAPI DecodeThread(LPVOID b); #define PATH_SIZE (MAX_PATH * 2) // the output module static In_Module WmpMod; // currently playing file (used for getting info on the current file) static in_char CurFileName[PATH_SIZE]; static int decode_pos; // current decoding position (depends on SampleRate) static int decode_pos_ms; // Used for correcting DSP plug-in pitch changes static volatile int seek_needed; // if != -1, it is the point that the decode // thread should seek to, in ms. static volatile bool killDecodeThread = 0; // the kill switch for the decode thread static HANDLE thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread static volatile bool InStopFunc; char IniFilePath[PATH_SIZE + 0x10]; // 16 extra characters for "in_vgm.ini" PLGIN_OPTS Options; extern UINT32 SampleRate; extern UINT32 VGMMaxLoop; extern UINT32 VGMPbRate; extern UINT32 FadeTime; extern UINT32 PauseTime; extern float VolumeLevel; extern bool SurroundSound; extern UINT8 HardStopOldVGMs; //extern bool FadeRAWLog; extern bool DoubleSSGVol; extern UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; extern CHIPS_OPTION ChipOpts[0x02]; extern char* AppPaths[8]; static char AppPathBuffer[PATH_SIZE]; extern VGM_HEADER VGMHead; extern GD3_TAG VGMTag; extern UINT32 VGMMaxLoopM; extern bool VGMEnd; extern bool PausePlay; extern bool EndPlay; HANDLE hPluginInst; // avoid CRT. Evil. Big. Bloated. Only uncomment this code if you are using // 'ignore default libraries' in VC++. Keeps DLL size way down. // /* BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { hPluginInst = hInst; // save for InitConfigDialog return TRUE; } // */ void Config(HWND hWndParent) { // if we had a configuration box we'd want to write it here (using DialogBox, etc) DialogBox(WmpMod.hDllInstance, (LPTSTR)DlgConfigMain, hWndParent, &ConfigDialogProc); return; } void About(HWND hWndParent) { const char* MsgStr = INVGM_TITLE #ifdef UNICODE_INPUT_PLUGIN " (Unicode build)" #endif "\n" "by Valley Bell 2011-2016\n" "\n" "Build date: " __DATE__ " (" INVGM_VERSION ")\n" "\n" "http://vgmrips.net/\n" "\n" "Current status:\n" "VGM file support up to version " VGM_VER_STR "\n" "Currently %u chips are emulated:\n" // CHIP_COUNT "%s\n" // ChipList "\n" "Don\'t be put off by the pre-1.0 version numbers.\n" "This is a non-commercial project and as such it is permanently in beta.\n" "\n" "Using:\n" "ZLib " ZLIB_VERSION " (http://www.zlib.org)\n" //"LZMA SDK 4.40 (http://www.7-zip.org)\n" // currently unused "\n" "Thanks also go to:\n" "Maxim for the first version of in_vgm, one SN76489 and\n" "YM2413 core and some dialogues\n" "MAME team for most sound cores\n" "openMSX team for the YMF278B core\n" "GerbilSoft for the RF5C164 and PWM cores from Gens/GS\n" "DOSBox Team for AdLibEmu (OPL2/3 sound core)\n" "The author of Ootake for the HuC6280 core\n" "rainwarrior for NSFPlay and the EMU2149 and NES cores\n" "superctr for the new C352 core"; char* ChipList; const char* ChipStr; char* FinalMsg; char* TempPnt; UINT8 CurChip; // generate Chip list ChipList = (char*)malloc(CHIP_COUNT * 12); TempPnt = ChipList; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) { if (CurChip && ! (CurChip % 6)) { // replace space with new-line every 6 chips *(TempPnt - 1) = '\n'; } ChipStr = GetAccurateChipName(CurChip, 0xFF); strcpy(TempPnt, ChipStr); TempPnt += strlen(TempPnt); strcpy(TempPnt, ", "); TempPnt += 2; } TempPnt -= 2; *TempPnt = '\0'; FinalMsg = (char*)malloc(strlen(MsgStr) + 0x10 + strlen(ChipList)); sprintf(FinalMsg, MsgStr, CHIP_COUNT, ChipList); free(ChipList); MessageBox(hWndParent, FinalMsg, WmpMod.description, MB_ICONINFORMATION | MB_OK); free(FinalMsg); return; } void Init(void) { char* FileTitle; setlocale(LC_CTYPE, ""); // any one-time initialization goes here (configuration reading, etc) VGMPlay_Init(); // General Init GetModuleFileName(hPluginInst, AppPathBuffer, PATH_SIZE); // get path of in_vgm.dll GetFullPathName(AppPathBuffer, PATH_SIZE, AppPathBuffer, &FileTitle); // find file title *FileTitle = '\0'; // Path 1: dll's directory AppPaths[0x00] = AppPathBuffer; // Path 2: working directory ("\0") AppPaths[0x01] = AppPathBuffer + strlen(AppPathBuffer); LoadConfigurationFile(); VGMPlay_Init2(); // Post-Config-Load Init return; } void FindIniFile(void) { const char* WAIniDir; // get directory for Winamp INI files WAIniDir = (char *)SendMessage(WmpMod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORY); if (WAIniDir == NULL) { // old Winamp strcpy(IniFilePath, AppPathBuffer); } else { // Winamp 5.11+ (with user profiles) strcpy(IniFilePath, WAIniDir); strcat(IniFilePath,"\\Plugins"); // make sure folder exists CreateDirectory(IniFilePath, NULL); strcat(IniFilePath, "\\"); } strcat(IniFilePath, "in_vgm.ini"); return; } void LoadConfigurationFile(void) { UINT8 CurCSet; UINT8 CurChip; UINT8 CurChn; CHIP_OPTS* TempCOpt; char TempStr[0x20]; const char* ChipName; // -- set default values -- // VGMPlay_Init() already sets most default values Options.ImmediateUpdate = false; Options.NoInfoCache = false; Options.SampleRate = 44100; Options.PauseNL = PauseTime; Options.PauseLp = PauseTime; strcpy(Options.TitleFormat, "%t (%g) - %a"); Options.JapTags = false; Options.AppendFM2413 = false; Options.TrimWhitespc = true; Options.StdSeparators = true; Options.TagFallback = false; Options.MLFileType = 0; Options.Enable7z = false; Options.ResetMuting = true; DoubleSSGVol = true; FindIniFile(); // Read actual options ReadIni_Boolean ("General", "ImmdtUpdate", &Options.ImmediateUpdate); ReadIni_Boolean ("General", "NoInfoCache", &Options.NoInfoCache); ReadIni_Integer ("Playback", "SampleRate", &Options.SampleRate); ReadIni_Integer ("Playback", "FadeTime", &FadeTime); ReadIni_Integer ("Playback", "PauseNoLoop", &Options.PauseNL); ReadIni_Integer ("Playback", "PauseLoop", &Options.PauseLp); ReadIni_IntByte ("Playback", "HardStopOld", &HardStopOldVGMs); ReadIni_Float ("Playback", "Volume", &VolumeLevel); ReadIni_Integer ("Playback", "MaxLoops", &VGMMaxLoop); ReadIni_Integer ("Playback", "PlaybackRate", &VGMPbRate); ReadIni_Boolean ("Playback", "DoubleSSGVol", &DoubleSSGVol); ReadIni_IntByte ("Playback", "ResamplMode", &ResampleMode); ReadIni_Integer ("Playback", "ChipSmplRate", &Options.ChipRate); ReadIni_IntByte ("Playback", "ChipSmplMode", &CHIP_SAMPLING_MODE); ReadIni_Boolean ("Playback", "SurroundSnd", &SurroundSound); ReadIni_String ("Tags", "TitleFormat", Options.TitleFormat, 0x80); ReadIni_Boolean ("Tags", "UseJapTags", &Options.JapTags); ReadIni_Boolean ("Tags", "AppendFM2413", &Options.AppendFM2413); ReadIni_Boolean ("Tags", "TrimWhitespc", &Options.TrimWhitespc); ReadIni_Boolean ("Tags", "SeparatorStd", &Options.StdSeparators); ReadIni_Boolean ("Tags", "TagFallback", &Options.TagFallback); ReadIni_Integer ("Tags", "MLFileType", &Options.MLFileType); //ReadIni_Boolean ("Vgm7z", "Enable", &Options.Enable7z); ReadIni_Boolean ("Muting", "Reset", &Options.ResetMuting); TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++) { ReadIni_IntByte("EmuCore", GetChipName(CurChip), &TempCOpt->EmuCore); } for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++) { ChipName = GetChipName(CurChip); sprintf(TempStr, "%s #%u All", ChipName, CurCSet); ReadIni_Boolean("Muting", TempStr, &TempCOpt->Disabled); sprintf(TempStr, "%s #%u", ChipName, CurCSet); ReadIni_Integer("Muting", TempStr, &TempCOpt->ChnMute1); sprintf(TempStr, "%s #%u_%u", ChipName, CurCSet, 2); ReadIni_Integer("Muting", TempStr, &TempCOpt->ChnMute2); sprintf(TempStr, "%s #%u_%u", ChipName, CurCSet, 3); ReadIni_Integer("Muting", TempStr, &TempCOpt->ChnMute3); for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) { if (TempCOpt->ChnCnt > 10) sprintf(TempStr, "%s #%u %02u", ChipName, CurCSet, CurChn); else sprintf(TempStr, "%s #%u %u", ChipName, CurCSet, CurChn); ReadIni_SIntSht("Panning", TempStr, &TempCOpt->Panning[CurChn]); } } } // Additional options // YM2612 ChipName = GetChipName(0x02); TempCOpt = &ChipOpts[0x00].YM2612; sprintf(TempStr, "%s Gens DACHighpass", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); sprintf(TempStr, "%s Gens SSG-EG", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 1, 1); sprintf(TempStr, "%s PseudoStereo", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 2, 1); sprintf(TempStr, "%s NukedType", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 3, 2); // YM2203 ChipName = GetChipName(0x06); TempCOpt = &ChipOpts[0x00].YM2203; sprintf(TempStr, "%s Disable AY", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // YM2608 ChipName = GetChipName(0x07); TempCOpt = &ChipOpts[0x00].YM2608; sprintf(TempStr, "%s Disable AY", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // YM2610 ChipName = GetChipName(0x08); TempCOpt = &ChipOpts[0x00].YM2610; sprintf(TempStr, "%s Disable AY", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // GameBoy ChipName = GetChipName(0x13); TempCOpt = &ChipOpts[0x00].GameBoy; sprintf(TempStr, "%s Boost WaveCh", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // NES ChipName = GetChipName(0x14); TempCOpt = &ChipOpts[0x00].NES; TempCOpt->SpecialFlags &= 0x7FFF; sprintf(TempStr, "%s Shared Opts", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 2); sprintf(TempStr, "%s APU Opts", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 2, 2); sprintf(TempStr, "%s DMC Opts", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 4, 8); sprintf(TempStr, "%s FDS Opts", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 12, 1); // OKIM6258 ChipName = GetChipName(0x17); TempCOpt = &ChipOpts[0x00].OKIM6258; sprintf(TempStr, "%s Internal 10bit", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // SCSP ChipName = GetChipName(0x20); TempCOpt = &ChipOpts[0x00].SCSP; sprintf(TempStr, "%s Bypass DSP", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); // C352 ChipName = GetChipName(0x27); TempCOpt = &ChipOpts[0x00].C352; sprintf(TempStr, "%s Disable Rear", ChipName); ReadIntoBitfield2("ChipOpts", TempStr, &TempCOpt->SpecialFlags, 0, 1); return; } static void ReadIntoBitfield2(const char* Section, const char* Key, UINT16* Value, UINT8 BitStart, UINT8 BitCount) { // Bitfield Read routine (2-byte/16-bit data) UINT16 BitMask; UINT16 NewBits; if (! BitCount) return; BitMask = (1 << BitCount) - 1; NewBits = (*Value >> BitStart) & BitMask; // read old bits, making them the default data ReadIni_SIntSht(Section, Key, &NewBits); // read .ini *Value &= ~(BitMask << BitStart); // clear bit range *Value |= (NewBits & BitMask) << BitStart; // add new bits in return; } void SaveConfigurationFile(void) { UINT8 CurCSet; UINT8 CurChip; UINT8 CurChn; CHIP_OPTS* TempCOpt; char TempStr[0x20]; const char* ChipName; WriteIni_Boolean("General", "ImmdtUpdate", Options.ImmediateUpdate); WriteIni_Boolean("General", "NoInfoCache", Options.NoInfoCache); WriteIni_Integer("Playback", "SampleRate", Options.SampleRate); WriteIni_Integer("Playback", "FadeTime", FadeTime); WriteIni_Integer("Playback", "PauseNoLoop", Options.PauseNL); WriteIni_Integer("Playback", "PauseLoop", Options.PauseLp); WriteIni_Integer("Playback", "HardStopOld", HardStopOldVGMs); WriteIni_Float ("Playback", "Volume", VolumeLevel); WriteIni_Integer("Playback", "MaxLoops", VGMMaxLoop); WriteIni_Integer("Playback", "PlaybackRate", VGMPbRate); WriteIni_Boolean("Playback", "DoubleSSGVol", DoubleSSGVol); WriteIni_Integer("Playback", "ResamplMode", ResampleMode); WriteIni_Integer("Playback", "ChipSmplRate", Options.ChipRate); WriteIni_Integer("Playback", "ChipSmplMode", CHIP_SAMPLING_MODE); WriteIni_Boolean("Playback", "SurroundSnd", SurroundSound); WriteIni_String ("Tags", "TitleFormat", Options.TitleFormat); WriteIni_Boolean("Tags", "UseJapTags", Options.JapTags); WriteIni_Boolean("Tags", "AppendFM2413", Options.AppendFM2413); WriteIni_Boolean("Tags", "TrimWhitespc", Options.TrimWhitespc); WriteIni_Boolean("Tags", "SeparatorStd", Options.StdSeparators); WriteIni_Boolean("Tags", "TagFallback", Options.TagFallback); WriteIni_XInteger("Tags", "MLFileType", Options.MLFileType); //WriteIni_Boolean("Vgm7z", "Enable", Options.Enable7z); TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++) { ChipName = GetChipName(CurChip); WriteIni_Integer("EmuCore", ChipName, TempCOpt->EmuCore); } WriteIni_Boolean("Muting", "Reset", Options.ResetMuting); for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++) { ChipName = GetChipName(CurChip); sprintf(TempStr, "%s #%u All", ChipName, CurCSet, 1); WriteIni_Boolean("Muting", TempStr, TempCOpt->Disabled); sprintf(TempStr, "%s #%u", ChipName, CurCSet, 1); WriteIni_XInteger("Muting", TempStr, TempCOpt->ChnMute1); if (CurChip == 0x07 || CurChip == 0x08 || CurChip == 0x0D) { // YM2608 (ADPCM), YM2610 (ADPCM), YMF278B (WaveTable) sprintf(TempStr, "%s #%u_%u", ChipName, CurCSet, 2); WriteIni_XInteger("Muting", TempStr, TempCOpt->ChnMute2); } if (CurChip == 0x06 || CurChip == 0x07 || CurChip == 0x08) { // YM2203, YM2608, YM2610 (all AY8910) sprintf(TempStr, "%s #%u_%u", ChipName, CurCSet, 3); WriteIni_XInteger("Muting", TempStr, TempCOpt->ChnMute3); } for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) { if (TempCOpt->ChnCnt > 10) sprintf(TempStr, "%s #%u %02u", ChipName, CurCSet, CurChn); else sprintf(TempStr, "%s #%u %u", ChipName, CurCSet, CurChn); WriteIni_SInteger("Panning", TempStr, TempCOpt->Panning[CurChn]); } } } // Additional options // YM2612 ChipName = GetChipName(0x02); TempCOpt = &ChipOpts[0x00].YM2612; sprintf(TempStr, "%s Gens DACHighpass", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); sprintf(TempStr, "%s Gens SSG-EG", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 1, 1); sprintf(TempStr, "%s PseudoStereo", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 2, 1); sprintf(TempStr, "%s NukedType", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 3, 2); // YM2203 ChipName = GetChipName(0x06); TempCOpt = &ChipOpts[0x00].YM2203; sprintf(TempStr, "%s Disable AY", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // YM2608 ChipName = GetChipName(0x07); TempCOpt = &ChipOpts[0x00].YM2608; sprintf(TempStr, "%s Disable AY", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // YM2610 ChipName = GetChipName(0x08); TempCOpt = &ChipOpts[0x00].YM2610; sprintf(TempStr, "%s Disable AY", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // GameBoy ChipName = GetChipName(0x13); TempCOpt = &ChipOpts[0x00].GameBoy; sprintf(TempStr, "%s Boost WaveCh", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // NES ChipName = GetChipName(0x14); TempCOpt = &ChipOpts[0x00].NES; sprintf(TempStr, "%s Shared Opts", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 2); sprintf(TempStr, "%s APU Opts", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 2, 2); sprintf(TempStr, "%s DMC Opts", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 4, 8); sprintf(TempStr, "%s FDS Opts", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 12, 1); // OKIM6258 ChipName = GetChipName(0x17); TempCOpt = &ChipOpts[0x00].OKIM6258; sprintf(TempStr, "%s Internal 10bit", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // SCSP ChipName = GetChipName(0x20); TempCOpt = &ChipOpts[0x00].SCSP; sprintf(TempStr, "%s Bypass DSP", ChipName); //TempCOpt->SpecialFlags |= 0x01; // force SCSP DSP bypass upon next loading WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); // C352 ChipName = GetChipName(0x27); TempCOpt = &ChipOpts[0x00].C352; sprintf(TempStr, "%s Disable Rear", ChipName); WriteFromBitfield("ChipOpts", TempStr, TempCOpt->SpecialFlags, 0, 1); return; } static void WriteFromBitfield(const char* Section, const char* Key, UINT32 Value, UINT8 BitStart, UINT8 BitCount) { UINT32 BitMask; UINT32 WrtBits; if (! BitCount) return; BitMask = (1 << BitCount) - 1; WrtBits = Value >> BitStart; WrtBits &= BitMask; WriteIni_XInteger(Section, Key, WrtBits); return; } void Deinit(void) { // one-time deinit, such as memory freeing SaveConfigurationFile(); VGMPlay_Deinit(); return; } int IsOurFile(const in_char* fn) { // used for detecting URL streams - currently unused. // return ! strncmp(fn,"http://",7); to detect HTTP streams, etc return 0; } INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator) { return (UINT32)((Number * Numerator + Denominator / 2) / Denominator); } // called when winamp wants to play a file int Play(const in_char* FileName) { UINT32 TempLng; int maxlatency; int thread_id; PausePlay = false; decode_pos = 0; decode_pos_ms = 0; seek_needed = -1; SampleRate = Options.SampleRate; CHIP_SAMPLE_RATE = Options.ChipRate ? Options.ChipRate : Options.SampleRate; // return -1 - jump to next file in playlist // return +1 - stop the playlist #ifndef UNICODE_INPUT_PLUGIN if (! OpenVGMFile(FileName)) { if (GetGZFileLength(FileName) == 0xFFFFFFFF) return -1; // file not found else return +1; // file invalid } LoadPlayingVGMInfo(FileName); strcpy(CurFileName, FileName); #else if (! OpenVGMFileW(FileName)) { if (GetGZFileLengthW(FileName) == 0xFFFFFFFF) return -1; // file not found else return +1; // file invalid } LoadPlayingVGMInfoW(FileName); wcscpy(CurFileName, FileName); #endif // -1 and -1 are to specify buffer and prebuffer lengths. // -1 means to use the default, which all input plug-ins should really do. maxlatency = WmpMod.outMod->Open(SampleRate, NUM_CHN, BIT_PER_SEC, -1, -1); if (maxlatency < 0) // error opening device return +1; if (! VGMHead.lngLoopOffset) PauseTime = Options.PauseNL; else PauseTime = Options.PauseLp; PlayVGM(); #ifndef UNICODE_INPUT_PLUGIN TempLng = GetVGZFileSize(FileName); #else TempLng = GetVGZFileSizeW(FileName); #endif if (! TempLng) { if (VGMHead.lngGD3Offset) TempLng = VGMHead.lngGD3Offset - VGMHead.lngDataOffset; else TempLng = VGMHead.lngEOFOffset - VGMHead.lngDataOffset; } // Bit/Sec = (TrackBytes * 8) / TrackTime // kbps = Bytes * (8 * SampleRate) / (Samples * 1000) if (VGMHead.lngTotalSamples) TempLng = MulDivRound(TempLng, CalcSampleMSec(8000, 0x03), (UINT64)VGMHead.lngTotalSamples * 1000); else TempLng = 0; WmpMod.SetInfo(TempLng, (SampleRate + 500) / 1000, NUM_CHN, 1); // initialize visualization stuff WmpMod.SAVSAInit(maxlatency, SampleRate); WmpMod.VSASetInfo(SampleRate, NUM_CHN); // set the output plug-ins default volume. // volume is 0-255, -666 is a token for current volume. WmpMod.outMod->SetVolume(-666); // launch decode thread killDecodeThread = 0; thread_handle = CreateThread(NULL, 0, &DecodeThread, NULL, 0, &thread_id); Dialogue_TrackChange(); return 0; } // standard pause implementation void Pause(void) { PauseVGM(true); WmpMod.outMod->Pause(PausePlay); return; } void Unpause(void) { PauseVGM(false); WmpMod.outMod->Pause(PausePlay); return; } int IsPaused(void) { return PausePlay; } // stop playing. void Stop(void) { if (InStopFunc) return; InStopFunc = true; // TODO: add Mutex for this block. // Stupid XMPlay seems to call Stop() twice in 2 seperate threads. if (thread_handle != INVALID_HANDLE_VALUE) { killDecodeThread = 1; if (WaitForSingleObject(thread_handle, 10000) == WAIT_TIMEOUT) { MessageBox(WmpMod.hMainWindow, "Error asking thread to die!\n", "Error killing decode thread", 0); TerminateThread(thread_handle,0); } CloseHandle(thread_handle); thread_handle = INVALID_HANDLE_VALUE; } // close output system WmpMod.outMod->Close(); // deinitialize visualization WmpMod.SAVSADeInit(); StopVGM(); if (Options.ResetMuting) { UINT8 CurChip; UINT8 CurCSet; CHIP_OPTS* TempCOpt; for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) { TempCOpt = (CHIP_OPTS*)&ChipOpts[CurCSet]; for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++) { TempCOpt->Disabled = false; TempCOpt->ChnMute1 = 0x00; TempCOpt->ChnMute2 = 0x00; TempCOpt->ChnMute3 = 0x00; } } // that would just make the check boxes flash during track change // and Winamp's main window is locked when the configuration is open // so I do it only when muting is reset Dialogue_TrackChange(); } CloseVGMFile(); LoadPlayingVGMInfo(NULL); InStopFunc = false; return; } int GetFileLength(VGM_HEADER* FileHead) { UINT32 SmplCnt; UINT32 MSecCnt; INT32 LoopCnt; if (FileHead == &VGMHead) { LoopCnt = VGMMaxLoopM; } else { LoopCnt = (VGMMaxLoop * FileHead->bytLoopModifier + 0x08) / 0x10 - FileHead->bytLoopBase; if (LoopCnt < 0x01) LoopCnt = 0x01; } if (! LoopCnt && FileHead->lngLoopSamples) return -1000; // Note: SmplCnt is ALWAYS 44.1 KHz, VGM's native sample rate SmplCnt = FileHead->lngTotalSamples + FileHead->lngLoopSamples * (LoopCnt - 0x01); if (FileHead == &VGMHead) MSecCnt = CalcSampleMSec(SmplCnt, 0x02); else MSecCnt = CalcSampleMSecExt(SmplCnt, 0x02, FileHead); if (FileHead->lngLoopSamples) MSecCnt += FadeTime + Options.PauseLp; else MSecCnt += Options.PauseNL; return MSecCnt; } int GetLength(void) // return length of playing track { return GetFileLength(&VGMHead); } // returns current output position, in ms. // you could just use return mod.outMod->GetOutputTime(), // but the dsp plug-ins that do tempo changing tend to make that wrong. int GetOutputTime(void) { if (seek_needed == -1) // seek is needed. return decode_pos_ms + (WmpMod.outMod->GetOutputTime() - WmpMod.outMod->GetWrittenTime()); else return seek_needed; } void SetOutputTime(int time_in_ms) // for seeking { // Winamp seems send negative seeking values, // when GetLength returns -1000. if (time_in_ms >= 0) seek_needed = time_in_ms; return; } void SetVolume(int volume) { WmpMod.outMod->SetVolume(volume); return; } void SetPan(int pan) { WmpMod.outMod->SetPan(pan); return; } void UpdatePlayback(void) { if (WmpMod.outMod == NULL || ! WmpMod.outMod->IsPlaying()) return; if (Options.ImmediateUpdate) { // add 30 ms - else it sounds like you seek back a little SetOutputTime(GetOutputTime() + 30); } return; } int InfoDialog(const in_char* FileName, HWND hWnd) { //MessageBox(hWnd, "No File Info. Yet.", "File Info", MB_OK); #ifndef UNICODE_INPUT_PLUGIN SetInfoDlgFile(FileName); #else SetInfoDlgFileW(FileName); #endif return DialogBox(WmpMod.hDllInstance, (LPTSTR)DlgFileInfo, hWnd, &FileInfoDialogProc); } const in_char* GetFileNameTitle(const in_char* FileName) { const in_char* TempPnt; #ifndef UNICODE_INPUT_PLUGIN TempPnt = FileName + strlen(FileName) - 0x01; while(TempPnt >= FileName && *TempPnt != '\\') TempPnt --; #else TempPnt = FileName + wcslen(FileName) - 0x01; while(TempPnt >= FileName && *TempPnt != L'\\') TempPnt --; #endif return TempPnt + 0x01; } // This is an odd function. It is used to get the title and/or length of a track. // If filename is either NULL or of length 0, it means you should // return the info of LastFileName. Otherwise, return the information // for the file in filename. void GetFileInfo(const in_char* filename, in_char* title, int* length_in_ms) { UINT32 FileSize; VGM_HEADER FileHead; GD3_TAG FileTag; const wchar_t* Tag_TrackName; #if 0 { char MsgStr[MAX_PATH * 2]; sprintf(MsgStr, "Calling GetFileInfo() with file:\n%ls", filename); MessageBoxA(WmpMod.hMainWindow, MsgStr, WmpMod.description, MB_ICONINFORMATION | MB_OK); } #endif // Note: If filename is be null OR of length zero, return info about the current file. if (filename == NULL || filename[0x00] == '\0') { // called when the Play-Button is pressed if (length_in_ms != NULL) *length_in_ms = GetLength(); if (title != NULL) // get non-path portion.of filename { if (! VGMTag.lngVersion) Tag_TrackName = NULL; else Tag_TrackName = GetTagStringEngJap(VGMTag.strTrackNameE, VGMTag.strTrackNameJ, false); if (Tag_TrackName != NULL) { //_snprintf(title, GETFILEINFO_TITLE_LENGTH, "%ls (%ls)", // VGMTag.strTrackNameE, VGMTag.strGameNameE); FormatVGMTag(GETFILEINFO_TITLE_LENGTH, title, &VGMTag, &VGMHead); } else { #ifndef UNICODE_INPUT_PLUGIN strncpy(title, GetFileNameTitle(CurFileName), GETFILEINFO_TITLE_LENGTH); #else wcsncpy(title, GetFileNameTitle(CurFileName), GETFILEINFO_TITLE_LENGTH); #endif } } } else // some other file { #ifndef UNICODE_INPUT_PLUGIN //FileSize = GetVGMFileInfo(filename, (length_in_ms != NULL) ? &FileHead : NULL, // (title != NULL) ? &FileTag : NULL); FileSize = GetVGMFileInfo(filename, &FileHead, (title != NULL) ? &FileTag : NULL); #else FileSize = GetVGMFileInfoW(filename, &FileHead, (title != NULL) ? &FileTag : NULL); #endif if (length_in_ms != NULL) { if (FileSize) *length_in_ms = GetFileLength(&FileHead); else *length_in_ms = -1000; // File not found } if (title != NULL) { if (! FileSize || ! FileTag.lngVersion) Tag_TrackName = NULL; else Tag_TrackName = GetTagStringEngJap(FileTag.strTrackNameE, FileTag.strTrackNameJ, false); if (Tag_TrackName != NULL) { //_snprintf(title, GETFILEINFO_TITLE_LENGTH, "%ls (%ls)", // FileTag.strTrackNameE, FileTag.strGameNameE); FormatVGMTag(GETFILEINFO_TITLE_LENGTH, title, &FileTag, &FileHead); FreeGD3Tag(&FileTag); } else { #ifndef UNICODE_INPUT_PLUGIN strncpy(title, GetFileNameTitle(filename), GETFILEINFO_TITLE_LENGTH); #else wcsncpy(title, GetFileNameTitle(filename), GETFILEINFO_TITLE_LENGTH); #endif } } } return; } void EQ_Set(int on, char data[10], int preamp) { // unsupported // Format: each data byte is 0-63 (+20db <-> -20db) and preamp is the same. return; } #define RENDER_SAMPLES 576 // visualizations look best with 576 samples #define BLOCK_SIZE (RENDER_SAMPLES * SMPL_BYTES) DWORD WINAPI DecodeThread(LPVOID b) { char sample_buffer[BLOCK_SIZE * 2]; // has to be twice as big as the blocksize UINT32 RetSamples; SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); while (! killDecodeThread) { if (seek_needed != -1) // seek is needed. { decode_pos = MulDivRound(seek_needed, SampleRate, 1000); //decode_pos_ms = MulDivRound(decode_pos, 1000, SampleRate); decode_pos_ms = seek_needed; SeekVGM(false, decode_pos); // flush output device and set output to seek position WmpMod.outMod->Flush(decode_pos_ms); seek_needed = -1; } if (EndPlay /*|| VGMEnd*/) { // Playback finished WmpMod.outMod->CanWrite(); // needed for some output drivers if (! WmpMod.outMod->IsPlaying()) { // we're done playing, so tell Winamp and quit the thread. PostMessage(WmpMod.hMainWindow, WM_WA_MPEG_EOF, 0, 0); break; } Sleep(10); // give a little CPU time back to the system. } else if (WmpMod.outMod->CanWrite() >= (BLOCK_SIZE * (WmpMod.dsp_isactive() ? 2 : 1))) // CanWrite() returns the number of bytes you can write, so we check that // to the block size. the reason we multiply the block size by two if // mod.dsp_isactive() is that DSP plug-ins can change it by up to a // factor of two (for tempo adjustment). { RetSamples = FillBuffer((WAVE_16BS*)sample_buffer, RENDER_SAMPLES); if (RetSamples) { // send data to the visualization and dsp systems WmpMod.SAAddPCMData(sample_buffer, NUM_CHN, BIT_PER_SEC, decode_pos_ms); WmpMod.VSAAddPCMData(sample_buffer, NUM_CHN, BIT_PER_SEC, decode_pos_ms); if (WmpMod.dsp_isactive()) RetSamples = WmpMod.dsp_dosamples((short*)sample_buffer, RetSamples, BIT_PER_SEC, NUM_CHN, SampleRate); WmpMod.outMod->Write(sample_buffer, RetSamples * SMPL_BYTES); decode_pos += RetSamples; decode_pos_ms = MulDivRound(decode_pos, 1000, SampleRate); } else { //done = true; } } else { Sleep(20); // Wait a little, until we can write again. } } return 0; } // module definition. In_Module WmpMod = { IN_VER, // defined in IN2.H INVGM_TITLE_FULL // winamp runs on both alpha systems and x86 ones. :) #ifdef __alpha " (AXP)" //#else // " (x86)" #endif , 0, // hMainWindow (filled in by winamp) 0, // hDllInstance (filled in by winamp) // Format List: "EXT\0Description\0EXT\0Description\0 ..." "vgm;vgz\0VGM Audio Files (*.vgm; *.vgz)\0", //"vgm;vgz\0VGM Audio Files (*.vgm; *.vgz)\0vgm7z\0VGM7Z Archive (*.vgm7z)\0", 1, // is_seekable IN_MODULE_FLAG_USES_OUTPUT_PLUGIN, &Config, &About, &Init, &Deinit, &GetFileInfo, &InfoDialog, &IsOurFile, &Play, &Pause, &Unpause, &IsPaused, &Stop, &GetLength, &GetOutputTime, &SetOutputTime, &SetVolume, &SetPan, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // visualization calls filled in by winamp NULL, NULL, // dsp calls filled in by winamp &EQ_Set, NULL, // setinfo call filled in by winamp NULL // out_mod filled in by winamp }; // exported symbol. Returns output module. __declspec(dllexport) In_Module* winampGetInModule2(void) { return &WmpMod; } ================================================ FILE: in_vgm/in_vgm.dsp ================================================ # Microsoft Developer Studio Project File - Name="in_vgm" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** NICHT BEARBEITEN ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=in_vgm - Win32 Debug !MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE !MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl !MESSAGE !MESSAGE NMAKE /f "in_vgm.mak". !MESSAGE !MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben !MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: !MESSAGE !MESSAGE NMAKE /f "in_vgm.mak" CFG="in_vgm - Win32 Debug" !MESSAGE !MESSAGE Fr die Konfiguration stehen zur Auswahl: !MESSAGE !MESSAGE "in_vgm - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library") !MESSAGE "in_vgm - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library") !MESSAGE "in_vgm - Win32 Unicode Release" (basierend auf "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "in_vgm - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "../VGMPlay" /I "../VGMPlay/zlib" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "DISABLE_HW_SUPPORT" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 zlib.lib kernel32.lib user32.lib comctl32.lib advapi32.lib /nologo /dll /machine:I386 /libpath:"../VGMPlay/zlib" !ELSEIF "$(CFG)" == "in_vgm - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "../VGMPlay" /I "../VGMPlay/zlib" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "DISABLE_HW_SUPPORT" /D "UNICODE_INPUT_PLUGIN" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 zlib.lib kernel32.lib user32.lib comctl32.lib advapi32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"../VGMPlay/zlib" # Begin Special Build Tool SOURCE="$(InputPath)" PostBuild_Cmds=copy Debug\in_vgm.dll D:\Programme\Winamp5_03a\Plugins\ copy Debug\in_vgm.dll xmplay36\ # End Special Build Tool !ELSEIF "$(CFG)" == "in_vgm - Win32 Unicode Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "in_vgm___Win32_Unicode_Release" # PROP BASE Intermediate_Dir "in_vgm___Win32_Unicode_Release" # PROP BASE Ignore_Export_Lib 0 # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "ReleaseW" # PROP Intermediate_Dir "ReleaseW" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "../VGMPlay" /I "../VGMPlay/zlib" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "DISABLE_HW_SUPPORT" /FD /c # SUBTRACT BASE CPP /YX # ADD CPP /nologo /MD /W3 /GX /O2 /I "../VGMPlay" /I "../VGMPlay/zlib" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_VGM_EXPORTS" /D "VGM_LITTLE_ENDIAN" /D "ENABLE_ALL_CORES" /D "DISABLE_HW_SUPPORT" /D "UNICODE_INPUT_PLUGIN" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 zlib.lib kernel32.lib user32.lib comctl32.lib advapi32.lib /nologo /dll /machine:I386 /libpath:"../VGMPlay/zlib" # ADD LINK32 zlib.lib kernel32.lib user32.lib comctl32.lib advapi32.lib /nologo /dll /machine:I386 /out:"ReleaseW/in_vgm_w.dll" /libpath:"../VGMPlay/zlib" !ENDIF # Begin Target # Name "in_vgm - Win32 Release" # Name "in_vgm - Win32 Debug" # Name "in_vgm - Win32 Unicode Release" # Begin Group "Source Files" # PROP Default_Filter "c;def" # Begin Source File SOURCE=.\dlg_cfg.c # End Source File # Begin Source File SOURCE=.\dlg_fileinfo.c # End Source File # Begin Source File SOURCE=.\in_vgm.c # End Source File # Begin Source File SOURCE=.\ini_func.c # End Source File # Begin Source File SOURCE=..\VGMPlay\pt_ioctl.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=..\VGMPlay\VGMPlay.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h" # Begin Source File SOURCE=.\in_vgm.h # End Source File # Begin Source File SOURCE=.\ini_func.h # End Source File # Begin Source File SOURCE=..\VGMPlay\PortTalk_IOCTL.h # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=..\VGMPlay\stdbool.h # End Source File # Begin Source File SOURCE=..\VGMPlay\VGMFile.h # End Source File # Begin Source File SOURCE=..\VGMPlay\VGMPlay.h # End Source File # Begin Source File SOURCE=..\VGMPlay\VGMPlay_Intf.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=.\icon.ico # End Source File # Begin Source File SOURCE=.\in_vgm.rc # End Source File # Begin Source File SOURCE=.\images\logo.bmp # End Source File # Begin Source File SOURCE=.\resource.h # End Source File # Begin Source File SOURCE=.\images\tabicons.bmp # End Source File # End Group # Begin Group "Sound Core" # PROP Default_Filter "" # Begin Source File SOURCE=..\VGMPlay\chips\2151intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2151intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2203intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2203intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2413intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2413intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2608intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2608intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2610intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2610intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2612intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\2612intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\262intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\262intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\3526intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\3526intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\3812intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\3812intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\8950intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\8950intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\adlibemu.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\adlibemu_opl2.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\adlibemu_opl3.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ay8910.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ay8910.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ay_intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ay_intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c140.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c140.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c352.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c352.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c6280.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c6280.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c6280intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\c6280intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ChipIncl.h # End Source File # Begin Source File SOURCE=..\VGMPlay\ChipMapper.c # End Source File # Begin Source File SOURCE=..\VGMPlay\ChipMapper.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\dac_control.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\dac_control.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\emu2149.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\emu2149.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\emu2413.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\emu2413.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\emutypes.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\es5503.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\es5503.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\es5506.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\es5506.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\fm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\fm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\fm2612.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\fmopl.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\fmopl.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\gb.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\gb.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\iremga20.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\iremga20.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k051649.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k051649.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k053260.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k053260.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k054539.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\k054539.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\mamedef.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\multipcm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\multipcm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\nes_apu.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\nes_apu.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\nes_defs.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\nes_intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\nes_intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_apu.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_apu.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_dmc.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_dmc.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_fds.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\np_nes_fds.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\okim6258.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\okim6258.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\okim6295.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\okim6295.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\Ootake_PSG.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\Ootake_PSG.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opl.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opl.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opll.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opll.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\opm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\panning.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\panning.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\pokey.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\pokey.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\pwm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\pwm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_ctr.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_ctr.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_mame.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\qsound_mame.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\rf5c68.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\rf5c68.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\saa1099.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\saa1099.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scd_pcm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scd_pcm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scsp.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scsp.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scspdsp.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scspdsp.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\scsplfo.c # PROP Exclude_From_Build 1 # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\segapcm.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\segapcm.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn76489.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn76489.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn76496.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn76496.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn764intf.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\sn764intf.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\upd7759.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\upd7759.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\vrc7tone.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\vsu.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\vsu.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ws_audio.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ws_audio.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ws_initialIo.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\x1_010.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\x1_010.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2151.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2151.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2413.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2413.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2612.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym2612.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym3438.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ym3438.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymdeltat.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymdeltat.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf262.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf262.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf271.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf271.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf278b.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymf278b.h # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymz280b.c # End Source File # Begin Source File SOURCE=..\VGMPlay\chips\ymz280b.h # End Source File # End Group # End Target # End Project ================================================ FILE: in_vgm/in_vgm.dsw ================================================ Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELSCHT WERDEN! ############################################################################### Project: "in_vgm"=".\in_vgm.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### ================================================ FILE: in_vgm/in_vgm.h ================================================ //#include "../VGMPlay.h" #if defined(APLHA) #define VER_EXTRA " alpha" #define VER_DATE " ("__DATE__" "__TIME__")" #elif defined(BETA) #define VER_EXTRA " beta" #define VER_DATE " ("__DATE__" "__TIME__")" #else #define VER_EXTRA "" #define VER_DATE "" #endif #define INVGM_VERSION VGMPLAY_VER_STR VER_EXTRA #define INVGM_TITLE "VGM Input Plugin v" INVGM_VERSION #define INVGM_TITLE_FULL "VGM Input Plugin v" INVGM_VERSION VER_DATE typedef struct plugin_options { bool ImmediateUpdate; bool NoInfoCache; UINT32 SampleRate; UINT32 ChipRate; UINT32 PauseNL; UINT32 PauseLp; char TitleFormat[0x80]; bool JapTags; bool AppendFM2413; bool TrimWhitespc; bool StdSeparators; bool TagFallback; UINT32 MLFileType; bool Enable7z; bool ResetMuting; } PLGIN_OPTS; extern PLGIN_OPTS Options; #include "resource.h" ================================================ FILE: in_vgm/in_vgm.rc ================================================ //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // Neutral resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) #ifdef _WIN32 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. MainIcon ICON DISCARDABLE "icon.ico" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // TabIcons BITMAP DISCARDABLE "images\\tabicons.bmp" LogoBitmap BITMAP DISCARDABLE "images\\logo.bmp" #endif // Neutral resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Deutsch (Deutschland) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) #ifdef _WIN32 LANGUAGE LANG_GERMAN, SUBLANG_GERMAN #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // Deutsch (Deutschland) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Englisch (USA) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // DlgCfgTags DIALOG DISCARDABLE 0, 0, 270, 166 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg" BEGIN GROUPBOX "Legacy title &formatting",IDC_STATIC,5,5,100,155 EDITTEXT TitleFormatText,10,18,86,12,ES_AUTOHSCROLL LTEXT "%t\n%a\n%g\n%d\n%s\n%c",IDC_STATIC,10,35,15,51,NOT WS_GROUP LTEXT "Track title*\nTrack author*\nGame name*\nGame release date\nSystem name*\nFile creator", IDC_STATIC,30,35,65,50,NOT WS_GROUP LTEXT "* use %?j for Japanese text if available",IDC_STATIC,10, 85,85,20,NOT WS_GROUP LTEXT "Default: %t (%g) - %a",IDC_STATIC,10,105,85,10,NOT WS_GROUP LTEXT "Note: this has no effect when Advanced Title Formatting is enabled in Winamp.", IDC_STATIC,10,120,85,37,NOT WS_GROUP GROUPBOX "Data formatting",IDC_STATIC,110,5,150,99 CONTROL "Prefer &Japanese text",PreferJapCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,115,15,140,15 CONTROL "Append ""(&FM)"" to YM2413 game titles",FM2413Check, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,28,140,15 CONTROL "&Trim whitespace",TrimWhitespcCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,115,44,140,10 CONTROL "Standardise &separators (to ,)",StdSeparatorsCheck, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,58,140,10 GROUPBOX "File type",IDC_STATIC,110,110,150,50 LTEXT "Return file &type ID:",IDC_STATIC,115,121,65,12, SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT MLTypeText,180,121,74,12,ES_AUTOHSCROLL | ES_NUMBER CTEXT "0 = audio, 1 = video\nSee the documentation for the usage.", IDC_STATIC,115,135,140,20,NOT WS_GROUP CONTROL "Tag fallb&ack in File Info Dialog",TagFallbackCheck, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,115,72,140,10 CONTROL "Don't &cache file information",NoInfoCacheCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,115,86,140,10 END DlgConfigMain DIALOG DISCARDABLE 0, 0, 286, 210 STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "VGM plugin configuration" FONT 8, "MS Shell Dlg" BEGIN CONTROL "Immediate &update",ImmediateUpdCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,6,195,83,10 PUSHBUTTON "Help",HelpButton,108,193,52,14,WS_DISABLED PUSHBUTTON "Cancel",IDCANCEL,169,193,52,14 DEFPUSHBUTTON "OK",IDOK,230,193,52,14 CONTROL "Tab1",TabCollection,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,2,2,280,187 END DlgFileInfo DIALOG DISCARDABLE 0, 0, 379, 143 STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "VGM File & GD3 Tag Information" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK",IDOK,325,127,52,14 GROUPBOX "Track",IDC_STATIC,2,0,173,40 LTEXT "&Title",IDC_STATIC,7,10,32,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT TrkTitleText,40,10,130,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Author",IDC_STATIC,7,23,32,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT TrkAuthorText,40,23,130,12,ES_AUTOHSCROLL | ES_READONLY GROUPBOX "Game",IDC_STATIC,2,40,173,52 LTEXT "Na&me",IDC_STATIC,7,49,32,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT GameNameText,40,49,130,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&System",IDC_STATIC,7,62,32,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT GameSysText,40,62,130,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Date",IDC_STATIC,7,75,32,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT GameDateText,40,75,130,12,ES_AUTOHSCROLL | ES_READONLY GROUPBOX "VGM file",IDC_STATIC,179,0,198,125 LTEXT "&Filename",IDC_STATIC,185,10,39,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT VGMFileText,224,10,101,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Creator",IDC_STATIC,185,23,39,12,SS_CENTERIMAGE EDITTEXT VGMCrtText,224,23,101,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Version",IDC_STATIC,185,36,39,12,SS_CENTERIMAGE EDITTEXT VGMVerText,224,36,23,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Gain",IDC_STATIC,259,36,31,12,SS_CENTERIMAGE EDITTEXT VGMGainText,290,36,35,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "Si&ze",IDC_STATIC,185,49,39,12,SS_CENTERIMAGE EDITTEXT VGMSizeText,224,49,101,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Length",IDC_STATIC,185,62,39,12,SS_CENTERIMAGE EDITTEXT VGMLenText,224,62,148,12,ES_AUTOHSCROLL | ES_READONLY LTEXT "&Notes:",IDC_STATIC,185,77,39,8 EDITTEXT VGMNotesText,185,87,188,34,ES_MULTILINE | ES_READONLY | WS_VSCROLL LTEXT "Chips &used:",IDC_STATIC,329,10,39,12,SS_CENTERIMAGE | NOT WS_GROUP EDITTEXT ChipUseText,329,23,42,39,ES_MULTILINE | ES_READONLY | NOT WS_BORDER GROUPBOX "Language",IDC_STATIC,123,92,52,33 CONTROL "&English",LangEngCheck,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,127,103,39,8 CONTROL "&Japanese",LangJapCheck,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,127,113,43,8 PUSHBUTTON "View in &browser",BrwsrInfoButton,123,127,80,14, WS_DISABLED PUSHBUTTON "Configure &plugin...",ConfigPluginButton,206,127,80,14 CONTROL 103,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,2, 95,119,46 END DlgCfgPlayback DIALOG DISCARDABLE 0, 0, 270, 166 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg" BEGIN GROUPBOX "Sample Rates",IDC_STATIC,5,5,257,40 LTEXT "&Playback Rate",IDC_STATIC,11,16,54,10,NOT WS_GROUP EDITTEXT SmplPbRateText,65,14,33,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "Hz",IDC_STATIC,102,16,15,10,NOT WS_GROUP LTEXT "Re&sampling Mode",IDC_STATIC,117,16,59,10,NOT WS_GROUP COMBOBOX ResmpModeList,182,14,74,43,CBS_DROPDOWNLIST | WS_TABSTOP LTEXT "C&hip Rate",SmplChipRateLabel,11,30,54,10,NOT WS_GROUP EDITTEXT SmplChipRateText,65,29,33,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "Hz",SmplChipRateHzLabel,102,31,15,10,NOT WS_GROUP LTEXT "Chip Sample &Mode",IDC_STATIC,117,31,59,10,NOT WS_GROUP COMBOBOX ChipSmpModeList,182,29,74,43,CBS_DROPDOWNLIST | WS_TABSTOP CONTROL """S&urround"" Sound",SurroundCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,5,52,85,10 GROUPBOX "Loops",IDC_STATIC,5,66,150,45 LTEXT "Play &loops",IDC_STATIC,11,79,38,10,NOT WS_GROUP EDITTEXT LoopText,49,77,27,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "times",LoopTimesLabel,81,79,22,10,NOT WS_GROUP LTEXT "(0 = infinite)",IDC_STATIC,107,79,38,10,NOT WS_GROUP LTEXT "&Fade over",IDC_STATIC,11,93,38,10,NOT WS_GROUP EDITTEXT FadeText,49,91,27,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "ms",IDC_STATIC,81,93,22,10,NOT WS_GROUP GROUPBOX "Pause between tracks",IDC_STATIC,5,115,100,45 LTEXT "&Non-looping",IDC_STATIC,11,127,38,10,NOT WS_GROUP EDITTEXT PauseNlText,54,126,27,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "ms",IDC_STATIC,86,128,15,10,NOT WS_GROUP LTEXT "Loop&ing",IDC_STATIC,11,143,38,10,NOT WS_GROUP EDITTEXT PauseLpText,54,141,27,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "ms",IDC_STATIC,86,143,15,10,NOT WS_GROUP GROUPBOX "Playback rate",IDC_STATIC,165,48,97,63 CONTROL "&Recorded rate",RateRecRadio,"Button", BS_AUTORADIOBUTTON,171,59,65,10 CONTROL "&60 Hz",Rate60HzRadio,"Button",BS_AUTORADIOBUTTON,171, 71,36,10 CONTROL "&50 Hz",Rate50HzRadio,"Button",BS_AUTORADIOBUTTON,171, 83,36,10 CONTROL "&Other:",RateOtherRadio,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,171,95,35,10 EDITTEXT RateText,208,94,22,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER LTEXT "Hz",RateLabel,235,96,17,10,NOT WS_GROUP GROUPBOX "&Volume",IDC_STATIC,115,115,147,45 CONTROL "Slider1",VolumeSlider,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,117,126,143,15 EDITTEXT VolumeText,156,146,65,12,ES_CENTER | ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER | NOT WS_BORDER END DlgCfgVgm7z DIALOG DISCARDABLE 0, 0, 270, 166 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg" BEGIN LTEXT "vgm7z-files are currently not supported.\n\nBut this will change someday.", IDC_STATIC,5,5,245,35 END DlgCfgMuting DIALOG DISCARDABLE 0, 0, 270, 166 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg" BEGIN LTEXT "&Select Chip:",IDC_STATIC,10,12,50,10 COMBOBOX MutingChipList,60,10,60,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX MutingChipNumList,130,10,60,80,CBS_DROPDOWNLIST | WS_TABSTOP CONTROL "Channel &1",MuteChn1Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,30,65,10 CONTROL "Channel &2",MuteChn2Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,45,65,10 CONTROL "Channel &3",MuteChn3Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,65,10 CONTROL "Channel &4",MuteChn4Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,75,65,10 CONTROL "Channel &5",MuteChn5Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,90,65,10 CONTROL "Channel &6",MuteChn6Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,105,65,10 CONTROL "Channel &7",MuteChn7Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,120,65,10 CONTROL "Channel &8",MuteChn8Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,135,65,10 CONTROL "Channel &9",MuteChn9Check,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,100,30,65,10 CONTROL "Channel 10 (&A)",MuteChn10Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,45,65,10 CONTROL "Channel 11 (&B)",MuteChn11Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,60,65,10 CONTROL "Channel 12 (&C)",MuteChn12Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,75,65,10 CONTROL "Channel 13 (&D)",MuteChn13Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,90,65,10 CONTROL "Channel 14 (&E)",MuteChn14Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,105,65,10 CONTROL "Channel 15 (&F)",MuteChn15Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,120,65,10 CONTROL "Channel 16 (&G)",MuteChn16Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,100,135,65,10 CONTROL "Channel 17 (&H)",MuteChn17Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,30,65,10 CONTROL "Channel 18 (&I)",MuteChn18Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,45,65,10 CONTROL "Channel 19 (&J)",MuteChn19Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,60,65,10 CONTROL "Channel 20 (&K)",MuteChn20Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,75,65,10 CONTROL "Channel 21 (&L)",MuteChn21Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,90,65,10 CONTROL "Channel 22 (&M)",MuteChn22Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,105,65,10 CONTROL "Channel 23 (&N)",MuteChn23Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,120,65,10 CONTROL "Channel 24 (&O)",MuteChn24Check,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,135,65,10 CONTROL "&Reset on track change",ResetMuteCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,10,155,115,10 CONTROL "Disable Chip (&X)",MuteChipCheck,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,180,155,70,10 END DlgCfgOptPan DIALOG DISCARDABLE 0, 0, 270, 166 STYLE DS_SETFOREGROUND | DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE FONT 8, "MS Shell Dlg" BEGIN LTEXT "&Select Chip:",IDC_STATIC,10,12,50,10 COMBOBOX EmuOptChipList,60,10,60,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX EmuOptChipNumList,130,10,60,80,CBS_DROPDOWNLIST | WS_TABSTOP GROUPBOX "E&mulation Core",IDC_STATIC,10,30,245,30 CONTROL "Default Core",EmuCoreRadio1,"Button",BS_AUTORADIOBUTTON, 20,43,105,10 CONTROL "Alternative Core",EmuCoreRadio2,"Button", BS_AUTORADIOBUTTON,135,43,110,10 GROUPBOX "Panning",IDC_STATIC,10,65,245,95 LTEXT "&1",PanChn1Label,20,80,10,8 CONTROL "Slider1",PanChn1Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,30,80,60,10 LTEXT "&2",PanChn2Label,20,95,10,8 CONTROL "Slider1",PanChn2Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,30,95,60,10 LTEXT "&3",PanChn3Label,20,110,10,8 CONTROL "Slider1",PanChn3Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,30,110,60,10 LTEXT "&4",PanChn4Label,20,125,10,8 CONTROL "Slider1",PanChn4Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,30,125,60,10 LTEXT "&5",PanChn5Label,20,140,10,8 CONTROL "Slider1",PanChn5Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,30,140,60,10 LTEXT "&6",PanChn6Label,95,80,10,8 CONTROL "Slider1",PanChn6Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,105,80,60,10 LTEXT "&7",PanChn7Label,95,95,10,8 CONTROL "Slider1",PanChn7Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,105,95,60,10 LTEXT "&8",PanChn8Label,95,110,10,8 CONTROL "Slider1",PanChn8Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,105,110,60,10 LTEXT "&9",PanChn9Label,95,125,10,8 CONTROL "Slider1",PanChn9Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,105,125,60,10 LTEXT "&A",PanChn10Label,95,140,10,8 CONTROL "Slider1",PanChn10Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,105,140,60,10 LTEXT "&B",PanChn11Label,175,80,10,8 CONTROL "Slider1",PanChn11Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,185,80,60,10 LTEXT "&C",PanChn12Label,175,95,10,8 CONTROL "Slider1",PanChn12Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,185,95,60,10 LTEXT "&D",PanChn13Label,175,110,10,8 CONTROL "Slider1",PanChn13Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,185,110,60,10 LTEXT "&E",PanChn14Label,175,125,10,8 CONTROL "Slider1",PanChn14Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,185,125,60,10 LTEXT "&F",PanChn15Label,175,140,10,8 CONTROL "Slider1",PanChn15Slider,"msctls_trackbar32", TBS_AUTOTICKS | WS_TABSTOP,185,140,60,10 END #endif // Englisch (USA) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: in_vgm/ini_func.c ================================================ #include #include #include "stdbool.h" #include "chips/mamedef.h" // for (U)INTxx types extern char IniFilePath[MAX_PATH]; void ReadIni_Integer(const char* Section, const char* Key, UINT32* Value) { *Value = (INT32)GetPrivateProfileInt(Section, Key, *Value, IniFilePath); return; } void ReadIni_SIntSht(const char* Section, const char* Key, INT16* Value) { *Value = (INT16)GetPrivateProfileInt(Section, Key, *Value, IniFilePath); return; } void ReadIni_IntByte(const char* Section, const char* Key, UINT8* Value) { *Value = (UINT8)GetPrivateProfileInt(Section, Key, *Value, IniFilePath); return; } void ReadIni_Boolean(const char* Section, const char* Key, bool* Value) { char TempStr[0x10]; GetPrivateProfileString(Section, Key, "", TempStr, 0x10, IniFilePath); if (! strcmp(TempStr, "")) return; if (! stricmp(TempStr, "True")) *Value = true; else if (! stricmp(TempStr, "False")) *Value = false; else *Value = strtol(TempStr, NULL, 0) ? true : false; return; } void ReadIni_String(const char* Section, const char* Key, char* String, UINT32 StrSize) { GetPrivateProfileString(Section, Key, String, String, StrSize, IniFilePath); return; } void ReadIni_Float(const char* Section, const char* Key, float* Value) { char TempStr[0x10]; GetPrivateProfileString(Section, Key, "", TempStr, 0x10, IniFilePath); if (! strcmp(TempStr, "")) return; *Value = (float)strtod(TempStr, NULL); return; } void WriteIni_Integer(const char* Section, const char* Key, UINT32 Value) { char TempStr[0x10]; sprintf(TempStr, "%u", Value); WritePrivateProfileString(Section, Key, TempStr, IniFilePath); return; } void WriteIni_SInteger(const char* Section, const char* Key, INT32 Value) { char TempStr[0x10]; sprintf(TempStr, "%d", Value); WritePrivateProfileString(Section, Key, TempStr, IniFilePath); return; } void WriteIni_XInteger(const char* Section, const char* Key, UINT32 Value) { char TempStr[0x10]; sprintf(TempStr, "0x%02X", Value); WritePrivateProfileString(Section, Key, TempStr, IniFilePath); return; } void WriteIni_Boolean(const char* Section, const char* Key, bool Value) { char TempStr[0x10]; if (Value) strcpy(TempStr, "True"); else strcpy(TempStr, "False"); WritePrivateProfileString(Section, Key, TempStr, IniFilePath); return; } void WriteIni_String(const char* Section, const char* Key, char* String) { WritePrivateProfileString(Section, Key, String, IniFilePath); return; } void WriteIni_Float(const char* Section, const char* Key, float Value) { char TempStr[0x10]; sprintf(TempStr, "%f", Value); WritePrivateProfileString(Section, Key, TempStr, IniFilePath); return; } ================================================ FILE: in_vgm/ini_func.h ================================================ void ReadIni_Integer(const char* Section, const char* Key, UINT32* Value); void ReadIni_SIntSht(const char* Section, const char* Key, INT16* Value); void ReadIni_IntByte(const char* Section, const char* Key, UINT8* Value); void ReadIni_Boolean(const char* Section, const char* Key, bool* Value); void ReadIni_String(const char* Section, const char* Key, char* String, UINT32 StrSize); void ReadIni_Float(const char* Section, const char* Key, float* Value); void WriteIni_Integer(const char* Section, const char* Key, UINT32 Value); void WriteIni_SInteger(const char* Section, const char* Key, INT32 Value); void WriteIni_XInteger(const char* Section, const char* Key, UINT32 Value); void WriteIni_Boolean(const char* Section, const char* Key, bool Value); void WriteIni_String(const char* Section, const char* Key, char* String); void WriteIni_Float(const char* Section, const char* Key, float Value); ================================================ FILE: in_vgm/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by in_vgm.rc // #define MainIcon 101 #define TabIcons 102 #define LogoBitmap 103 #define DlgConfigMain 201 #define DlgFileInfo 202 #define DlgCfgPlayback 203 #define DlgCfgTags 204 #define DlgCfgVgm7z 205 #define DlgCfgMuting 206 #define DlgCfgOptPan 207 #define TabCollection 1001 #define ImmediateUpdCheck 1002 #define HelpButton 1003 #define LoopText 1101 #define FadeText 1102 #define PauseNlText 1103 #define PauseLpText 1104 #define RateRecRadio 1105 #define Rate60HzRadio 1106 #define Rate50HzRadio 1107 #define RateOtherRadio 1108 #define RateText 1109 #define RateLabel 1110 #define VolumeSlider 1111 #define VolumeText 1112 #define SmplPbRateText 1113 #define ResmpModeList 1114 #define SmplChipRateText 1115 #define ChipSmpModeList 1116 #define SurroundCheck 1117 #define SmplChipRateLabel 1118 #define SmplChipRateHzLabel 1119 #define LoopTimesLabel 1120 #define TitleFormatText 1201 #define PreferJapCheck 1202 #define FM2413Check 1203 #define TrimWhitespcCheck 1204 #define StdSeparatorsCheck 1205 #define MLTypeText 1206 #define TagFallbackCheck 1207 #define NoInfoCacheCheck 1208 #define MutingChipList 1301 #define MutingChipNumList 1302 #define MuteChn1Check 1303 #define MuteChn2Check 1304 #define MuteChn3Check 1305 #define MuteChn4Check 1306 #define MuteChn5Check 1307 #define MuteChn6Check 1308 #define MuteChn7Check 1309 #define MuteChn8Check 1310 #define MuteChn9Check 1311 #define MuteChn10Check 1312 #define MuteChn11Check 1313 #define MuteChn12Check 1314 #define MuteChn13Check 1315 #define MuteChn14Check 1316 #define MuteChn15Check 1317 #define MuteChn16Check 1318 #define MuteChn17Check 1319 #define MuteChn18Check 1320 #define MuteChn19Check 1321 #define MuteChn20Check 1322 #define MuteChn21Check 1323 #define MuteChn22Check 1324 #define MuteChn23Check 1325 #define MuteChn24Check 1326 #define ResetMuteCheck 1327 #define MuteChipCheck 1328 #define TrkTitleText 2001 #define TrkAuthorText 2002 #define GameNameText 2003 #define GameSysText 2004 #define GameDateText 2005 #define VGMFileText 2006 #define VGMCrtText 2007 #define VGMVerText 2008 #define VGMGainText 2009 #define VGMSizeText 2010 #define VGMLenText 2011 #define VGMNotesText 2012 #define ChipUseText 2013 #define LangEngCheck 2014 #define LangJapCheck 2015 #define ConfigPluginButton 2016 #define BrwsrInfoButton 2017 #define EmuOptChipList 3001 #define EmuOptChipNumList 3002 #define EmuCoreRadio1 3003 #define EmuCoreRadio2 3004 #define PanChn1Label 3005 #define PanChn2Label 3006 #define PanChn3Label 3007 #define PanChn4Label 3008 #define PanChn5Label 3009 #define PanChn6Label 3010 #define PanChn7Label 3011 #define PanChn8Label 3012 #define PanChn9Label 3013 #define PanChn10Label 3014 #define PanChn11Label 3015 #define PanChn12Label 3016 #define PanChn13Label 3017 #define PanChn14Label 3018 #define PanChn15Label 3019 #define PanChn1Slider 3020 #define PanChn2Slider 3021 #define PanChn3Slider 3022 #define PanChn4Slider 3023 #define PanChn5Slider 3024 #define PanChn6Slider 3025 #define PanChn7Slider 3026 #define PanChn8Slider 3027 #define PanChn9Slider 3028 #define PanChn10Slider 3029 #define PanChn11Slider 3030 #define PanChn12Slider 3031 #define PanChn13Slider 3032 #define PanChn14Slider 3033 #define PanChn15Slider 3034 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 212 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 3038 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif