diff --git a/CMakeLists.txt b/CMakeLists.txt index ba2d6f9ba..1de208854 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,14 +198,14 @@ endmacro() include( CheckFunctionExists ) -macro( require_stricmp ) +macro( gz_require_stricmp ) CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS ) if( NOT STRICMP_EXISTS ) add_definitions( -Dstricmp=strcasecmp ) endif() endmacro() -macro( require_strnicmp ) +macro( gz_require_strnicmp ) CHECK_FUNCTION_EXISTS( strnicmp STRNICMP_EXISTS ) if( NOT STRNICMP_EXISTS ) add_definitions( -Dstrnicmp=strncasecmp ) @@ -219,6 +219,7 @@ find_package( VPX ) if (NOT FORCE_INTERNAL_CPPDAP) find_package( cppdap CONFIG ) endif() +find_package( ZMusic ) include( TargetArch ) @@ -402,6 +403,18 @@ else() set( CPPDAP_FOUND TRUE ) endif() +# ZMUSIC +if( ZMUSIC_FOUND AND NOT FORCE_INTERNAL_ZMUSIC ) + message( STATUS "Using system zmusic library, includes found at ${ZMUSIC_INCLUDE_DIR}" ) +else() + message( STATUS "Using internal zmusic library" ) + add_subdirectory( libraries/zmusic ) + set( ZMUSIC_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zmusic" ) + set( ZMUSIC_INCLUDE_DIR ${ZMUSIC_ROOT_PATH}/include ) + set( ZMUSIC_LIBRARIES zmusic ) + set( ZMUSIC_FOUND TRUE ) +endif() + set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/lzma/C" ) if( NOT CMAKE_CROSSCOMPILING ) @@ -423,7 +436,7 @@ install(DIRECTORY docs/ option( DYN_OPENAL "Dynamically load OpenAL" ON ) add_subdirectory( libraries/lzma ) -add_subdirectory( libraries/miniz ) +#add_subdirectory( libraries/miniz ) #ZMusic already includes this add_subdirectory( tools ) add_subdirectory( wadsrc ) add_subdirectory( wadsrc_bm ) diff --git a/libraries/ZMusic/thirdparty/adlmidi/chips/opal/LICENSE.txt b/libraries/ZMusic/thirdparty/adlmidi/chips/opal/LICENSE.txt index 326b10498..2ee2f0385 100644 --- a/libraries/ZMusic/thirdparty/adlmidi/chips/opal/LICENSE.txt +++ b/libraries/ZMusic/thirdparty/adlmidi/chips/opal/LICENSE.txt @@ -1,19 +1,19 @@ -A quote from a letter sent by Willy Reeve to Vitaly Novichkov -as a reply from a letter sent by Reality's (https://www.3eality.com/) -contact form ----------------------------------------------------------------- -All source code supplied with RAD is in the public domain. ----------------------------------------------------------------- - -================================================================ -The complete letter: -================================================================ -Hi, - -All source code supplied with RAD is in the public domain. -We'll clarify that in the documentation for the next release. - -Later... -SHAYDE -================================================================ - +A quote from a letter sent by Willy Reeve to Vitaly Novichkov +as a reply from a letter sent by Reality's (https://www.3eality.com/) +contact form +---------------------------------------------------------------- +All source code supplied with RAD is in the public domain. +---------------------------------------------------------------- + +================================================================ +The complete letter: +================================================================ +Hi, + +All source code supplied with RAD is in the public domain. +We'll clarify that in the documentation for the next release. + +Later... +SHAYDE +================================================================ + diff --git a/libraries/ZMusic/thirdparty/adlmidi/chips/opal/old/LICENSE.txt b/libraries/ZMusic/thirdparty/adlmidi/chips/opal/old/LICENSE.txt index 326b10498..2ee2f0385 100644 --- a/libraries/ZMusic/thirdparty/adlmidi/chips/opal/old/LICENSE.txt +++ b/libraries/ZMusic/thirdparty/adlmidi/chips/opal/old/LICENSE.txt @@ -1,19 +1,19 @@ -A quote from a letter sent by Willy Reeve to Vitaly Novichkov -as a reply from a letter sent by Reality's (https://www.3eality.com/) -contact form ----------------------------------------------------------------- -All source code supplied with RAD is in the public domain. ----------------------------------------------------------------- - -================================================================ -The complete letter: -================================================================ -Hi, - -All source code supplied with RAD is in the public domain. -We'll clarify that in the documentation for the next release. - -Later... -SHAYDE -================================================================ - +A quote from a letter sent by Willy Reeve to Vitaly Novichkov +as a reply from a letter sent by Reality's (https://www.3eality.com/) +contact form +---------------------------------------------------------------- +All source code supplied with RAD is in the public domain. +---------------------------------------------------------------- + +================================================================ +The complete letter: +================================================================ +Hi, + +All source code supplied with RAD is in the public domain. +We'll clarify that in the documentation for the next release. + +Later... +SHAYDE +================================================================ + diff --git a/libraries/ZMusic/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h b/libraries/ZMusic/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h index dec55c84b..4f11de25f 100644 --- a/libraries/ZMusic/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h +++ b/libraries/ZMusic/thirdparty/adlmidi/chips/ymf262_lle/nuked_fmopl3.h @@ -1,347 +1,347 @@ -/* - * Copyright (C) 2023 nukeykt - * - * This file is part of YMF262-LLE. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * YMF262 emulator - * Thanks: - * John McMaster (siliconpr0n.org): - * YMF262 decap and die shot - * - */ - -#pragma once - -#ifndef NUKED_FMOPL3_H -#define NUKED_FMOPL3_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct -{ - int mclk; - int address; - int data_i; - int ic; - int cs; - int rd; - int wr; -} fmopl3_input_t; - -typedef struct -{ - fmopl3_input_t input; - - int mclk1; - int mclk2; - int aclk1; - int aclk2; - int clk1; - int clk2; - int rclk1; - int rclk2; - - int o_clk1; - int o_clk2; - int o_rclk1; - int o_rclk2; - int o_wrcheck; - int o_data_latch; - int o_bank_latch; - int o_reset0; - int o_ra_w1_l1; - - int prescaler1_reset[2]; - int prescaler1_cnt[2]; - - int prescaler2_reset_l[2]; - int prescaler2_cnt[2]; - int prescaler2_reset; - int prescaler2_l1[2]; - int prescaler2_l2; - int prescaler2_l3[2]; - int prescaler2_l4; - int prescaler2_l5[2]; - int prescaler2_l6[2]; - int prescaler2_l7; - - int fsm_cnt1[2]; - int fsm_cnt2[2]; - int fsm_cnt3[2]; - int fsm_cnt; - - int fsm_reset_l[2]; - int fsm_out[17]; - int fsm_l1[2]; - int fsm_l2[2]; - int fsm_l3[2]; - int fsm_l4[2]; - int fsm_l5[2]; - int fsm_l6[2]; - int fsm_l7[2]; - int fsm_l8[2]; - int fsm_l9[2]; - int fsm_l10[2]; - - int ic_latch[2]; - - int io_rd; - int io_wr; - int io_cs; - int io_a0; - int io_a1; - - int io_read; - int io_write; - int io_write0; - int io_write1; - int io_bank; - - int data_latch; - int bank_latch; - int bank_masked; - - int reg_sel1; - int reg_sel2; - int reg_sel3; - int reg_sel4; - int reg_sel5; - int reg_sel8; - int reg_selbd; - - int reg_test0; - int reg_timer1; - int reg_timer2; - int reg_notesel; - int rhythm; - int reg_rh_kon; - int reg_da; - int reg_dv; - - int reg_test1; - int reg_new; - int reg_4op; - - int reg_t1_mask; - int reg_t2_mask; - int reg_t1_start; - int reg_t2_start; - - int lfo_cnt[2]; - int vib_cnt[2]; - int t1_step; - int t2_step; - int am_step; - int vib_step; - - int rh_sel0; - int rh_sel[2]; - - int keyon_comb; - - int ra_address_latch; - int ra_address_good; - int ra_data_latch; - int ra_cnt1[2]; - int ra_cnt2[2]; - int ra_cnt3[2]; - int ra_cnt4[2]; - int ra_cnt; - int ra_rst_l[2]; - int ra_w1_l1; - int ra_w1_l2; - int ra_write; - int ra_write_a; - - int ra_multi[36]; - int ra_ksr[36]; - int ra_egt[36]; - int ra_am[36]; - int ra_vib[36]; - int ra_tl[36]; - int ra_ksl[36]; - int ra_ar[36]; - int ra_dr[36]; - int ra_sl[36]; - int ra_rr[36]; - int ra_wf[36]; - int ra_fnum[18]; - int ra_block[18]; - int ra_keyon[18]; - int ra_connect[18]; - int ra_fb[18]; - int ra_pan[18]; - int ra_connect_pair[18]; - int multi[2]; - int ksr[2]; - int egt[2]; - int am[2]; - int vib[2]; - int tl[2]; - int ksl[2]; - int ar[2]; - int dr[2]; - int sl[2]; - int rr[2]; - int wf[2]; - int fnum[2]; - int block[2]; - int keyon[2]; - int connect[2]; - int fb[2]; - int pan[2]; - int connect_pair[2]; - - int64_t ra_dbg1[2]; - int ra_dbg2[2]; - int ra_dbg_load[2]; - - int fb_l[2][2]; - int pan_l[2][2]; - - int write0_sr; - int write0_l[4]; - int write0; - - int write1_sr; - int write1_l[4]; - int write1; - - int connect_l[2]; - int connect_pair_l[2]; - - int t1_cnt[2]; - int t2_cnt[2]; - int t1_of[2]; - int t2_of[2]; - int t1_status; - int t2_status; - int timer_st_load_l; - int timer_st_load; - int t1_start; - int t2_start; - int t1_start_l[2]; - int t2_start_l[2]; - - int reset0; - int reset1; - - int pg_phase_o[4]; - int pg_dbg[2]; - int pg_dbg_load_l[2]; - int noise_lfsr[2]; - int pg_index[2]; - int pg_cells[36]; - int pg_out_rhy; - - int trem_load_l; - int trem_load; - int trem_st_load_l; - int trem_st_load; - int trem_carry[2]; - int trem_value[2]; - int trem_dir[2]; - int trem_step; - int trem_out; - int trem_of[2]; - - int eg_load_l1[2]; - int eg_load_l; - int eg_load; - - int64_t eg_timer_masked[2]; - int eg_carry[2]; - int eg_mask[2]; - int eg_subcnt[2]; - int eg_subcnt_l[2]; - int eg_sync_l[2]; - int eg_timer_low; - int eg_shift; - int eg_timer_dbg[2]; - - int eg_timer_i; - int eg_timer_o[4]; - int eg_state_o[4]; - int eg_level_o[4]; - int eg_index[2]; - int eg_cells[36]; - - int eg_out[2]; - int eg_dbg[2]; - int eg_dbg_load_l[2]; - - int hh_load; - int tc_load; - int hh_bit2; - int hh_bit3; - int hh_bit7; - int hh_bit8; - int tc_bit3; - int tc_bit5; - - int op_logsin[2]; - int op_saw[2]; - int op_saw_phase[2]; - int op_shift[2]; - int op_pow[2]; - int op_mute[2]; - int op_sign[2]; - int op_fb[4][13][2]; - int op_mod[2]; - - int op_value; - - int accm_a[2]; - int accm_b[2]; - int accm_c[2]; - int accm_d[2]; - int accm_shift_a[2]; - int accm_shift_b[2]; - int accm_shift_c[2]; - int accm_shift_d[2]; - int accm_load_ac_l; - int accm_load_ac; - int accm_load_bd_l; - int accm_load_bd; - int accm_a_of; - int accm_a_sign; - int accm_b_of; - int accm_b_sign; - int accm_c_of; - int accm_c_sign; - int accm_d_of; - int accm_d_sign; - - int o_doab; - int o_docd; - int o_sy; - int o_smpac; - int o_smpbd; - int o_irq_pull; - int o_test; - - int data_o; - int data_z; -} fmopl3_t; - -extern void FMOPL3_Clock(fmopl3_t *chip); - -#ifdef __cplusplus -} -#endif - -#endif /* NUKED_FMOPL3_H */ +/* + * Copyright (C) 2023 nukeykt + * + * This file is part of YMF262-LLE. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * YMF262 emulator + * Thanks: + * John McMaster (siliconpr0n.org): + * YMF262 decap and die shot + * + */ + +#pragma once + +#ifndef NUKED_FMOPL3_H +#define NUKED_FMOPL3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct +{ + int mclk; + int address; + int data_i; + int ic; + int cs; + int rd; + int wr; +} fmopl3_input_t; + +typedef struct +{ + fmopl3_input_t input; + + int mclk1; + int mclk2; + int aclk1; + int aclk2; + int clk1; + int clk2; + int rclk1; + int rclk2; + + int o_clk1; + int o_clk2; + int o_rclk1; + int o_rclk2; + int o_wrcheck; + int o_data_latch; + int o_bank_latch; + int o_reset0; + int o_ra_w1_l1; + + int prescaler1_reset[2]; + int prescaler1_cnt[2]; + + int prescaler2_reset_l[2]; + int prescaler2_cnt[2]; + int prescaler2_reset; + int prescaler2_l1[2]; + int prescaler2_l2; + int prescaler2_l3[2]; + int prescaler2_l4; + int prescaler2_l5[2]; + int prescaler2_l6[2]; + int prescaler2_l7; + + int fsm_cnt1[2]; + int fsm_cnt2[2]; + int fsm_cnt3[2]; + int fsm_cnt; + + int fsm_reset_l[2]; + int fsm_out[17]; + int fsm_l1[2]; + int fsm_l2[2]; + int fsm_l3[2]; + int fsm_l4[2]; + int fsm_l5[2]; + int fsm_l6[2]; + int fsm_l7[2]; + int fsm_l8[2]; + int fsm_l9[2]; + int fsm_l10[2]; + + int ic_latch[2]; + + int io_rd; + int io_wr; + int io_cs; + int io_a0; + int io_a1; + + int io_read; + int io_write; + int io_write0; + int io_write1; + int io_bank; + + int data_latch; + int bank_latch; + int bank_masked; + + int reg_sel1; + int reg_sel2; + int reg_sel3; + int reg_sel4; + int reg_sel5; + int reg_sel8; + int reg_selbd; + + int reg_test0; + int reg_timer1; + int reg_timer2; + int reg_notesel; + int rhythm; + int reg_rh_kon; + int reg_da; + int reg_dv; + + int reg_test1; + int reg_new; + int reg_4op; + + int reg_t1_mask; + int reg_t2_mask; + int reg_t1_start; + int reg_t2_start; + + int lfo_cnt[2]; + int vib_cnt[2]; + int t1_step; + int t2_step; + int am_step; + int vib_step; + + int rh_sel0; + int rh_sel[2]; + + int keyon_comb; + + int ra_address_latch; + int ra_address_good; + int ra_data_latch; + int ra_cnt1[2]; + int ra_cnt2[2]; + int ra_cnt3[2]; + int ra_cnt4[2]; + int ra_cnt; + int ra_rst_l[2]; + int ra_w1_l1; + int ra_w1_l2; + int ra_write; + int ra_write_a; + + int ra_multi[36]; + int ra_ksr[36]; + int ra_egt[36]; + int ra_am[36]; + int ra_vib[36]; + int ra_tl[36]; + int ra_ksl[36]; + int ra_ar[36]; + int ra_dr[36]; + int ra_sl[36]; + int ra_rr[36]; + int ra_wf[36]; + int ra_fnum[18]; + int ra_block[18]; + int ra_keyon[18]; + int ra_connect[18]; + int ra_fb[18]; + int ra_pan[18]; + int ra_connect_pair[18]; + int multi[2]; + int ksr[2]; + int egt[2]; + int am[2]; + int vib[2]; + int tl[2]; + int ksl[2]; + int ar[2]; + int dr[2]; + int sl[2]; + int rr[2]; + int wf[2]; + int fnum[2]; + int block[2]; + int keyon[2]; + int connect[2]; + int fb[2]; + int pan[2]; + int connect_pair[2]; + + int64_t ra_dbg1[2]; + int ra_dbg2[2]; + int ra_dbg_load[2]; + + int fb_l[2][2]; + int pan_l[2][2]; + + int write0_sr; + int write0_l[4]; + int write0; + + int write1_sr; + int write1_l[4]; + int write1; + + int connect_l[2]; + int connect_pair_l[2]; + + int t1_cnt[2]; + int t2_cnt[2]; + int t1_of[2]; + int t2_of[2]; + int t1_status; + int t2_status; + int timer_st_load_l; + int timer_st_load; + int t1_start; + int t2_start; + int t1_start_l[2]; + int t2_start_l[2]; + + int reset0; + int reset1; + + int pg_phase_o[4]; + int pg_dbg[2]; + int pg_dbg_load_l[2]; + int noise_lfsr[2]; + int pg_index[2]; + int pg_cells[36]; + int pg_out_rhy; + + int trem_load_l; + int trem_load; + int trem_st_load_l; + int trem_st_load; + int trem_carry[2]; + int trem_value[2]; + int trem_dir[2]; + int trem_step; + int trem_out; + int trem_of[2]; + + int eg_load_l1[2]; + int eg_load_l; + int eg_load; + + int64_t eg_timer_masked[2]; + int eg_carry[2]; + int eg_mask[2]; + int eg_subcnt[2]; + int eg_subcnt_l[2]; + int eg_sync_l[2]; + int eg_timer_low; + int eg_shift; + int eg_timer_dbg[2]; + + int eg_timer_i; + int eg_timer_o[4]; + int eg_state_o[4]; + int eg_level_o[4]; + int eg_index[2]; + int eg_cells[36]; + + int eg_out[2]; + int eg_dbg[2]; + int eg_dbg_load_l[2]; + + int hh_load; + int tc_load; + int hh_bit2; + int hh_bit3; + int hh_bit7; + int hh_bit8; + int tc_bit3; + int tc_bit5; + + int op_logsin[2]; + int op_saw[2]; + int op_saw_phase[2]; + int op_shift[2]; + int op_pow[2]; + int op_mute[2]; + int op_sign[2]; + int op_fb[4][13][2]; + int op_mod[2]; + + int op_value; + + int accm_a[2]; + int accm_b[2]; + int accm_c[2]; + int accm_d[2]; + int accm_shift_a[2]; + int accm_shift_b[2]; + int accm_shift_c[2]; + int accm_shift_d[2]; + int accm_load_ac_l; + int accm_load_ac; + int accm_load_bd_l; + int accm_load_bd; + int accm_a_of; + int accm_a_sign; + int accm_b_of; + int accm_b_sign; + int accm_c_of; + int accm_c_sign; + int accm_d_of; + int accm_d_sign; + + int o_doab; + int o_docd; + int o_sy; + int o_smpac; + int o_smpbd; + int o_irq_pull; + int o_test; + + int data_o; + int data_z; +} fmopl3_t; + +extern void FMOPL3_Clock(fmopl3_t *chip); + +#ifdef __cplusplus +} +#endif + +#endif /* NUKED_FMOPL3_H */ diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.c b/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.c index e420a2703..246e2da0a 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.c +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.c @@ -1,2271 +1,2271 @@ -/* -** -** software implementation of Yamaha FM sound generator (YM2612/YM3438) -** -** Original code (MAME fm.c) -** -** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) -** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development -** -** Version 1.4 (final beta) -** -** Additional code & fixes by Eke-Eke for Genesis Plus GX -** -** Huge thanks to Nemesis, most of those fixes came from his tests on Sega Genesis hardware -** Additional info from YM2612 die shot analysis by Sauraen -** See http://gendev.spritesmind.net/forum/viewtopic.php?t=386 -** -*/ - -/* -** CHANGELOG: -** -** 08-06-2018 JPCima (OPNMIDI) -** - repaired the multichip support back -** -** 03-12-2017 Eke-Eke (Genesis Plus GX): -** - improved 9-bit DAC emulation accuracy -** - added discrete YM2612 DAC distortion emulation ("ladder effect") -** - replaced configurable DAC depth with configurable chip types (discrete, integrated or enhanced) -** -** 26-09-2017 Eke-Eke (Genesis Plus GX): -** - fixed EG counter loopback behavior (verified on YM3438 die) -** - reverted changes to EG rates 2-7 increment values -** -** 09-04-2017 Eke-Eke (Genesis Plus GX): -** - fixed LFO PM implementation: block & keyscale code should not be modified by LFO (verified on YM2612 die) -** - fixed Timer B overflow handling -** -** 12-03-2017 Eke-Eke (Genesis Plus GX): -** - fixed Op1 self-feedback regression introduced by previous modifications -** - removed one-sample extra delay on Op1 calculated output -** - refactored chan_calc() function -** -** 01-09-2012 Eke-Eke (Genesis Plus GX): -** - removed input clock / output samplerate frequency ratio, chip now always run at (original) internal sample frequency -** - removed now uneeded extra bits of precision -** -** 2006~2012 Eke-Eke (Genesis Plus GX): -** - removed unused multichip support -** - added YM2612 Context external access functions -** - fixed LFO implementation: -** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) -** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) -** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. -** - improved internal timers emulation -** - adjusted lowest EG rates increment values -** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) -** - fixed EG behavior when Attack Rate is maximal -** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation -** - implemented EG output immediate changes on register writes -** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B -** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) -** - implemented accurate CSM mode emulation -** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) -** - implemented accurate address/data ports behavior -** - added preliminar support for DAC precision -** -** 03-08-2003 Jarek Burczynski: -** - fixed YM2608 initial values (after the reset) -** - fixed flag and irqmask handling (YM2608) -** - fixed BUFRDY flag handling (YM2608) -** -** 14-06-2003 Jarek Burczynski: -** - implemented all of the YM2608 status register flags -** - implemented support for external memory read/write via YM2608 -** - implemented support for deltat memory limit register in YM2608 emulation -** -** 22-05-2003 Jarek Burczynski: -** - fixed LFO PM calculations (copy&paste bugfix) -** -** 08-05-2003 Jarek Burczynski: -** - fixed SSG support -** -** 22-04-2003 Jarek Burczynski: -** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) -** -** 15-04-2003 Jarek Burczynski: -** - added support for YM2608's register 0x110 - status mask -** -** 01-12-2002 Jarek Burczynski: -** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) -** The addressing patch used for early Neo-Geo games can be removed now. -** -** 26-11-2002 Jarek Burczynski, Nicola Salmoria: -** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: -** - added emulation of YM2608 drums. -** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) -** -** 16-08-2002 Jarek Burczynski: -** - binary exact Envelope Generator (verified on real YM2203); -** identical to YM2151 -** - corrected 'off by one' error in feedback calculations (when feedback is off) -** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) -** -** 18-12-2001 Jarek Burczynski: -** - added SSG-EG support (verified on real YM2203) -** -** 12-08-2001 Jarek Burczynski: -** - corrected sin_tab and tl_tab data (verified on real chip) -** - corrected feedback calculations (verified on real chip) -** - corrected phase generator calculations (verified on real chip) -** - corrected envelope generator calculations (verified on real chip) -** - corrected FM volume level (YM2610 and YM2610B). -** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : -** this was needed to calculate YM2610 FM channels output correctly. -** (Each FM channel is calculated as in other chips, but the output of the channel -** gets shifted right by one *before* sending to accumulator. That was impossible to do -** with previous implementation). -** -** 23-07-2001 Jarek Burczynski, Nicola Salmoria: -** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) -** -** 11-06-2001 Jarek Burczynski: -** - corrected end of sample bug in ADPCMA_calc_cha(). -** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). -** -** 08-12-98 hiro-shi: -** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA -** move ROM limit check.(CALC_CH? -> 2610Write1/2) -** test program (ADPCMB_TEST) -** move ADPCM A/B end check. -** ADPCMB repeat flag(no check) -** change ADPCM volume rate (8->16) (32->48). -** -** 09-12-98 hiro-shi: -** change ADPCM volume. (8->16, 48->64) -** replace ym2610 ch0/3 (YM-2610B) -** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. -** add ADPCM_SHIFT_MASK -** change ADPCMA_DECODE_MIN/MAX. -*/ - -/************************************************************************/ -/* comment of hiro-shi(Hiromitsu Shioya) */ -/* YM2610(B) = OPN-B */ -/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ -/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ -/************************************************************************/ - -#include "gx_ym2612.h" -#include "../mame/mamedef.h" -#include -#include -#include - -/* envelope generator */ -#define ENV_BITS 10 -#define ENV_LEN (1<>3) - -/* sin waveform table in 'decibel' scale */ -static unsigned int sin_tab[SIN_LEN]; - -/* sustain level table (3dB per step) */ -/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ -/* 1, 2, 4, 8, 16, 32, 64 (value)*/ -/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ - -/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -/* attenuation value (10 bits) = (SL << 2) << 3 */ -#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) -static const UINT32 sl_table[16]={ - SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), - SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) -}; -#undef SC - - -#define RATE_STEPS (8) -static const UINT8 eg_inc[19*RATE_STEPS]={ - -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ -/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ -/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ -/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ - -/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ -/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ -/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/*note that there is no O(17) in this table - it's directly in the code */ -static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ -/* 32 infinite time rates (same as Rate 0) */ -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), - -/* rates 00-11 */ -/* -O( 0),O( 1) -*/ -O(18),O(18), /* from Nemesis's tests on real YM2612 hardware */ - O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 12 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 13 */ -O( 8),O( 9),O(10),O(11), - -/* rate 14 */ -O(12),O(13),O(14),O(15), - -/* rate 15 */ -O(16),O(16),O(16),O(16), - -/* 32 dummy rates (same as 15 3) */ -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ -/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ -/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ - -#define O(a) (a*1) -static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ -/* 32 infinite time rates */ -/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ - -/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ -O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), -O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), -O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), -O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), - -/* rates 00-11 */ -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), -O( 0),O( 0),O( 0),O( 0), - -/* rate 12 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 32 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) - -}; -#undef O - -static const UINT8 dt_tab[4 * 32]={ -/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ -/* FD=0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/* FD=1 */ - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, -/* FD=2 */ - 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, - 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, -/* FD=3 */ - 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, - 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 -}; - - -/* OPN key frequency number -> key code follow table */ -/* fnum higher 4bit -> keycode lower 2bit */ -static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; - - -/* 8 LFO speed parameters */ -/* each value represents number of samples that one LFO level will last for */ -static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; - - -/*There are 4 different LFO AM depths available, they are: - 0 dB, 1.4 dB, 5.9 dB, 11.8 dB - Here is how it is generated (in EG steps): - - 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 - 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 - 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 - - (1.4 dB is loosing precision as you can see) - - It's implemented as generator from 0..126 with step 2 then a shift - right N times, where N is: - 8 for 0 dB - 3 for 1.4 dB - 1 for 5.9 dB - 0 for 11.8 dB -*/ -static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; - - - -/*There are 8 different LFO PM depths available, they are: - 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) - - Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 - (bits 8,9,10 = FNUM MSB from OCT/FNUM register) - - Here we store only first quarter (positive one) of full waveform. - Full table (lfo_pm_table) containing all 128 waveforms is build - at run (init) time. - - One value in table below represents 4 (four) basic LFO steps - (1 PM step = 4 AM steps). - - For example: - at LFO SPEED=0 (which is 108 samples per basic LFO step) - one value from "lfo_pm_output" table lasts for 432 consecutive - samples (4*108=432) and one full LFO waveform cycle lasts for 13824 - samples (32*432=13824; 32 because we store only a quarter of whole - waveform in the table below) -*/ -static const UINT8 lfo_pm_output[7*8][8]={ -/* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ -/* FNUM BIT 4: 000 0001xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, - -/* FNUM BIT 5: 000 0010xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, -/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, - -/* FNUM BIT 6: 000 0100xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, -/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, -/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, -/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, - -/* FNUM BIT 7: 000 1000xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, -/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, -/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, -/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, -/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, -/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, - -/* FNUM BIT 8: 001 0000xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, -/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, -/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, -/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, -/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, -/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, -/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, - -/* FNUM BIT 9: 010 0000xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, -/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, -/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, -/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, -/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, -/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, -/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, - -/* FNUM BIT10: 100 0000xxxx */ -/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, -/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, -/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, -/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, -/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, -/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, -/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, -/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, - -}; - -/* all 128 LFO PM waveforms */ -static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ - -/* register number to channel number , slot offset */ -#define OPN_CHAN(N) (N&3) -#define OPN_SLOT(N) ((N>>2)&3) - -/* slot number */ -#define SLOT1 0 -#define SLOT2 2 -#define SLOT3 1 -#define SLOT4 3 - -/* - * Pan law table - */ - -static const UINT16 panlawtable[] = -{ - 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, - 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, - 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, - 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, - 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, - 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, - 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, - 50433, 49912, 49383, 48846, 48302, 47750, 47191, - 46340, /* Center left */ - 46340, /* Center right */ - 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, - 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, - 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, - 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, - 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, - 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, - 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, - 4858, 4050, 3240, 2431, 1620, 810, 0 -}; - -/* struct describing a single operator (SLOT) */ -typedef struct -{ - INT32 *DT; /* detune :dt_tab[DT] */ - UINT8 KSR; /* key scale rate :3-KSR */ - UINT32 ar; /* attack rate */ - UINT32 d1r; /* decay rate */ - UINT32 d2r; /* sustain rate */ - UINT32 rr; /* release rate */ - UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ - UINT32 mul; /* multiple :ML_TABLE[ML] */ - - /* Phase Generator */ - UINT32 phase; /* phase counter */ - INT32 Incr; /* phase step */ - - /* Envelope Generator */ - UINT8 state; /* phase type */ - UINT32 tl; /* total level: TL << 3 */ - INT32 volume; /* envelope counter */ - UINT32 sl; /* sustain level:sl_table[SL] */ - UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ - - UINT8 eg_sh_ar; /* (attack state) */ - UINT8 eg_sel_ar; /* (attack state) */ - UINT8 eg_sh_d1r; /* (decay state) */ - UINT8 eg_sel_d1r; /* (decay state) */ - UINT8 eg_sh_d2r; /* (sustain state) */ - UINT8 eg_sel_d2r; /* (sustain state) */ - UINT8 eg_sh_rr; /* (release state) */ - UINT8 eg_sel_rr; /* (release state) */ - - UINT8 ssg; /* SSG-EG waveform */ - UINT8 ssgn; /* SSG-EG negated output */ - - UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ - - /* LFO */ - UINT32 AMmask; /* AM enable flag */ - -} FM_SLOT; - -typedef struct -{ - FM_SLOT SLOT[4]; /* four SLOTs (operators) */ - - UINT8 ALGO; /* algorithm */ - UINT8 FB; /* feedback shift */ - INT32 op1_out[2]; /* op1 output for feedback */ - - INT32 *connect1; /* SLOT1 output pointer */ - INT32 *connect3; /* SLOT3 output pointer */ - INT32 *connect2; /* SLOT2 output pointer */ - INT32 *connect4; /* SLOT4 output pointer */ - - INT32 *mem_connect; /* where to put the delayed sample (MEM) */ - INT32 mem_value; /* delayed sample (MEM) value */ - - INT32 pms; /* channel PMS */ - UINT8 ams; /* channel AMS */ - - UINT32 fc; /* fnum,blk */ - UINT8 kcode; /* key code */ - UINT32 block_fnum; /* blk/fnum value (for LFO PM calculations) */ - - INT32 pan_volume_l; - INT32 pan_volume_r; -} FM_CH; - - -typedef struct -{ - UINT16 address; /* address register */ - UINT8 status; /* status flag */ - UINT32 mode; /* mode CSM / 3SLOT */ - UINT8 fn_h; /* freq latch */ - INT32 TA; /* timer a value */ - INT32 TAL; /* timer a base */ - INT32 TAC; /* timer a counter */ - INT32 TB; /* timer b value */ - INT32 TBL; /* timer b base */ - INT32 TBC; /* timer b counter */ - INT32 dt_tab[8][32]; /* DeTune table */ - -} FM_ST; - - -/***********************************************************/ -/* OPN unit */ -/***********************************************************/ - -/* OPN 3slot struct */ -typedef struct -{ - UINT32 fc[3]; /* fnum3,blk3: calculated */ - UINT8 fn_h; /* freq3 latch */ - UINT8 kcode[3]; /* key code */ - UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ - UINT8 key_csm; /* CSM mode Key-ON flag */ - -} FM_3SLOT; - -/* OPN/A/B common state */ -typedef struct -{ - FM_ST ST; /* general state */ - FM_3SLOT SL3; /* 3 slot mode state */ - unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ - - /* EG */ - UINT32 eg_cnt; /* global envelope generator counter */ - UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ - - /* LFO */ - UINT8 lfo_cnt; /* current LFO phase (out of 128) */ - UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ - UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ - UINT32 LFO_AM; /* current LFO AM step */ - UINT32 LFO_PM; /* current LFO PM step */ - -} FM_OPN; - -/***********************************************************/ -/* YM2612 chip */ -/***********************************************************/ -typedef struct YM2612GX -{ - FM_CH CH[6]; /* channel state */ - UINT8 dacen; /* DAC mode */ - INT32 dacout; /* DAC output */ - FM_OPN OPN; /* OPN state */ - - /* current chip state */ - INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ - INT32 mem; /* one sample delay memory */ - INT32 out_fm[6]; /* outputs of working channels */ - - /* chip type */ - UINT32 op_mask[8][4]; /* operator output bitmasking (DAC quantization) */ - int chip_type; -} YM2612; - - -INLINE void FM_KEYON(YM2612 *ym2612, FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - - if (!SLOT->key && !ym2612->OPN.SL3.key_csm) - { - /* restart Phase Generator */ - SLOT->phase = 0; - - /* reset SSG-EG inversion flag */ - SLOT->ssgn = 0; - - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; - } - else - { - /* force attenuation level to 0 */ - SLOT->volume = MIN_ATT_INDEX; - - /* directly switch to Decay (or Sustain) */ - SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; - } - - /* recalculate EG output */ - if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - - SLOT->key = 1; -} - -INLINE void FM_KEYOFF(YM2612 *ym2612, FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - - if (SLOT->key && !ym2612->OPN.SL3.key_csm) - { - if (SLOT->state>EG_REL) - { - SLOT->state = EG_REL; /* phase -> Release */ - - /* SSG-EG specific update */ - if (SLOT->ssg&0x08) - { - /* convert EG attenuation level */ - if (SLOT->ssgn ^ (SLOT->ssg&0x04)) - SLOT->volume = (0x200 - SLOT->volume); - - /* force EG attenuation level */ - if (SLOT->volume >= 0x200) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; - } - - /* recalculate EG output */ - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - } - } - - SLOT->key = 0; -} - -INLINE void FM_KEYON_CSM(YM2612 *ym2612, FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - - if (!SLOT->key && !ym2612->OPN.SL3.key_csm) - { - /* restart Phase Generator */ - SLOT->phase = 0; - - /* reset SSG-EG inversion flag */ - SLOT->ssgn = 0; - - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; - } - else - { - /* force attenuation level to 0 */ - SLOT->volume = MIN_ATT_INDEX; - - /* directly switch to Decay (or Sustain) */ - SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; - } - - /* recalculate EG output */ - if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } -} - -INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - if (!SLOT->key) - { - if (SLOT->state>EG_REL) - { - SLOT->state = EG_REL; /* phase -> Release */ - - /* SSG-EG specific update */ - if (SLOT->ssg&0x08) - { - /* convert EG attenuation level */ - if (SLOT->ssgn ^ (SLOT->ssg&0x04)) - SLOT->volume = (0x200 - SLOT->volume); - - /* force EG attenuation level */ - if (SLOT->volume >= 0x200) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; - } - - /* recalculate EG output */ - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - } - } -} - -/* CSM Key Controll */ -INLINE void CSMKeyControll(YM2612 *ym2612, FM_CH *CH) -{ - /* all key ON (verified by Nemesis on real hardware) */ - FM_KEYON_CSM(ym2612, CH,SLOT1); - FM_KEYON_CSM(ym2612, CH,SLOT2); - FM_KEYON_CSM(ym2612, CH,SLOT3); - FM_KEYON_CSM(ym2612, CH,SLOT4); - ym2612->OPN.SL3.key_csm = 1; -} - -INLINE void INTERNAL_TIMER_A(YM2612 *ym2612) -{ - if (ym2612->OPN.ST.mode & 0x01) - { - ym2612->OPN.ST.TAC--; - if (ym2612->OPN.ST.TAC <= 0) - { - /* set status (if enabled) */ - if (ym2612->OPN.ST.mode & 0x04) - ym2612->OPN.ST.status |= 0x01; - - /* reload the counter */ - ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; - - /* CSM mode auto key on */ - if ((ym2612->OPN.ST.mode & 0xC0) == 0x80) - CSMKeyControll(ym2612, &ym2612->CH[2]); - } - } -} - -INLINE void INTERNAL_TIMER_B(YM2612 *ym2612, int step) -{ - if (ym2612->OPN.ST.mode & 0x02) - { - ym2612->OPN.ST.TBC-=step; - if (ym2612->OPN.ST.TBC <= 0) - { - /* set status (if enabled) */ - if (ym2612->OPN.ST.mode & 0x08) - ym2612->OPN.ST.status |= 0x02; - - /* reload the counter */ - do - { - ym2612->OPN.ST.TBC += ym2612->OPN.ST.TBL; - } - while (ym2612->OPN.ST.TBC <= 0); - } - } -} - -/* OPN Mode Register Write */ -INLINE void set_timers(YM2612 *ym2612, int v ) -{ - /* b7 = CSM MODE */ - /* b6 = 3 slot mode */ - /* b5 = reset b */ - /* b4 = reset a */ - /* b3 = timer enable b */ - /* b2 = timer enable a */ - /* b1 = load b */ - /* b0 = load a */ - - if ((ym2612->OPN.ST.mode ^ v) & 0xC0) - { - /* phase increment need to be recalculated */ - ym2612->CH[2].SLOT[SLOT1].Incr=-1; - - /* CSM mode disabled and CSM key ON active*/ - if (((v & 0xC0) != 0x80) && ym2612->OPN.SL3.key_csm) - { - /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); - ym2612->OPN.SL3.key_csm = 0; - } - } - - /* reload Timers */ - if ((v&1) && !(ym2612->OPN.ST.mode&1)) - ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; - if ((v&2) && !(ym2612->OPN.ST.mode&2)) - ym2612->OPN.ST.TBC = ym2612->OPN.ST.TBL; - - /* reset Timers flags */ - ym2612->OPN.ST.status &= (~v >> 4); - - ym2612->OPN.ST.mode = v; -} - -/* set algorithm connection */ -INLINE void setup_connection(YM2612 *ym2612, FM_CH *CH, int ch ) -{ - INT32 *carrier = &ym2612->out_fm[ch]; - - INT32 **om1 = &CH->connect1; - INT32 **om2 = &CH->connect3; - INT32 **oc1 = &CH->connect2; - - INT32 **memc = &CH->mem_connect; - - switch( CH->ALGO ){ - case 0: - /* M1---C1---MEM---M2---C2---OUT */ - *om1 = &ym2612->c1; - *oc1 = &ym2612->mem; - *om2 = &ym2612->c2; - *memc= &ym2612->m2; - break; - case 1: - /* M1------+-MEM---M2---C2---OUT */ - /* C1-+ */ - *om1 = &ym2612->mem; - *oc1 = &ym2612->mem; - *om2 = &ym2612->c2; - *memc= &ym2612->m2; - break; - case 2: - /* M1-----------------+-C2---OUT */ - /* C1---MEM---M2-+ */ - *om1 = &ym2612->c2; - *oc1 = &ym2612->mem; - *om2 = &ym2612->c2; - *memc= &ym2612->m2; - break; - case 3: - /* M1---C1---MEM------+-C2---OUT */ - /* M2-+ */ - *om1 = &ym2612->c1; - *oc1 = &ym2612->mem; - *om2 = &ym2612->c2; - *memc= &ym2612->c2; - break; - case 4: - /* M1---C1-+-OUT */ - /* M2---C2-+ */ - /* MEM: not used */ - *om1 = &ym2612->c1; - *oc1 = carrier; - *om2 = &ym2612->c2; - *memc= &ym2612->mem; /* store it anywhere where it will not be used */ - break; - case 5: - /* +----C1----+ */ - /* M1-+-MEM---M2-+-OUT */ - /* +----C2----+ */ - *om1 = 0; /* special mark */ - *oc1 = carrier; - *om2 = carrier; - *memc= &ym2612->m2; - break; - case 6: - /* M1---C1-+ */ - /* M2-+-OUT */ - /* C2-+ */ - /* MEM: not used */ - *om1 = &ym2612->c1; - *oc1 = carrier; - *om2 = carrier; - *memc= &ym2612->mem; /* store it anywhere where it will not be used */ - break; - case 7: - /* M1-+ */ - /* C1-+-OUT */ - /* M2-+ */ - /* C2-+ */ - /* MEM: not used*/ - *om1 = carrier; - *oc1 = carrier; - *om2 = carrier; - *memc= &ym2612->mem; /* store it anywhere where it will not be used */ - break; - } - - CH->connect4 = carrier; -} - -/* set detune & multiple */ -INLINE void set_det_mul(YM2612 *ym2612, FM_CH *CH,FM_SLOT *SLOT,int v) -{ - SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; - SLOT->DT = ym2612->OPN.ST.dt_tab[(v>>4)&7]; - CH->SLOT[SLOT1].Incr=-1; -} - -/* set total level */ -INLINE void set_tl(FM_SLOT *SLOT , int v) -{ - SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ - - /* recalculate EG output */ - if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; -} - -/* set attack rate & key scale */ -INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v) -{ - UINT8 old_KSR = SLOT->KSR; - - SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - - SLOT->KSR = 3-(v>>6); - if (SLOT->KSR != old_KSR) - { - CH->SLOT[SLOT1].Incr=-1; - } - - /* Even if it seems unnecessary to do it here, it could happen that KSR and KC */ - /* are modified but the resulted SLOT->ksr value (kc >> SLOT->KSR) remains unchanged. */ - /* In such case, Attack Rate would not be recalculated by "refresh_fc_eg_slot". */ - /* This actually fixes the intro of "The Adventures of Batman & Robin" (Eke-Eke) */ - if ((SLOT->ar + SLOT->ksr) < (32+62)) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - /* verified by Nemesis on real hardware (Attack phase is blocked) */ - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 18*RATE_STEPS; - } - } - -/* set decay rate */ -INLINE void set_dr(FM_SLOT *SLOT,int v) -{ - SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - - SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; - SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; - -} - -/* set sustain rate */ -INLINE void set_sr(FM_SLOT *SLOT,int v) -{ - SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - - SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; - SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; -} - -/* set release rate */ -INLINE void set_sl_rr(FM_SLOT *SLOT,int v) -{ - SLOT->sl = sl_table[ v>>4 ]; - - /* check EG state changes */ - if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) - SLOT->state = EG_SUS; - - SLOT->rr = 34 + ((v&0x0f)<<2); - - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; -} - -/* advance LFO to next sample */ -INLINE void advance_lfo(YM2612 *ym2612) -{ - if (ym2612->OPN.lfo_timer_overflow) /* LFO enabled ? */ - { - /* increment LFO timer (every samples) */ - ym2612->OPN.lfo_timer ++; - - /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ - if (ym2612->OPN.lfo_timer >= ym2612->OPN.lfo_timer_overflow) - { - ym2612->OPN.lfo_timer = 0; - - /* There are 128 LFO steps */ - ym2612->OPN.lfo_cnt = ( ym2612->OPN.lfo_cnt + 1 ) & 127; - - /* triangle (inverted) */ - /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ - if (ym2612->OPN.lfo_cnt<64) - ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt ^ 63) << 1; - else - ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt & 63) << 1; - - /* PM works with 4 times slower clock */ - ym2612->OPN.LFO_PM = ym2612->OPN.lfo_cnt >> 2; - } - } -} - - -INLINE void advance_eg_channels(FM_CH *CH, unsigned int eg_cnt) -{ - unsigned int i = 6; /* six channels */ - unsigned int j; - FM_SLOT *SLOT; - - do - { - SLOT = &CH->SLOT[SLOT1]; - j = 4; /* four operators per channel */ - do - { - switch(SLOT->state) - { - case EG_ATT: /* attack phase */ - { - if (!(eg_cnt & ((1<eg_sh_ar)-1))) - { - /* update attenuation level */ - SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; - - /* check phase transition*/ - if (SLOT->volume <= MIN_ATT_INDEX) - { - SLOT->volume = MIN_ATT_INDEX; - SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ - } - - /* recalculate EG output */ - if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - break; - } - - case EG_DEC: /* decay phase */ - { - if (!(eg_cnt & ((1<eg_sh_d1r)-1))) - { - /* SSG EG type */ - if (SLOT->ssg&0x08) - { - /* update attenuation level */ - if (SLOT->volume < 0x200) - { - SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; - - /* recalculate EG output */ - if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - } - else - { - /* update attenuation level */ - SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; - - /* recalculate EG output */ - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - - /* check phase transition*/ - if (SLOT->volume >= (INT32)(SLOT->sl)) - SLOT->state = EG_SUS; - } - break; - } - - case EG_SUS: /* sustain phase */ - { - if (!(eg_cnt & ((1<eg_sh_d2r)-1))) - { - /* SSG EG type */ - if (SLOT->ssg&0x08) - { - /* update attenuation level */ - if (SLOT->volume < 0x200) - { - SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; - - /* recalculate EG output */ - if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - } - else - { - /* update attenuation level */ - SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; - - /* check phase transition*/ - if ( SLOT->volume >= MAX_ATT_INDEX ) - SLOT->volume = MAX_ATT_INDEX; - /* do not change SLOT->state (verified on real chip) */ - - /* recalculate EG output */ - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - } - break; - } - - case EG_REL: /* release phase */ - { - if (!(eg_cnt & ((1<eg_sh_rr)-1))) - { - /* SSG EG type */ - if (SLOT->ssg&0x08) - { - /* update attenuation level */ - if (SLOT->volume < 0x200) - SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; - - /* check phase transition */ - if (SLOT->volume >= 0x200) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; - } - } - else - { - /* update attenuation level */ - SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; - - /* check phase transition*/ - if (SLOT->volume >= MAX_ATT_INDEX) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; - } - } - - /* recalculate EG output */ - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - - } - break; - } - } - - /* next slot */ - SLOT++; - } while (--j); - - /* next channel */ - CH++; - } while (--i); -} - -/* SSG-EG update process */ -/* The behavior is based upon Nemesis tests on real hardware */ -/* This is actually executed before each samples */ -INLINE void update_ssg_eg_channels(FM_CH *CH) -{ - unsigned int i = 6; /* six channels */ - unsigned int j; - FM_SLOT *SLOT; - - do - { - j = 4; /* four operators per channel */ - SLOT = &CH->SLOT[SLOT1]; - - do - { - /* detect SSG-EG transition */ - /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ - /* if an Attack Phase is programmed, inversion can occur on each sample */ - if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) - { - if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ - { - /* set inversion flag */ - if (SLOT->ssg & 0x02) - SLOT->ssgn = 4; - - /* force attenuation level during decay phases */ - if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) - SLOT->volume = MAX_ATT_INDEX; - } - else /* loop SSG-EG */ - { - /* toggle output inversion flag or reset Phase Generator */ - if (SLOT->ssg & 0x02) - SLOT->ssgn ^= 4; - else - SLOT->phase = 0; - - /* same as Key ON */ - if (SLOT->state != EG_ATT) - { - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; - } - else - { - /* Attack Rate is maximal: directly switch to Decay or Substain */ - SLOT->volume = MIN_ATT_INDEX; - SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; - } - } - } - - /* recalculate EG output */ - if (SLOT->ssgn ^ (SLOT->ssg&0x04)) - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - - /* next slot */ - SLOT++; - } while (--j); - - /* next channel */ - CH++; - } while (--i); -} - -INLINE void update_phase_lfo_slot(FM_SLOT *SLOT, UINT32 pm, UINT8 kc, UINT32 fc) -{ - INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + pm]; - - if (lfo_fn_offset) /* LFO phase modulation active */ - { - /* block is not modified by LFO PM */ - UINT8 blk = fc >> 11; - - /* LFO works with one more bit of a precision (12-bit) */ - fc = ((fc << 1) + lfo_fn_offset) & 0xfff; - - /* (frequency) phase increment counter (17-bit) */ - fc = (((fc << blk) >> 2) + SLOT->DT[kc]) & DT_MASK; - - /* update phase */ - SLOT->phase += ((fc * SLOT->mul) >> 1); - } - else /* LFO phase modulation = zero */ - { - SLOT->phase += SLOT->Incr; - } -} - -INLINE void update_phase_lfo_channel(YM2612 *ym2612, FM_CH *CH) -{ - UINT32 fc = CH->block_fnum; - - INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + CH->pms + ym2612->OPN.LFO_PM]; - - if (lfo_fn_offset) /* LFO phase modulation active */ - { - UINT32 finc; - - /* block & keyscale code are not modified by LFO PM */ - UINT8 blk = fc >> 11; - UINT8 kc = CH->kcode; - - /* LFO works with one more bit of a precision (12-bit) */ - fc = ((fc << 1) + lfo_fn_offset) & 0xfff; - - /* (frequency) phase increment counter (17-bit) */ - fc = (fc << blk) >> 2; - - /* apply DETUNE & MUL operator specific values */ - finc = (fc + CH->SLOT[SLOT1].DT[kc]) & DT_MASK; - CH->SLOT[SLOT1].phase += ((finc * CH->SLOT[SLOT1].mul) >> 1); - - finc = (fc + CH->SLOT[SLOT2].DT[kc]) & DT_MASK; - CH->SLOT[SLOT2].phase += ((finc * CH->SLOT[SLOT2].mul) >> 1); - - finc = (fc + CH->SLOT[SLOT3].DT[kc]) & DT_MASK; - CH->SLOT[SLOT3].phase += ((finc * CH->SLOT[SLOT3].mul) >> 1); - - finc = (fc + CH->SLOT[SLOT4].DT[kc]) & DT_MASK; - CH->SLOT[SLOT4].phase += ((finc * CH->SLOT[SLOT4].mul) >> 1); - } - else /* LFO phase modulation = zero */ - { - CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; - CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; - CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; - CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; - } -} - -/* update phase increment and envelope generator */ -INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , unsigned int fc , unsigned int kc ) -{ - /* add detune value */ - fc += SLOT->DT[kc]; - - /* (frequency) phase overflow (credits to Nemesis) */ - fc &= DT_MASK; - - /* (frequency) phase increment counter */ - SLOT->Incr = (fc * SLOT->mul) >> 1; - - /* ksr */ - kc = kc >> SLOT->KSR; - - if( SLOT->ksr != kc ) - { - SLOT->ksr = kc; - - /* recalculate envelope generator rates */ - if ((SLOT->ar + kc) < (32+62)) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + kc ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + kc ]; - } - else - { - /* verified by Nemesis on real hardware (Attack phase is blocked) */ - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 18*RATE_STEPS; - } - - SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + kc]; - SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + kc]; - - SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + kc]; - SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + kc]; - - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + kc]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + kc]; - } -} - -/* update phase increment counters */ -INLINE void refresh_fc_eg_chan(FM_CH *CH ) -{ - if( CH->SLOT[SLOT1].Incr==-1) - { - int fc = CH->fc; - int kc = CH->kcode; - refresh_fc_eg_slot(&CH->SLOT[SLOT1] , fc , kc ); - refresh_fc_eg_slot(&CH->SLOT[SLOT2] , fc , kc ); - refresh_fc_eg_slot(&CH->SLOT[SLOT3] , fc , kc ); - refresh_fc_eg_slot(&CH->SLOT[SLOT4] , fc , kc ); - } -} - -#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) - -INLINE signed int op_calc(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) -{ - UINT32 p = (env<<3) + sin_tab[ ( (phase >> SIN_BITS) + (pm >> 1) ) & SIN_MASK ]; - - if (p >= TL_TAB_LEN) - return 0; - return (tl_tab[p] & opmask); -} - -INLINE signed int op_calc1(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) -{ - UINT32 p = (env<<3) + sin_tab[ ( ( phase >> SIN_BITS ) + pm ) & SIN_MASK ]; - - if (p >= TL_TAB_LEN) - return 0; - return (tl_tab[p] & opmask); -} - -INLINE void chan_calc(YM2612 *ym2612, FM_CH *CH, int num) -{ - do - { - INT32 out = 0; - UINT32 AM = ym2612->OPN.LFO_AM >> CH->ams; - unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]); - UINT32 *mask = ym2612->op_mask[CH->ALGO]; - - ym2612->m2 = ym2612->c1 = ym2612->c2 = ym2612->mem = 0; - - *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ - - if( eg_out < ENV_QUIET ) /* SLOT 1 */ - { - if (CH->FB < SIN_BITS) - out = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB; - - out = op_calc1(CH->SLOT[SLOT1].phase, eg_out, out, mask[0]); - } - - CH->op1_out[0] = CH->op1_out[1]; - CH->op1_out[1] = out; - - if( !CH->connect1 ){ - /* algorithm 5 */ - ym2612->mem = ym2612->c1 = ym2612->c2 = out; - }else{ - /* other algorithms */ - *CH->connect1 = out; - } - - eg_out = volume_calc(&CH->SLOT[SLOT3]); - if( eg_out < ENV_QUIET ) /* SLOT 3 */ - *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, ym2612->m2, mask[2]); - - eg_out = volume_calc(&CH->SLOT[SLOT2]); - if( eg_out < ENV_QUIET ) /* SLOT 2 */ - *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, ym2612->c1, mask[1]); - - eg_out = volume_calc(&CH->SLOT[SLOT4]); - if( eg_out < ENV_QUIET ) /* SLOT 4 */ - *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, ym2612->c2, mask[3]); - - /* store current MEM */ - CH->mem_value = ym2612->mem; - - /* update phase counters AFTER output calculations */ - if (CH->pms) - { - /* 3-slot mode */ - if ((ym2612->OPN.ST.mode & 0xC0) && (CH == &ym2612->CH[2])) - { - /* keyscale code is not modifiedby LFO */ - UINT8 kc = ym2612->CH[2].kcode; - UINT32 pm = ym2612->CH[2].pms + ym2612->OPN.LFO_PM; - update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT1], pm, kc, ym2612->OPN.SL3.block_fnum[1]); - update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT2], pm, kc, ym2612->OPN.SL3.block_fnum[2]); - update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT3], pm, kc, ym2612->OPN.SL3.block_fnum[0]); - update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT4], pm, kc, ym2612->CH[2].block_fnum); - } - else - { - update_phase_lfo_channel(ym2612, CH); - } - } - else /* no LFO phase modulation */ - { - CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; - CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; - CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; - CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; - } - - /* next channel */ - CH++; - } while (--num); -} - -/* write a OPN mode register 0x20-0x2f */ -INLINE void OPNWriteMode(YM2612 *ym2612, int r, int v) -{ - UINT8 c; - FM_CH *CH; - - switch(r){ - case 0x21: /* Test */ - break; - - case 0x22: /* LFO FREQ */ - if (v&8) /* LFO enabled ? */ - { - ym2612->OPN.lfo_timer_overflow = lfo_samples_per_step[v&7]; - } - else - { - /* hold LFO waveform in reset state */ - ym2612->OPN.lfo_timer_overflow = 0; - ym2612->OPN.lfo_timer = 0; - ym2612->OPN.lfo_cnt = 0; - ym2612->OPN.LFO_PM = 0; - ym2612->OPN.LFO_AM = 126; - } - break; - case 0x24: /* timer A High */ - ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x03)|(((int)v)<<2); - ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; - break; - case 0x25: /* timer A Low */ - ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x3fc)|(v&3); - ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; - break; - case 0x26: /* timer B */ - ym2612->OPN.ST.TB = v; - ym2612->OPN.ST.TBL = (256 - v) << 4; - break; - case 0x27: /* mode, timer control */ - set_timers(ym2612, v); - break; - case 0x28: /* key on / off */ - c = v & 0x03; - if( c == 3 ) break; - if (v&0x04) c+=3; /* CH 4-6 */ - CH = &ym2612->CH[c]; - if (v&0x10) FM_KEYON(ym2612,CH,SLOT1); else FM_KEYOFF(ym2612,CH,SLOT1); - if (v&0x20) FM_KEYON(ym2612,CH,SLOT2); else FM_KEYOFF(ym2612,CH,SLOT2); - if (v&0x40) FM_KEYON(ym2612,CH,SLOT3); else FM_KEYOFF(ym2612,CH,SLOT3); - if (v&0x80) FM_KEYON(ym2612,CH,SLOT4); else FM_KEYOFF(ym2612,CH,SLOT4); - break; - } -} - -/* write a OPN register (0x30-0xff) */ -INLINE void OPNWriteReg(YM2612 *ym2612, int r, int v) -{ - FM_CH *CH; - FM_SLOT *SLOT; - - UINT8 c = OPN_CHAN(r); - - if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ - - if (r >= 0x100) c+=3; - - CH = &ym2612->CH[c]; - - SLOT = &(CH->SLOT[OPN_SLOT(r)]); - - switch( r & 0xf0 ) { - case 0x30: /* DET , MUL */ - set_det_mul(ym2612,CH,SLOT,v); - break; - - case 0x40: /* TL */ - set_tl(SLOT,v); - break; - - case 0x50: /* KS, AR */ - set_ar_ksr(CH,SLOT,v); - break; - - case 0x60: /* bit7 = AM ENABLE, DR */ - set_dr(SLOT,v); - SLOT->AMmask = (v&0x80) ? ~0 : 0; - break; - - case 0x70: /* SR */ - set_sr(SLOT,v); - break; - - case 0x80: /* SL, RR */ - set_sl_rr(SLOT,v); - break; - - case 0x90: /* SSG-EG */ - SLOT->ssg = v&0x0f; - - /* recalculate EG output */ - if (SLOT->state > EG_REL) - { - if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) - SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; - else - SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; - } - - /* SSG-EG envelope shapes : - - E AtAlH - 1 0 0 0 \\\\ - - 1 0 0 1 \___ - - 1 0 1 0 \/\/ - ___ - 1 0 1 1 \ - - 1 1 0 0 //// - ___ - 1 1 0 1 / - - 1 1 1 0 /\/\ - - 1 1 1 1 /___ - - - E = SSG-EG enable - - - The shapes are generated using Attack, Decay and Sustain phases. - - Each single character in the diagrams above represents this whole - sequence: - - - when KEY-ON = 1, normal Attack phase is generated (*without* any - difference when compared to normal mode), - - - later, when envelope level reaches minimum level (max volume), - the EG switches to Decay phase (which works with bigger steps - when compared to normal mode - see below), - - - later when envelope level passes the SL level, - the EG swithes to Sustain phase (which works with bigger steps - when compared to normal mode - see below), - - - finally when envelope level reaches maximum level (min volume), - the EG switches to Attack phase again (depends on actual waveform). - - Important is that when switch to Attack phase occurs, the phase counter - of that operator will be zeroed-out (as in normal KEY-ON) but not always. - (I havent found the rule for that - perhaps only when the output level is low) - - The difference (when compared to normal Envelope Generator mode) is - that the resolution in Decay and Sustain phases is 4 times lower; - this results in only 256 steps instead of normal 1024. - In other words: - when SSG-EG is disabled, the step inside of the EG is one, - when SSG-EG is enabled, the step is four (in Decay and Sustain phases). - - Times between the level changes are the same in both modes. - - - Important: - Decay 1 Level (so called SL) is compared to actual SSG-EG output, so - it is the same in both SSG and no-SSG modes, with this exception: - - when the SSG-EG is enabled and is generating raising levels - (when the EG output is inverted) the SL will be found at wrong level !!! - For example, when SL=02: - 0 -6 = -6dB in non-inverted EG output - 96-6 = -90dB in inverted EG output - Which means that EG compares its level to SL as usual, and that the - output is simply inverted afterall. - - - The Yamaha's manuals say that AR should be set to 0x1f (max speed). - That is not necessary, but then EG will be generating Attack phase. - - */ - break; - - case 0xa0: - switch( OPN_SLOT(r) ){ - case 0: /* 0xa0-0xa2 : FNUM1 */ - { - UINT32 fn = (((UINT32)((ym2612->OPN.ST.fn_h)&7))<<8) + v; - UINT8 blk = ym2612->OPN.ST.fn_h>>3; - /* keyscale code */ - CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; - /* phase increment counter */ - CH->fc = (fn<>1; - - /* store fnum in clear form for LFO PM calculations */ - CH->block_fnum = (blk<<11) | fn; - - CH->SLOT[SLOT1].Incr=-1; - break; - } - case 1: /* 0xa4-0xa6 : FNUM2,BLK */ - ym2612->OPN.ST.fn_h = v&0x3f; - break; - case 2: /* 0xa8-0xaa : 3CH FNUM1 */ - if(r < 0x100) - { - UINT32 fn = (((UINT32)(ym2612->OPN.SL3.fn_h&7))<<8) + v; - UINT8 blk = ym2612->OPN.SL3.fn_h>>3; - /* keyscale code */ - ym2612->OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; - /* phase increment counter */ - ym2612->OPN.SL3.fc[c] = (fn<>1; - ym2612->OPN.SL3.block_fnum[c] = (blk<<11) | fn; - ym2612->CH[2].SLOT[SLOT1].Incr=-1; - } - break; - case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ - if(r < 0x100) - ym2612->OPN.SL3.fn_h = v&0x3f; - break; - } - break; - - case 0xb0: - switch( OPN_SLOT(r) ){ - case 0: /* 0xb0-0xb2 : FB,ALGO */ - { - CH->ALGO = v&7; - CH->FB = SIN_BITS - ((v>>3)&7); - setup_connection(ym2612, CH, c ); - break; - } - case 1: /* 0xb4-0xb6 : L , R , AMS , PMS */ - /* b0-2 PMS */ - CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ - - /* b4-5 AMS */ - CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; - - /* PAN : b7 = L, b6 = R */ - ym2612->OPN.pan[ c*2 ] = (v & 0x80) ? 0xffffffff : 0; - ym2612->OPN.pan[ c*2+1 ] = (v & 0x40) ? 0xffffffff : 0; - break; - } - break; - } -} - -static void reset_channels(FM_CH *CH , int num ) -{ - int c,s; - - for( c = 0 ; c < num ; c++ ) - { - CH[c].mem_value = 0; - CH[c].op1_out[0] = 0; - CH[c].op1_out[1] = 0; - for(s = 0 ; s < 4 ; s++ ) - { - CH[c].SLOT[s].Incr = -1; - CH[c].SLOT[s].key = 0; - CH[c].SLOT[s].phase = 0; - CH[c].SLOT[s].ssgn = 0; - CH[c].SLOT[s].state = EG_OFF; - CH[c].SLOT[s].volume = MAX_ATT_INDEX; - CH[c].SLOT[s].vol_out = MAX_ATT_INDEX; - } - } -} - -/* initialize generic tables */ -static int tables_ready = 0; -static void init_tables() -{ - signed int i,x; - signed int n; - double o,m; - - if (tables_ready) - return; - - /* build Linear Power Table */ - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - n <<= 2; /* 13 bits here (as in real chip) */ - - /* 14 bits (with sign bit) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ - /* s = sign bit */ - /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ - /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ - /* any value above 13 (included) would be discarded. */ - for (i=1; i<13; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - } - - /* build Logarithmic Sinus table */ - for (i=0; i0.0) - o = 8*log(1.0/m)/log(2); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - - /* 13-bits (8.5) value is formatted for above 'Power' table */ - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - } - - /* build LFO PM modulation table */ - for(i = 0; i < 8; i++) /* 8 PM depths */ - { - UINT8 fnum; - for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ - { - UINT8 value; - UINT8 step; - UINT32 offset_depth = i; - UINT32 offset_fnum_bit; - UINT32 bit_tmp; - - for (step=0; step<8; step++) - { - value = 0; - for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ - { - if (fnum & (1<OPN.ST.dt_tab[d][i] = (INT32) dt_tab[d*32 + i]; - ym2612->OPN.ST.dt_tab[d+4][i] = -ym2612->OPN.ST.dt_tab[d][i]; - } - } - - /* build default OP mask table */ - for (i = 0;i < 8;i++) - { - for (d = 0;d < 4;d++) - { - ym2612->op_mask[i][d] = 0xffffffff; - } - } - - for (i = 0; i < 6; i++) - { - ym2612->CH[i].pan_volume_l = 46340; - ym2612->CH[i].pan_volume_r = 46340; - } - - init_tables(); -} - -/* reset OPN registers */ -void YM2612GXResetChip(YM2612 *ym2612) -{ - int i; - - ym2612->OPN.eg_timer = 0; - ym2612->OPN.eg_cnt = 0; - - ym2612->OPN.lfo_timer_overflow = 0; - ym2612->OPN.lfo_timer = 0; - ym2612->OPN.lfo_cnt = 0; - ym2612->OPN.LFO_AM = 126; - ym2612->OPN.LFO_PM = 0; - - ym2612->OPN.ST.TAC = 0; - ym2612->OPN.ST.TBC = 0; - - ym2612->OPN.SL3.key_csm = 0; - - ym2612->dacen = 0; - ym2612->dacout = 0; - - set_timers(ym2612, 0x30); - ym2612->OPN.ST.TB = 0; - ym2612->OPN.ST.TBL = 256 << 4; - ym2612->OPN.ST.TA = 0; - ym2612->OPN.ST.TAL = 1024; - - reset_channels(&ym2612->CH[0] , 6 ); - - for(i = 0xb6 ; i >= 0xb4 ; i-- ) - { - OPNWriteReg(ym2612, i ,0xc0); - OPNWriteReg(ym2612, i|0x100,0xc0); - } - for(i = 0xb2 ; i >= 0x30 ; i-- ) - { - OPNWriteReg(ym2612, i ,0); - OPNWriteReg(ym2612, i|0x100,0); - } -} - -/* ym2612 write */ -/* n = number */ -/* a = address */ -/* v = value */ -void YM2612GXWrite(YM2612 *ym2612, unsigned int a, unsigned int v) -{ - v &= 0xff; /* adjust to 8 bit bus */ - - switch( a ) - { - case 0: /* address port 0 */ - ym2612->OPN.ST.address = v; - break; - - case 2: /* address port 1 */ - ym2612->OPN.ST.address = v | 0x100; - break; - - default: /* data port */ - { - int addr = ym2612->OPN.ST.address; /* verified by Nemesis on real YM2612 */ - switch( addr & 0x1f0 ) - { - case 0x20: /* 0x20-0x2f Mode */ - switch( addr ) - { - case 0x2a: /* DAC data (ym2612) */ - ym2612->dacout = ((int)v - 0x80) << 6; /* convert to 14-bit output */ - break; - case 0x2b: /* DAC Sel (ym2612) */ - /* b7 = dac enable */ - ym2612->dacen = v & 0x80; - break; - default: /* OPN section */ - /* write register */ - OPNWriteMode(ym2612,addr,v); - } - break; - default: /* 0x30-0xff OPN section */ - /* write register */ - OPNWriteReg(ym2612,addr,v); - } - break; - } - } -} - -void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v) -{ - chip->CH[c].pan_volume_l = panlawtable[v & 0x7F]; - chip->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; -} - -unsigned int YM2612GXRead(YM2612 *ym2612) -{ - return ym2612->OPN.ST.status; -} - -void YM2612GXPreGenerate(YM2612GX *ym2612) -{ - /* refresh PG increments and EG rates if required */ - refresh_fc_eg_chan(&ym2612->CH[0]); - refresh_fc_eg_chan(&ym2612->CH[1]); - - if (!(ym2612->OPN.ST.mode & 0xC0)) - { - refresh_fc_eg_chan(&ym2612->CH[2]); - } - else - { - /* 3SLOT MODE (operator order is 0,1,3,2) */ - if(ym2612->CH[2].SLOT[SLOT1].Incr==-1) - { - refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT1] , ym2612->OPN.SL3.fc[1] , ym2612->OPN.SL3.kcode[1] ); - refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT2] , ym2612->OPN.SL3.fc[2] , ym2612->OPN.SL3.kcode[2] ); - refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT3] , ym2612->OPN.SL3.fc[0] , ym2612->OPN.SL3.kcode[0] ); - refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT4] , ym2612->CH[2].fc , ym2612->CH[2].kcode ); - } - } - - refresh_fc_eg_chan(&ym2612->CH[3]); - refresh_fc_eg_chan(&ym2612->CH[4]); - refresh_fc_eg_chan(&ym2612->CH[5]); -} - -void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count) -{ - /* timer B control */ - INTERNAL_TIMER_B(ym2612, count); -} - -void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame) -{ - int lt,rt; - INT32 *out_fm = ym2612->out_fm; - - /* clear outputs */ - out_fm[0] = 0; - out_fm[1] = 0; - out_fm[2] = 0; - out_fm[3] = 0; - out_fm[4] = 0; - out_fm[5] = 0; - - /* update SSG-EG output */ - update_ssg_eg_channels(&ym2612->CH[0]); - - /* calculate FM */ - if (!ym2612->dacen) - { - chan_calc(ym2612, &ym2612->CH[0],6); - } - else - { - /* DAC Mode */ - out_fm[5] = ym2612->dacout; - chan_calc(ym2612,&ym2612->CH[0],5); - } - - /* advance LFO */ - advance_lfo(ym2612); - - /* EG is updated every 3 samples */ - ym2612->OPN.eg_timer++; - if (ym2612->OPN.eg_timer >= 3) - { - /* reset EG timer */ - ym2612->OPN.eg_timer = 0; - - /* increment EG counter */ - ym2612->OPN.eg_cnt++; - - /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ - if (ym2612->OPN.eg_cnt == 4096) - ym2612->OPN.eg_cnt = 1; - - /* advance envelope generator */ - advance_eg_channels(&ym2612->CH[0], ym2612->OPN.eg_cnt); - } - - /* channels accumulator output clipping (14-bit max) */ - if (out_fm[0] > 8191) out_fm[0] = 8191; - else if (out_fm[0] < -8192) out_fm[0] = -8192; - if (out_fm[1] > 8191) out_fm[1] = 8191; - else if (out_fm[1] < -8192) out_fm[1] = -8192; - if (out_fm[2] > 8191) out_fm[2] = 8191; - else if (out_fm[2] < -8192) out_fm[2] = -8192; - if (out_fm[3] > 8191) out_fm[3] = 8191; - else if (out_fm[3] < -8192) out_fm[3] = -8192; - if (out_fm[4] > 8191) out_fm[4] = 8191; - else if (out_fm[4] < -8192) out_fm[4] = -8192; - if (out_fm[5] > 8191) out_fm[5] = 8191; - else if (out_fm[5] < -8192) out_fm[5] = -8192; - -#define PANLAW_L(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_l / 65535) & ym2612->OPN.pan[chpan]); -#define PANLAW_R(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_r / 65535) & ym2612->OPN.pan[chpan]); - - /* stereo DAC output panning & mixing */ - lt = PANLAW_L(0, 0); - rt = PANLAW_R(0, 1); - lt += PANLAW_L(1, 2); - rt += PANLAW_R(1, 3); - lt += PANLAW_L(2, 4); - rt += PANLAW_R(2, 5); - lt += PANLAW_L(3, 6); - rt += PANLAW_R(3, 7); - lt += PANLAW_L(4, 8); - rt += PANLAW_R(4, 9); - lt += PANLAW_L(5, 10); - rt += PANLAW_R(5, 11); - -#undef PANLAW_L -#undef PANLAW_R - - /* discrete YM2612 DAC */ - if (ym2612->chip_type == YM2612_DISCRETE) - { - int i; - - /* DAC 'ladder effect' */ - for (i=0; i<6; i++) - { - if (out_fm[i] < 0) - { - /* -4 offset (-3 when not muted) on negative channel output (9-bit) */ - lt -= ((4 - (ym2612->OPN.pan[(2*i)+0] & 1)) << 5); - rt -= ((4 - (ym2612->OPN.pan[(2*i)+1] & 1)) << 5); - } - else - { - /* +4 offset (when muted or not) on positive channel output (9-bit) */ - lt += (4 << 5); - rt += (4 << 5); - } - } - } - - /* buffering */ - frame[0] = lt / 2; - frame[1] = rt / 2; - - /* CSM mode: if CSM Key ON has occurred, CSM Key OFF need to be sent */ - /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ - ym2612->OPN.SL3.key_csm <<= 1; - - /* timer A control */ - INTERNAL_TIMER_A(ym2612); - - /* CSM Mode Key ON still disabled */ - if (ym2612->OPN.SL3.key_csm & 2) - { - /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); - FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); - ym2612->OPN.SL3.key_csm = 0; - } -} - -void YM2612GXConfig(YM2612 *ym2612, int type) -{ - /* YM2612 chip type */ - ym2612->chip_type = type; - - /* carrier operator outputs bitmask */ - if (ym2612->chip_type < YM2612_ENHANCED) - { - /* 9-bit DAC */ - ym2612->op_mask[0][3] = 0xffffffe0; - ym2612->op_mask[1][3] = 0xffffffe0; - ym2612->op_mask[2][3] = 0xffffffe0; - ym2612->op_mask[3][3] = 0xffffffe0; - ym2612->op_mask[4][1] = 0xffffffe0; - ym2612->op_mask[4][3] = 0xffffffe0; - ym2612->op_mask[5][1] = 0xffffffe0; - ym2612->op_mask[5][2] = 0xffffffe0; - ym2612->op_mask[5][3] = 0xffffffe0; - ym2612->op_mask[6][1] = 0xffffffe0; - ym2612->op_mask[6][2] = 0xffffffe0; - ym2612->op_mask[6][3] = 0xffffffe0; - ym2612->op_mask[7][0] = 0xffffffe0; - ym2612->op_mask[7][1] = 0xffffffe0; - ym2612->op_mask[7][2] = 0xffffffe0; - ym2612->op_mask[7][3] = 0xffffffe0; - } - else - { - /* 14-bit DAC */ - ym2612->op_mask[0][3] = 0xffffffff; - ym2612->op_mask[1][3] = 0xffffffff; - ym2612->op_mask[2][3] = 0xffffffff; - ym2612->op_mask[3][3] = 0xffffffff; - ym2612->op_mask[4][1] = 0xffffffff; - ym2612->op_mask[4][3] = 0xffffffff; - ym2612->op_mask[5][1] = 0xffffffff; - ym2612->op_mask[5][2] = 0xffffffff; - ym2612->op_mask[5][3] = 0xffffffff; - ym2612->op_mask[6][1] = 0xffffffff; - ym2612->op_mask[6][2] = 0xffffffff; - ym2612->op_mask[6][3] = 0xffffffff; - ym2612->op_mask[7][0] = 0xffffffff; - ym2612->op_mask[7][1] = 0xffffffff; - ym2612->op_mask[7][2] = 0xffffffff; - ym2612->op_mask[7][3] = 0xffffffff; - } -} +/* +** +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) +** +** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) +** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.4 (final beta) +** +** Additional code & fixes by Eke-Eke for Genesis Plus GX +** +** Huge thanks to Nemesis, most of those fixes came from his tests on Sega Genesis hardware +** Additional info from YM2612 die shot analysis by Sauraen +** See http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +*/ + +/* +** CHANGELOG: +** +** 08-06-2018 JPCima (OPNMIDI) +** - repaired the multichip support back +** +** 03-12-2017 Eke-Eke (Genesis Plus GX): +** - improved 9-bit DAC emulation accuracy +** - added discrete YM2612 DAC distortion emulation ("ladder effect") +** - replaced configurable DAC depth with configurable chip types (discrete, integrated or enhanced) +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** 09-04-2017 Eke-Eke (Genesis Plus GX): +** - fixed LFO PM implementation: block & keyscale code should not be modified by LFO (verified on YM2612 die) +** - fixed Timer B overflow handling +** +** 12-03-2017 Eke-Eke (Genesis Plus GX): +** - fixed Op1 self-feedback regression introduced by previous modifications +** - removed one-sample extra delay on Op1 calculated output +** - refactored chan_calc() function +** +** 01-09-2012 Eke-Eke (Genesis Plus GX): +** - removed input clock / output samplerate frequency ratio, chip now always run at (original) internal sample frequency +** - removed now uneeded extra bits of precision +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** - removed unused multichip support +** - added YM2612 Context external access functions +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** - added preliminar support for DAC precision +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include "gx_ym2612.h" +#include "../mame/mamedef.h" +#include +#include +#include + +/* envelope generator */ +#define ENV_BITS 10 +#define ENV_LEN (1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1) +*/ +O(18),O(18), /* from Nemesis's tests on real YM2612 hardware */ + O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is loosing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ +/* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* + * Pan law table + */ + +static const UINT16 panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect; /* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk */ + UINT8 kcode; /* key code */ + UINT32 block_fnum; /* blk/fnum value (for LFO PM calculations) */ + + INT32 pan_volume_l; + INT32 pan_volume_r; +} FM_CH; + + +typedef struct +{ + UINT16 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + INT32 TA; /* timer a value */ + INT32 TAL; /* timer a base */ + INT32 TAC; /* timer a counter */ + INT32 TB; /* timer b value */ + INT32 TBL; /* timer b base */ + INT32 TBC; /* timer b counter */ + INT32 dt_tab[8][32]; /* DeTune table */ + +} FM_ST; + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ + +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + /* EG */ + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + +} FM_OPN; + +/***********************************************************/ +/* YM2612 chip */ +/***********************************************************/ +typedef struct YM2612GX +{ + FM_CH CH[6]; /* channel state */ + UINT8 dacen; /* DAC mode */ + INT32 dacout; /* DAC output */ + FM_OPN OPN; /* OPN state */ + + /* current chip state */ + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + + /* chip type */ + UINT32 op_mask[8][4]; /* operator output bitmasking (DAC quantization) */ + int chip_type; +} YM2612; + + +INLINE void FM_KEYON(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (!SLOT->key && !ym2612->OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && !ym2612->OPN.SL3.key_csm) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(YM2612 *ym2612, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (!SLOT->key && !ym2612->OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(YM2612 *ym2612, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(ym2612, CH,SLOT1); + FM_KEYON_CSM(ym2612, CH,SLOT2); + FM_KEYON_CSM(ym2612, CH,SLOT3); + FM_KEYON_CSM(ym2612, CH,SLOT4); + ym2612->OPN.SL3.key_csm = 1; +} + +INLINE void INTERNAL_TIMER_A(YM2612 *ym2612) +{ + if (ym2612->OPN.ST.mode & 0x01) + { + ym2612->OPN.ST.TAC--; + if (ym2612->OPN.ST.TAC <= 0) + { + /* set status (if enabled) */ + if (ym2612->OPN.ST.mode & 0x04) + ym2612->OPN.ST.status |= 0x01; + + /* reload the counter */ + ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; + + /* CSM mode auto key on */ + if ((ym2612->OPN.ST.mode & 0xC0) == 0x80) + CSMKeyControll(ym2612, &ym2612->CH[2]); + } + } +} + +INLINE void INTERNAL_TIMER_B(YM2612 *ym2612, int step) +{ + if (ym2612->OPN.ST.mode & 0x02) + { + ym2612->OPN.ST.TBC-=step; + if (ym2612->OPN.ST.TBC <= 0) + { + /* set status (if enabled) */ + if (ym2612->OPN.ST.mode & 0x08) + ym2612->OPN.ST.status |= 0x02; + + /* reload the counter */ + do + { + ym2612->OPN.ST.TBC += ym2612->OPN.ST.TBL; + } + while (ym2612->OPN.ST.TBC <= 0); + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers(YM2612 *ym2612, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((ym2612->OPN.ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + ym2612->CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && ym2612->OPN.SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); + ym2612->OPN.SL3.key_csm = 0; + } + } + + /* reload Timers */ + if ((v&1) && !(ym2612->OPN.ST.mode&1)) + ym2612->OPN.ST.TAC = ym2612->OPN.ST.TAL; + if ((v&2) && !(ym2612->OPN.ST.mode&2)) + ym2612->OPN.ST.TBC = ym2612->OPN.ST.TBL; + + /* reset Timers flags */ + ym2612->OPN.ST.status &= (~v >> 4); + + ym2612->OPN.ST.mode = v; +} + +/* set algorithm connection */ +INLINE void setup_connection(YM2612 *ym2612, FM_CH *CH, int ch ) +{ + INT32 *carrier = &ym2612->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ){ + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &ym2612->c1; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &ym2612->mem; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &ym2612->c2; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &ym2612->c1; + *oc1 = &ym2612->mem; + *om2 = &ym2612->c2; + *memc= &ym2612->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &ym2612->c1; + *oc1 = carrier; + *om2 = &ym2612->c2; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &ym2612->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &ym2612->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(YM2612 *ym2612, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ym2612->OPN.ST.dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary to do it here, it could happen that KSR and KC */ + /* are modified but the resulted SLOT->ksr value (kc >> SLOT->KSR) remains unchanged. */ + /* In such case, Attack Rate would not be recalculated by "refresh_fc_eg_slot". */ + /* This actually fixes the intro of "The Adventures of Batman & Robin" (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < (32+62)) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + /* verified by Nemesis on real hardware (Attack phase is blocked) */ + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; + } + } + +/* set decay rate */ +INLINE void set_dr(FM_SLOT *SLOT,int v) +{ + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; + +} + +/* set sustain rate */ +INLINE void set_sr(FM_SLOT *SLOT,int v) +{ + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(FM_SLOT *SLOT,int v) +{ + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(YM2612 *ym2612) +{ + if (ym2612->OPN.lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer (every samples) */ + ym2612->OPN.lfo_timer ++; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + if (ym2612->OPN.lfo_timer >= ym2612->OPN.lfo_timer_overflow) + { + ym2612->OPN.lfo_timer = 0; + + /* There are 128 LFO steps */ + ym2612->OPN.lfo_cnt = ( ym2612->OPN.lfo_cnt + 1 ) & 127; + + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (ym2612->OPN.lfo_cnt<64) + ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt ^ 63) << 1; + else + ym2612->OPN.LFO_AM = (ym2612->OPN.lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + ym2612->OPN.LFO_PM = ym2612->OPN.lfo_cnt >> 2; + } + } +} + + +INLINE void advance_eg_channels(FM_CH *CH, unsigned int eg_cnt) +{ + unsigned int i = 6; /* six channels */ + unsigned int j; + FM_SLOT *SLOT; + + do + { + SLOT = &CH->SLOT[SLOT1]; + j = 4; /* four operators per channel */ + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + { + if (!(eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + } + + case EG_DEC: /* decay phase */ + { + if (!(eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + } + + case EG_SUS: /* sustain phase */ + { + if (!(eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + } + + case EG_REL: /* release phase */ + { + if (!(eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + } + + /* next slot */ + SLOT++; + } while (--j); + + /* next channel */ + CH++; + } while (--i); +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channels(FM_CH *CH) +{ + unsigned int i = 6; /* six channels */ + unsigned int j; + FM_SLOT *SLOT; + + do + { + j = 4; /* four operators per channel */ + SLOT = &CH->SLOT[SLOT1]; + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + } while (--j); + + /* next channel */ + CH++; + } while (--i); +} + +INLINE void update_phase_lfo_slot(FM_SLOT *SLOT, UINT32 pm, UINT8 kc, UINT32 fc) +{ + INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + pm]; + + if (lfo_fn_offset) /* LFO phase modulation active */ + { + /* block is not modified by LFO PM */ + UINT8 blk = fc >> 11; + + /* LFO works with one more bit of a precision (12-bit) */ + fc = ((fc << 1) + lfo_fn_offset) & 0xfff; + + /* (frequency) phase increment counter (17-bit) */ + fc = (((fc << blk) >> 2) + SLOT->DT[kc]) & DT_MASK; + + /* update phase */ + SLOT->phase += ((fc * SLOT->mul) >> 1); + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(YM2612 *ym2612, FM_CH *CH) +{ + UINT32 fc = CH->block_fnum; + + INT32 lfo_fn_offset = lfo_pm_table[((fc & 0x7f0) << 4) + CH->pms + ym2612->OPN.LFO_PM]; + + if (lfo_fn_offset) /* LFO phase modulation active */ + { + UINT32 finc; + + /* block & keyscale code are not modified by LFO PM */ + UINT8 blk = fc >> 11; + UINT8 kc = CH->kcode; + + /* LFO works with one more bit of a precision (12-bit) */ + fc = ((fc << 1) + lfo_fn_offset) & 0xfff; + + /* (frequency) phase increment counter (17-bit) */ + fc = (fc << blk) >> 2; + + /* apply DETUNE & MUL operator specific values */ + finc = (fc + CH->SLOT[SLOT1].DT[kc]) & DT_MASK; + CH->SLOT[SLOT1].phase += ((finc * CH->SLOT[SLOT1].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT2].DT[kc]) & DT_MASK; + CH->SLOT[SLOT2].phase += ((finc * CH->SLOT[SLOT2].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT3].DT[kc]) & DT_MASK; + CH->SLOT[SLOT3].phase += ((finc * CH->SLOT[SLOT3].mul) >> 1); + + finc = (fc + CH->SLOT[SLOT4].DT[kc]) & DT_MASK; + CH->SLOT[SLOT4].phase += ((finc * CH->SLOT[SLOT4].mul) >> 1); + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , unsigned int fc , unsigned int kc ) +{ + /* add detune value */ + fc += SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + fc &= DT_MASK; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + /* ksr */ + kc = kc >> SLOT->KSR; + + if( SLOT->ksr != kc ) + { + SLOT->ksr = kc; + + /* recalculate envelope generator rates */ + if ((SLOT->ar + kc) < (32+62)) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + kc ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + kc ]; + } + else + { + /* verified by Nemesis on real hardware (Attack phase is blocked) */ + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + kc]; + SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + kc]; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + kc]; + SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + kc]; + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + kc]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + kc]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(&CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(&CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) +{ + UINT32 p = (env<<3) + sin_tab[ ( (phase >> SIN_BITS) + (pm >> 1) ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return (tl_tab[p] & opmask); +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, unsigned int pm, unsigned int opmask) +{ + UINT32 p = (env<<3) + sin_tab[ ( ( phase >> SIN_BITS ) + pm ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return (tl_tab[p] & opmask); +} + +INLINE void chan_calc(YM2612 *ym2612, FM_CH *CH, int num) +{ + do + { + INT32 out = 0; + UINT32 AM = ym2612->OPN.LFO_AM >> CH->ams; + unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]); + UINT32 *mask = ym2612->op_mask[CH->ALGO]; + + ym2612->m2 = ym2612->c1 = ym2612->c2 = ym2612->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (CH->FB < SIN_BITS) + out = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB; + + out = op_calc1(CH->SLOT[SLOT1].phase, eg_out, out, mask[0]); + } + + CH->op1_out[0] = CH->op1_out[1]; + CH->op1_out[1] = out; + + if( !CH->connect1 ){ + /* algorithm 5 */ + ym2612->mem = ym2612->c1 = ym2612->c2 = out; + }else{ + /* other algorithms */ + *CH->connect1 = out; + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, ym2612->m2, mask[2]); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, ym2612->c1, mask[1]); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, ym2612->c2, mask[3]); + + /* store current MEM */ + CH->mem_value = ym2612->mem; + + /* update phase counters AFTER output calculations */ + if (CH->pms) + { + /* 3-slot mode */ + if ((ym2612->OPN.ST.mode & 0xC0) && (CH == &ym2612->CH[2])) + { + /* keyscale code is not modifiedby LFO */ + UINT8 kc = ym2612->CH[2].kcode; + UINT32 pm = ym2612->CH[2].pms + ym2612->OPN.LFO_PM; + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT1], pm, kc, ym2612->OPN.SL3.block_fnum[1]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT2], pm, kc, ym2612->OPN.SL3.block_fnum[2]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT3], pm, kc, ym2612->OPN.SL3.block_fnum[0]); + update_phase_lfo_slot(&ym2612->CH[2].SLOT[SLOT4], pm, kc, ym2612->CH[2].block_fnum); + } + else + { + update_phase_lfo_channel(ym2612, CH); + } + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } + + /* next channel */ + CH++; + } while (--num); +} + +/* write a OPN mode register 0x20-0x2f */ +INLINE void OPNWriteMode(YM2612 *ym2612, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r){ + case 0x21: /* Test */ + break; + + case 0x22: /* LFO FREQ */ + if (v&8) /* LFO enabled ? */ + { + ym2612->OPN.lfo_timer_overflow = lfo_samples_per_step[v&7]; + } + else + { + /* hold LFO waveform in reset state */ + ym2612->OPN.lfo_timer_overflow = 0; + ym2612->OPN.lfo_timer = 0; + ym2612->OPN.lfo_cnt = 0; + ym2612->OPN.LFO_PM = 0; + ym2612->OPN.LFO_AM = 126; + } + break; + case 0x24: /* timer A High */ + ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x03)|(((int)v)<<2); + ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; + break; + case 0x25: /* timer A Low */ + ym2612->OPN.ST.TA = (ym2612->OPN.ST.TA & 0x3fc)|(v&3); + ym2612->OPN.ST.TAL = 1024 - ym2612->OPN.ST.TA; + break; + case 0x26: /* timer B */ + ym2612->OPN.ST.TB = v; + ym2612->OPN.ST.TBL = (256 - v) << 4; + break; + case 0x27: /* mode, timer control */ + set_timers(ym2612, v); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if (v&0x04) c+=3; /* CH 4-6 */ + CH = &ym2612->CH[c]; + if (v&0x10) FM_KEYON(ym2612,CH,SLOT1); else FM_KEYOFF(ym2612,CH,SLOT1); + if (v&0x20) FM_KEYON(ym2612,CH,SLOT2); else FM_KEYOFF(ym2612,CH,SLOT2); + if (v&0x40) FM_KEYON(ym2612,CH,SLOT3); else FM_KEYOFF(ym2612,CH,SLOT3); + if (v&0x80) FM_KEYON(ym2612,CH,SLOT4); else FM_KEYOFF(ym2612,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +INLINE void OPNWriteReg(YM2612 *ym2612, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = &ym2612->CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(ym2612,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(SLOT,v); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + break; + + case 0x70: /* SR */ + set_sr(SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + break; + + case 0xa0: + switch( OPN_SLOT(r) ){ + case 0: /* 0xa0-0xa2 : FNUM1 */ + { + UINT32 fn = (((UINT32)((ym2612->OPN.ST.fn_h)&7))<<8) + v; + UINT8 blk = ym2612->OPN.ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = (fn<>1; + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + break; + } + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + ym2612->OPN.ST.fn_h = v&0x3f; + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ + if(r < 0x100) + { + UINT32 fn = (((UINT32)(ym2612->OPN.SL3.fn_h&7))<<8) + v; + UINT8 blk = ym2612->OPN.SL3.fn_h>>3; + /* keyscale code */ + ym2612->OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + ym2612->OPN.SL3.fc[c] = (fn<>1; + ym2612->OPN.SL3.block_fnum[c] = (blk<<11) | fn; + ym2612->CH[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + ym2612->OPN.SL3.fn_h = v&0x3f; + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ){ + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + CH->ALGO = v&7; + CH->FB = SIN_BITS - ((v>>3)&7); + setup_connection(ym2612, CH, c ); + break; + } + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS */ + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + ym2612->OPN.pan[ c*2 ] = (v & 0x80) ? 0xffffffff : 0; + ym2612->OPN.pan[ c*2+1 ] = (v & 0x40) ? 0xffffffff : 0; + break; + } + break; + } +} + +static void reset_channels(FM_CH *CH , int num ) +{ + int c,s; + + for( c = 0 ; c < num ; c++ ) + { + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + for(s = 0 ; s < 4 ; s++ ) + { + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state = EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out = MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static int tables_ready = 0; +static void init_tables() +{ + signed int i,x; + signed int n; + double o,m; + + if (tables_ready) + return; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<OPN.ST.dt_tab[d][i] = (INT32) dt_tab[d*32 + i]; + ym2612->OPN.ST.dt_tab[d+4][i] = -ym2612->OPN.ST.dt_tab[d][i]; + } + } + + /* build default OP mask table */ + for (i = 0;i < 8;i++) + { + for (d = 0;d < 4;d++) + { + ym2612->op_mask[i][d] = 0xffffffff; + } + } + + for (i = 0; i < 6; i++) + { + ym2612->CH[i].pan_volume_l = 46340; + ym2612->CH[i].pan_volume_r = 46340; + } + + init_tables(); +} + +/* reset OPN registers */ +void YM2612GXResetChip(YM2612 *ym2612) +{ + int i; + + ym2612->OPN.eg_timer = 0; + ym2612->OPN.eg_cnt = 0; + + ym2612->OPN.lfo_timer_overflow = 0; + ym2612->OPN.lfo_timer = 0; + ym2612->OPN.lfo_cnt = 0; + ym2612->OPN.LFO_AM = 126; + ym2612->OPN.LFO_PM = 0; + + ym2612->OPN.ST.TAC = 0; + ym2612->OPN.ST.TBC = 0; + + ym2612->OPN.SL3.key_csm = 0; + + ym2612->dacen = 0; + ym2612->dacout = 0; + + set_timers(ym2612, 0x30); + ym2612->OPN.ST.TB = 0; + ym2612->OPN.ST.TBL = 256 << 4; + ym2612->OPN.ST.TA = 0; + ym2612->OPN.ST.TAL = 1024; + + reset_channels(&ym2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(ym2612, i ,0xc0); + OPNWriteReg(ym2612, i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(ym2612, i ,0); + OPNWriteReg(ym2612, i|0x100,0); + } +} + +/* ym2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +void YM2612GXWrite(YM2612 *ym2612, unsigned int a, unsigned int v) +{ + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a ) + { + case 0: /* address port 0 */ + ym2612->OPN.ST.address = v; + break; + + case 2: /* address port 1 */ + ym2612->OPN.ST.address = v | 0x100; + break; + + default: /* data port */ + { + int addr = ym2612->OPN.ST.address; /* verified by Nemesis on real YM2612 */ + switch( addr & 0x1f0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (ym2612) */ + ym2612->dacout = ((int)v - 0x80) << 6; /* convert to 14-bit output */ + break; + case 0x2b: /* DAC Sel (ym2612) */ + /* b7 = dac enable */ + ym2612->dacen = v & 0x80; + break; + default: /* OPN section */ + /* write register */ + OPNWriteMode(ym2612,addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + /* write register */ + OPNWriteReg(ym2612,addr,v); + } + break; + } + } +} + +void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v) +{ + chip->CH[c].pan_volume_l = panlawtable[v & 0x7F]; + chip->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; +} + +unsigned int YM2612GXRead(YM2612 *ym2612) +{ + return ym2612->OPN.ST.status; +} + +void YM2612GXPreGenerate(YM2612GX *ym2612) +{ + /* refresh PG increments and EG rates if required */ + refresh_fc_eg_chan(&ym2612->CH[0]); + refresh_fc_eg_chan(&ym2612->CH[1]); + + if (!(ym2612->OPN.ST.mode & 0xC0)) + { + refresh_fc_eg_chan(&ym2612->CH[2]); + } + else + { + /* 3SLOT MODE (operator order is 0,1,3,2) */ + if(ym2612->CH[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT1] , ym2612->OPN.SL3.fc[1] , ym2612->OPN.SL3.kcode[1] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT2] , ym2612->OPN.SL3.fc[2] , ym2612->OPN.SL3.kcode[2] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT3] , ym2612->OPN.SL3.fc[0] , ym2612->OPN.SL3.kcode[0] ); + refresh_fc_eg_slot(&ym2612->CH[2].SLOT[SLOT4] , ym2612->CH[2].fc , ym2612->CH[2].kcode ); + } + } + + refresh_fc_eg_chan(&ym2612->CH[3]); + refresh_fc_eg_chan(&ym2612->CH[4]); + refresh_fc_eg_chan(&ym2612->CH[5]); +} + +void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count) +{ + /* timer B control */ + INTERNAL_TIMER_B(ym2612, count); +} + +void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame) +{ + int lt,rt; + INT32 *out_fm = ym2612->out_fm; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channels(&ym2612->CH[0]); + + /* calculate FM */ + if (!ym2612->dacen) + { + chan_calc(ym2612, &ym2612->CH[0],6); + } + else + { + /* DAC Mode */ + out_fm[5] = ym2612->dacout; + chan_calc(ym2612,&ym2612->CH[0],5); + } + + /* advance LFO */ + advance_lfo(ym2612); + + /* EG is updated every 3 samples */ + ym2612->OPN.eg_timer++; + if (ym2612->OPN.eg_timer >= 3) + { + /* reset EG timer */ + ym2612->OPN.eg_timer = 0; + + /* increment EG counter */ + ym2612->OPN.eg_cnt++; + + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (ym2612->OPN.eg_cnt == 4096) + ym2612->OPN.eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channels(&ym2612->CH[0], ym2612->OPN.eg_cnt); + } + + /* channels accumulator output clipping (14-bit max) */ + if (out_fm[0] > 8191) out_fm[0] = 8191; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8191) out_fm[1] = 8191; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8191) out_fm[2] = 8191; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8191) out_fm[3] = 8191; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8191) out_fm[4] = 8191; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8191) out_fm[5] = 8191; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + +#define PANLAW_L(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_l / 65535) & ym2612->OPN.pan[chpan]); +#define PANLAW_R(ch, chpan) ((out_fm[ch] * ym2612->CH[ch].pan_volume_r / 65535) & ym2612->OPN.pan[chpan]); + + /* stereo DAC output panning & mixing */ + lt = PANLAW_L(0, 0); + rt = PANLAW_R(0, 1); + lt += PANLAW_L(1, 2); + rt += PANLAW_R(1, 3); + lt += PANLAW_L(2, 4); + rt += PANLAW_R(2, 5); + lt += PANLAW_L(3, 6); + rt += PANLAW_R(3, 7); + lt += PANLAW_L(4, 8); + rt += PANLAW_R(4, 9); + lt += PANLAW_L(5, 10); + rt += PANLAW_R(5, 11); + +#undef PANLAW_L +#undef PANLAW_R + + /* discrete YM2612 DAC */ + if (ym2612->chip_type == YM2612_DISCRETE) + { + int i; + + /* DAC 'ladder effect' */ + for (i=0; i<6; i++) + { + if (out_fm[i] < 0) + { + /* -4 offset (-3 when not muted) on negative channel output (9-bit) */ + lt -= ((4 - (ym2612->OPN.pan[(2*i)+0] & 1)) << 5); + rt -= ((4 - (ym2612->OPN.pan[(2*i)+1] & 1)) << 5); + } + else + { + /* +4 offset (when muted or not) on positive channel output (9-bit) */ + lt += (4 << 5); + rt += (4 << 5); + } + } + } + + /* buffering */ + frame[0] = lt / 2; + frame[1] = rt / 2; + + /* CSM mode: if CSM Key ON has occurred, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + ym2612->OPN.SL3.key_csm <<= 1; + + /* timer A control */ + INTERNAL_TIMER_A(ym2612); + + /* CSM Mode Key ON still disabled */ + if (ym2612->OPN.SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612->CH[2],SLOT4); + ym2612->OPN.SL3.key_csm = 0; + } +} + +void YM2612GXConfig(YM2612 *ym2612, int type) +{ + /* YM2612 chip type */ + ym2612->chip_type = type; + + /* carrier operator outputs bitmask */ + if (ym2612->chip_type < YM2612_ENHANCED) + { + /* 9-bit DAC */ + ym2612->op_mask[0][3] = 0xffffffe0; + ym2612->op_mask[1][3] = 0xffffffe0; + ym2612->op_mask[2][3] = 0xffffffe0; + ym2612->op_mask[3][3] = 0xffffffe0; + ym2612->op_mask[4][1] = 0xffffffe0; + ym2612->op_mask[4][3] = 0xffffffe0; + ym2612->op_mask[5][1] = 0xffffffe0; + ym2612->op_mask[5][2] = 0xffffffe0; + ym2612->op_mask[5][3] = 0xffffffe0; + ym2612->op_mask[6][1] = 0xffffffe0; + ym2612->op_mask[6][2] = 0xffffffe0; + ym2612->op_mask[6][3] = 0xffffffe0; + ym2612->op_mask[7][0] = 0xffffffe0; + ym2612->op_mask[7][1] = 0xffffffe0; + ym2612->op_mask[7][2] = 0xffffffe0; + ym2612->op_mask[7][3] = 0xffffffe0; + } + else + { + /* 14-bit DAC */ + ym2612->op_mask[0][3] = 0xffffffff; + ym2612->op_mask[1][3] = 0xffffffff; + ym2612->op_mask[2][3] = 0xffffffff; + ym2612->op_mask[3][3] = 0xffffffff; + ym2612->op_mask[4][1] = 0xffffffff; + ym2612->op_mask[4][3] = 0xffffffff; + ym2612->op_mask[5][1] = 0xffffffff; + ym2612->op_mask[5][2] = 0xffffffff; + ym2612->op_mask[5][3] = 0xffffffff; + ym2612->op_mask[6][1] = 0xffffffff; + ym2612->op_mask[6][2] = 0xffffffff; + ym2612->op_mask[6][3] = 0xffffffff; + ym2612->op_mask[7][0] = 0xffffffff; + ym2612->op_mask[7][1] = 0xffffffff; + ym2612->op_mask[7][2] = 0xffffffff; + ym2612->op_mask[7][3] = 0xffffffff; + } +} diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.h b/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.h index 77cbaa87b..1f499d267 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/gx/gx_ym2612.h @@ -1,53 +1,53 @@ -/* -** -** software implementation of Yamaha FM sound generator (YM2612/YM3438) -** -** Original code (MAME fm.c) -** -** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) -** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development -** -** Version 1.4 (final beta) -** -** Additional code & fixes by Eke-Eke for Genesis Plus GX -** Adaptations by Jean Pierre Cimalando for use in libOPNMIDI. -** (based on fee2bc8 dated Jan 7th, 2018) -** -*/ - -#ifndef _H_YM2612_ -#define _H_YM2612_ - -#if defined(__cplusplus) -extern "C" { -#endif - -enum { - YM2612_DISCRETE = 0, - YM2612_INTEGRATED, - YM2612_ENHANCED -}; - -struct YM2612GX; -typedef struct YM2612GX YM2612GX; - -/* typedef signed int FMSAMPLE; */ -typedef signed short FMSAMPLE; - -extern YM2612GX *YM2612GXAlloc(); -extern void YM2612GXFree(YM2612GX *ym2612); -extern void YM2612GXInit(YM2612GX *ym2612); -extern void YM2612GXConfig(YM2612GX *ym2612, int type); -extern void YM2612GXResetChip(YM2612GX *ym2612); -extern void YM2612GXPreGenerate(YM2612GX *ym2612); -extern void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count); -extern void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame); -extern void YM2612GXWrite(YM2612GX *ym2612, unsigned int a, unsigned int v); -extern void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v); -extern unsigned int YM2612GXRead(YM2612GX *ym2612); - -#if defined(__cplusplus) -} /* extern "C" */ -#endif - -#endif /* _YM2612_ */ +/* +** +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) +** +** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) +** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.4 (final beta) +** +** Additional code & fixes by Eke-Eke for Genesis Plus GX +** Adaptations by Jean Pierre Cimalando for use in libOPNMIDI. +** (based on fee2bc8 dated Jan 7th, 2018) +** +*/ + +#ifndef _H_YM2612_ +#define _H_YM2612_ + +#if defined(__cplusplus) +extern "C" { +#endif + +enum { + YM2612_DISCRETE = 0, + YM2612_INTEGRATED, + YM2612_ENHANCED +}; + +struct YM2612GX; +typedef struct YM2612GX YM2612GX; + +/* typedef signed int FMSAMPLE; */ +typedef signed short FMSAMPLE; + +extern YM2612GX *YM2612GXAlloc(); +extern void YM2612GXFree(YM2612GX *ym2612); +extern void YM2612GXInit(YM2612GX *ym2612); +extern void YM2612GXConfig(YM2612GX *ym2612, int type); +extern void YM2612GXResetChip(YM2612GX *ym2612); +extern void YM2612GXPreGenerate(YM2612GX *ym2612); +extern void YM2612GXPostGenerate(YM2612GX *ym2612, unsigned int count); +extern void YM2612GXGenerateOneNative(YM2612GX *ym2612, FMSAMPLE *frame); +extern void YM2612GXWrite(YM2612GX *ym2612, unsigned int a, unsigned int v); +extern void YM2612GXWritePan(YM2612GX *chip, int c, unsigned char v); +extern unsigned int YM2612GXRead(YM2612GX *ym2612); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* _YM2612_ */ diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_diag.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_diag.h index e0ef26b39..20dc3707d 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_diag.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_diag.h @@ -1,15 +1,15 @@ -#ifndef incl_diag_h -#define incl_diag_h - -#define LOG0(m) void (0) -#define LOG1(m,a) void (0) -#define LOG2(m,a,b) void (0) -#define LOG3(m,a,b,c) void (0) -#define LOG4(m,a,b,c,d) void (0) -#define LOG5(m,a,b,c,d,e) void (0) -#define LOG6(m,a,b,c,d,e,f) void (0) -#define LOG7(m,a,b,c,d,e,f,g) void (0) -#define LOG8(m,a,b,c,d,e,f,g,h) void (0) -#define LOG9(m,a,b,c,d,e,f,g,h,i) void (0) - -#endif // incl_diag_h +#ifndef incl_diag_h +#define incl_diag_h + +#define LOG0(m) void (0) +#define LOG1(m,a) void (0) +#define LOG2(m,a,b) void (0) +#define LOG3(m,a,b,c) void (0) +#define LOG4(m,a,b,c,d) void (0) +#define LOG5(m,a,b,c,d,e) void (0) +#define LOG6(m,a,b,c,d,e,f) void (0) +#define LOG7(m,a,b,c,d,e,f,g) void (0) +#define LOG8(m,a,b,c,d,e,f,g,h) void (0) +#define LOG9(m,a,b,c,d,e,f,g,h,i) void (0) + +#endif // incl_diag_h diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.cpp b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.cpp index 22b8f9b1c..04974b844 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.cpp +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.cpp @@ -1,177 +1,177 @@ -// $Id: file.cpp,v 1.6 1999/12/28 11:14:05 cisc Exp $ - -#include -#include "fmgen_types.h" -#include "fmgen_headers.h" -#include "fmgen_file.h" - -// --------------------------------------------------------------------------- -// 構築/消滅 -// --------------------------------------------------------------------------- - -FileIO::FileIO() -{ - flags = 0; -} - -FileIO::FileIO(const char* filename, uint flg) -{ - flags = 0; - Open(filename, flg); -} - -FileIO::~FileIO() -{ - Close(); -} - -// --------------------------------------------------------------------------- -// ファイルを開く -// --------------------------------------------------------------------------- - -bool FileIO::Open(const char* filename, uint flg) -{ - char mode[5] = "rwb"; - Close(); - - strncpy(path, filename, MAX_PATH - 1); - - if(flg & readonly) - strcpy(mode, "rb"); - else { - if(flg & create) - strcpy(mode, "rwb+"); - else - strcpy(mode, "rwb"); - } - - pfile = fopen(filename, mode); - - flags = (flg & readonly) | (pfile == NULL ? 0 : open); - - if (pfile == NULL) - error = file_not_found; - - SetLogicalOrigin(0); - - return !(pfile == NULL); -} - -// --------------------------------------------------------------------------- -// ファイルがない場合は作成 -// --------------------------------------------------------------------------- - -bool FileIO::CreateNew(const char* filename) -{ - uint flg = create; - - return Open(filename, flg); -} - -// --------------------------------------------------------------------------- -// ファイルを作り直す -// --------------------------------------------------------------------------- - -bool FileIO::Reopen(uint flg) -{ - if (!(flags & open)) return false; - if ((flags & readonly) && (flg & create)) return false; - - if (flags & readonly) flg |= readonly; - - return Open(path, flg); -} - -// --------------------------------------------------------------------------- -// ファイルを閉じる -// --------------------------------------------------------------------------- - -void FileIO::Close() -{ - if (GetFlags() & open) - { - fclose(pfile); - flags = 0; - } -} - -// --------------------------------------------------------------------------- -// ファイル殻の読み出し -// --------------------------------------------------------------------------- - -int32 FileIO::Read(void* dest, int32 size) -{ - if (!(GetFlags() & open)) - return -1; - - size_t readsize; - if (!(readsize = fread(dest, 1, static_cast(size), pfile))) - return -1; - return size; -} - -// --------------------------------------------------------------------------- -// ファイルへの書き出し -// --------------------------------------------------------------------------- - -int32 FileIO::Write(const void* dest, int32 size) -{ - if (!(GetFlags() & open) || (GetFlags() & readonly)) - return -1; - - size_t writtensize; - if (!(writtensize = fwrite(dest, 1, static_cast(size), pfile))) - return -1; - return static_cast(writtensize); -} - -// --------------------------------------------------------------------------- -// ファイルをシーク -// --------------------------------------------------------------------------- - -bool FileIO::Seek(int32 pos, SeekMethod method) -{ - if (!(GetFlags() & open)) - return false; - - int origin; - switch (method) - { - case begin: - origin = SEEK_SET; - break; - case current: - origin = SEEK_CUR; - break; - case end: - origin = SEEK_END; - break; - default: - return false; - } - - return (fseek(pfile, pos, origin) != 0); -} - -// --------------------------------------------------------------------------- -// ファイルの位置を得る -// --------------------------------------------------------------------------- - -int32 FileIO::Tellp() -{ - if (!(GetFlags() & open)) - return 0; - - return static_cast(ftell(pfile)); -} - -// --------------------------------------------------------------------------- -// 現在の位置をファイルの終端とする -// --------------------------------------------------------------------------- - -bool FileIO::SetEndOfFile() -{ - if (!(GetFlags() & open)) - return false; - return Seek(0, end); -} +// $Id: file.cpp,v 1.6 1999/12/28 11:14:05 cisc Exp $ + +#include +#include "fmgen_types.h" +#include "fmgen_headers.h" +#include "fmgen_file.h" + +// --------------------------------------------------------------------------- +// 構築/消滅 +// --------------------------------------------------------------------------- + +FileIO::FileIO() +{ + flags = 0; +} + +FileIO::FileIO(const char* filename, uint flg) +{ + flags = 0; + Open(filename, flg); +} + +FileIO::~FileIO() +{ + Close(); +} + +// --------------------------------------------------------------------------- +// ファイルを開く +// --------------------------------------------------------------------------- + +bool FileIO::Open(const char* filename, uint flg) +{ + char mode[5] = "rwb"; + Close(); + + strncpy(path, filename, MAX_PATH - 1); + + if(flg & readonly) + strcpy(mode, "rb"); + else { + if(flg & create) + strcpy(mode, "rwb+"); + else + strcpy(mode, "rwb"); + } + + pfile = fopen(filename, mode); + + flags = (flg & readonly) | (pfile == NULL ? 0 : open); + + if (pfile == NULL) + error = file_not_found; + + SetLogicalOrigin(0); + + return !(pfile == NULL); +} + +// --------------------------------------------------------------------------- +// ファイルがない場合は作成 +// --------------------------------------------------------------------------- + +bool FileIO::CreateNew(const char* filename) +{ + uint flg = create; + + return Open(filename, flg); +} + +// --------------------------------------------------------------------------- +// ファイルを作り直す +// --------------------------------------------------------------------------- + +bool FileIO::Reopen(uint flg) +{ + if (!(flags & open)) return false; + if ((flags & readonly) && (flg & create)) return false; + + if (flags & readonly) flg |= readonly; + + return Open(path, flg); +} + +// --------------------------------------------------------------------------- +// ファイルを閉じる +// --------------------------------------------------------------------------- + +void FileIO::Close() +{ + if (GetFlags() & open) + { + fclose(pfile); + flags = 0; + } +} + +// --------------------------------------------------------------------------- +// ファイル殻の読み出し +// --------------------------------------------------------------------------- + +int32 FileIO::Read(void* dest, int32 size) +{ + if (!(GetFlags() & open)) + return -1; + + size_t readsize; + if (!(readsize = fread(dest, 1, static_cast(size), pfile))) + return -1; + return size; +} + +// --------------------------------------------------------------------------- +// ファイルへの書き出し +// --------------------------------------------------------------------------- + +int32 FileIO::Write(const void* dest, int32 size) +{ + if (!(GetFlags() & open) || (GetFlags() & readonly)) + return -1; + + size_t writtensize; + if (!(writtensize = fwrite(dest, 1, static_cast(size), pfile))) + return -1; + return static_cast(writtensize); +} + +// --------------------------------------------------------------------------- +// ファイルをシーク +// --------------------------------------------------------------------------- + +bool FileIO::Seek(int32 pos, SeekMethod method) +{ + if (!(GetFlags() & open)) + return false; + + int origin; + switch (method) + { + case begin: + origin = SEEK_SET; + break; + case current: + origin = SEEK_CUR; + break; + case end: + origin = SEEK_END; + break; + default: + return false; + } + + return (fseek(pfile, pos, origin) != 0); +} + +// --------------------------------------------------------------------------- +// ファイルの位置を得る +// --------------------------------------------------------------------------- + +int32 FileIO::Tellp() +{ + if (!(GetFlags() & open)) + return 0; + + return static_cast(ftell(pfile)); +} + +// --------------------------------------------------------------------------- +// 現在の位置をファイルの終端とする +// --------------------------------------------------------------------------- + +bool FileIO::SetEndOfFile() +{ + if (!(GetFlags() & open)) + return false; + return Seek(0, end); +} diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.h index e54297fa3..8f02f2052 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_file.h @@ -1,64 +1,64 @@ -// $Id: file.h,v 1.6 1999/11/26 10:14:09 cisc Exp $ - -#if !defined(win32_file_h) -#define win32_file_h - -#include "fmgen_types.h" - -// --------------------------------------------------------------------------- - -class FileIO -{ -public: - enum Flags - { - open = 0x000001, - readonly = 0x000002, - create = 0x000004 - }; - - enum SeekMethod - { - begin = 0, current = 1, end = 2 - }; - - enum Error - { - success = 0, - file_not_found, - sharing_violation, - unknown = -1 - }; - -public: - FileIO(); - FileIO(const char* filename, uint flg = 0); - virtual ~FileIO(); - - bool Open(const char* filename, uint flg = 0); - bool CreateNew(const char* filename); - bool Reopen(uint flg = 0); - void Close(); - Error GetError() { return error; } - - int32 Read(void* dest, int32 len); - int32 Write(const void* src, int32 len); - bool Seek(int32 fpos, SeekMethod method); - int32 Tellp(); - bool SetEndOfFile(); - - uint GetFlags() { return flags; } - void SetLogicalOrigin(int32 origin) { lorigin = origin; } - -private: - FILE* pfile; - uint flags; - uint32 lorigin; - Error error; - char path[MAX_PATH]; - - FileIO(const FileIO&); - const FileIO& operator=(const FileIO&); -}; - -#endif // +// $Id: file.h,v 1.6 1999/11/26 10:14:09 cisc Exp $ + +#if !defined(win32_file_h) +#define win32_file_h + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- + +class FileIO +{ +public: + enum Flags + { + open = 0x000001, + readonly = 0x000002, + create = 0x000004 + }; + + enum SeekMethod + { + begin = 0, current = 1, end = 2 + }; + + enum Error + { + success = 0, + file_not_found, + sharing_violation, + unknown = -1 + }; + +public: + FileIO(); + FileIO(const char* filename, uint flg = 0); + virtual ~FileIO(); + + bool Open(const char* filename, uint flg = 0); + bool CreateNew(const char* filename); + bool Reopen(uint flg = 0); + void Close(); + Error GetError() { return error; } + + int32 Read(void* dest, int32 len); + int32 Write(const void* src, int32 len); + bool Seek(int32 fpos, SeekMethod method); + int32 Tellp(); + bool SetEndOfFile(); + + uint GetFlags() { return flags; } + void SetLogicalOrigin(int32 origin) { lorigin = origin; } + +private: + FILE* pfile; + uint flags; + uint32 lorigin; + Error error; + char path[MAX_PATH]; + + FileIO(const FileIO&); + const FileIO& operator=(const FileIO&); +}; + +#endif // diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp index d5b30d3be..f389985eb 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.cpp @@ -1,1156 +1,1156 @@ -// --------------------------------------------------------------------------- -// FM Sound Generator - Core Unit -// Copyright (C) cisc 1998, 2003. -// --------------------------------------------------------------------------- -// $Id: fmgen.cpp,v 1.49 2003/09/02 14:51:04 cisc Exp $ -// --------------------------------------------------------------------------- -// 参考: -// FM sound generator for M.A.M.E., written by Tatsuyuki Satoh. -// -// 謎: -// OPNB の CSM モード(仕様がよくわからない) -// -// 制限: -// ・AR!=31 で SSGEC を使うと波形が実際と異なる可能性あり -// -// 謝辞: -// Tatsuyuki Satoh さん(fm.c) -// Hiromitsu Shioya さん(ADPCM-A) -// DMP-SOFT. さん(OPNB) -// KAJA さん(test program) -// ほか掲示板等で様々なご助言,ご支援をお寄せいただいた皆様に -// --------------------------------------------------------------------------- - -#include "fmgen_headers.h" -#include "fmgen_misc.h" -#include "fmgen_fmgen.h" -#include "fmgen_fmgeninl.h" - -#define LOGNAME "fmgen" - -// --------------------------------------------------------------------------- - -#define FM_EG_BOTTOM 955 - -// --------------------------------------------------------------------------- -// Table/etc -// -namespace FM -{ - const uint8 Operator::notetable[128] = - { - 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, - 16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19, - 20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, - 24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27, - 28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, - }; - - const int8 Operator::dttable[256] = - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, - 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, 16, 16, 16, 16, - 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, - 10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32, - 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, - 16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4, - -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16, - -2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10, - -10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32, - -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14, - -16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44, - }; - - const int8 Operator::decaytable1[64][8] = - { - {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 0, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, - {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, - {1, 1, 1, 1, 1, 1, 1, 1}, {2, 1, 1, 1, 2, 1, 1, 1}, - {2, 1, 2, 1, 2, 1, 2, 1}, {2, 2, 2, 1, 2, 2, 2, 1}, - {2, 2, 2, 2, 2, 2, 2, 2}, {4, 2, 2, 2, 4, 2, 2, 2}, - {4, 2, 4, 2, 4, 2, 4, 2}, {4, 4, 4, 2, 4, 4, 4, 2}, - {4, 4, 4, 4, 4, 4, 4, 4}, {8, 4, 4, 4, 8, 4, 4, 4}, - {8, 4, 8, 4, 8, 4, 8, 4}, {8, 8, 8, 4, 8, 8, 8, 4}, - {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, - {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, - }; - - const int Operator::decaytable2[16] = - { - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2047, 2047, 2047, 2047, 2047 - }; - - const int8 Operator::attacktable[64][8] = - { - {-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1}, - { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, - { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4,-1, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, - { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, - { 4, 4, 4, 4, 4, 4, 4, 4}, { 3, 4, 4, 4, 3, 4, 4, 4}, - { 3, 4, 3, 4, 3, 4, 3, 4}, { 3, 3, 3, 4, 3, 3, 3, 4}, - { 3, 3, 3, 3, 3, 3, 3, 3}, { 2, 3, 3, 3, 2, 3, 3, 3}, - { 2, 3, 2, 3, 2, 3, 2, 3}, { 2, 2, 2, 3, 2, 2, 2, 3}, - { 2, 2, 2, 2, 2, 2, 2, 2}, { 1, 2, 2, 2, 1, 2, 2, 2}, - { 1, 2, 1, 2, 1, 2, 1, 2}, { 1, 1, 1, 2, 1, 1, 1, 2}, - { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, - }; - -#if 0 // libOPNMIDI: experimental SSG-EG - const int Operator::ssgenvtable[8][2][3][2] = - { - {{{1, 1}, {1, 1}, {1, 1}}, // 08 - {{0, 1}, {1, 1}, {1, 1}}}, // 08 56~ - {{{0, 1}, {2, 0}, {2, 0}}, // 09 - {{0, 1}, {2, 0}, {2, 0}}}, // 09 - {{{1,-1}, {0, 1}, {1,-1}}, // 10 - {{0, 1}, {1,-1}, {0, 1}}}, // 10 60~ - {{{1,-1}, {0, 0}, {0, 0}}, // 11 - {{0, 1}, {0, 0}, {0, 0}}}, // 11 60~ - {{{2,-1}, {2,-1}, {2,-1}}, // 12 - {{1,-1}, {2,-1}, {2,-1}}}, // 12 56~ - {{{1,-1}, {0, 0}, {0, 0}}, // 13 - {{1,-1}, {0, 0}, {0, 0}}}, // 13 - {{{0, 1}, {1,-1}, {0, 1}}, // 14 - {{1,-1}, {0, 1}, {1,-1}}}, // 14 60~ - {{{0, 1}, {2, 0}, {2, 0}}, // 15 - {{1,-1}, {2, 0}, {2, 0}}}, // 15 60~ - }; -#endif - - // fixed equasion-based tables - int pmtable[2][8][FM_LFOENTS]; - uint amtable[2][4][FM_LFOENTS]; - - static bool tablemade = false; -} - -namespace FM -{ - -// --------------------------------------------------------------------------- -// テーブル作成 -// -void MakeLFOTable() -{ - if (tablemade) - return; - - tablemade = true; - - int i; - - static const double pms[2][8] = - { - { 0, 1/360., 2/360., 3/360., 4/360., 6/360., 12/360., 24/360., }, // OPNA -// { 0, 1/240., 2/240., 4/240., 10/240., 20/240., 80/240., 140/240., }, // OPM - { 0, 1/480., 2/480., 4/480., 10/480., 20/480., 80/480., 140/480., }, // OPM -// { 0, 1/960., 2/960., 4/960., 10/960., 20/960., 80/960., 140/960., }, // OPM - }; - // 3 6, 12 30 60 240 420 / 720 - // 1.000963 - // lfofref[level * max * wave]; - // pre = lfofref[level][pms * wave >> 8]; - static const uint8 amt[2][4] = - { - { 31, 6, 4, 3 }, // OPNA - { 31, 2, 1, 0 }, // OPM - }; - - for (int type = 0; type < 2; type++) - { - for (i=0; i<8; i++) - { - double pmb = pms[type][i]; - for (int j=0; j> amt[type][i]) * 2) << 2; - } - } - } -} - - -// --------------------------------------------------------------------------- -// チップ内で共通な部分 -// -Chip::Chip() -: ratio_(0), aml_(0), pml_(0), pmv_(0) -//, optype_(typeN) -{ -} - -// クロック・サンプリングレート比に依存するテーブルを作成 -void Chip::SetRatio(uint ratio) -{ - if (ratio_ != ratio) - { - ratio_ = ratio; - MakeTable(); - } -} - -void Chip::MakeTable() -{ - int h, l; - - // PG Part - static const float dt2lv[4] = { 1.f, 1.414f, 1.581f, 1.732f }; - for (h=0; h<4; h++) - { - assert(2 + FM_RATIOBITS - FM_PGBITS >= 0); - double rr = dt2lv[h] * double(ratio_) / (1 << (2 + FM_RATIOBITS - FM_PGBITS)); - for (l=0; l<16; l++) - { - int mul = l ? l * 2 : 1; - multable_[h][l] = uint(mul * rr); - } - } -} - -void Chip::DataSave(struct ChipData* data) -{ - data->ratio_ = ratio_; - data->aml_ = aml_; - data->pml_ = pml_; - data->pmv_ = pmv_; - memcpy(data->multable_, multable_, sizeof(uint32) * 4 * 16); -} - -void Chip::DataLoad(struct ChipData* data) -{ - ratio_ = data->ratio_; - aml_ = data->aml_; - pml_ = data->pml_; - pmv_ = data->pmv_; - memcpy(multable_, data->multable_, sizeof(uint32) * 4 * 16); -} - -// --------------------------------------------------------------------------- -// Operator -// -bool FM::Operator::tablehasmade = false; -uint FM::Operator::sinetable[1024]; -int32 FM::Operator::cltable[FM_CLENTS]; - -// 構築 -FM::Operator::Operator() -: chip_(0) -{ - if (!tablehasmade) - MakeTable(); - - // EG Part - ar_ = dr_ = sr_ = rr_ = key_scale_rate_ = 0; - ams_ = amtable[0][0]; - mute_ = false; - keyon_ = false; - tl_out_ = false; - ssg_type_ = 0; -#if 1 // libOPNMIDI: experimental SSG-EG - inverted_ = false; - held_ = false; -#endif - - // PG Part - multiple_ = 0; - detune_ = 0; - detune2_ = 0; - - // LFO - ms_ = 0; - -// Reset(); -} - -// 初期化 -void FM::Operator::Reset() -{ - // EG part - tl_ = tl_latch_ = 127; - ShiftPhase(off); - eg_count_ = 0; - eg_curve_count_ = 0; -#if 1 // libOPNMIDI: experimental SSG-EG - inverted_ = false; - held_ = false; -#else - ssg_phase_ = 0; -#endif - - // PG part - pg_count_ = 0; - - // OP part - out_ = out2_ = 0; - - param_changed_ = true; - PARAMCHANGE(0); -} - -void Operator::MakeTable() -{ - // 対数テーブルの作成 - assert(FM_CLENTS >= 256); - - int* p = cltable; - int i; - for (i=0; i<256; i++) - { - int v = int(floor(pow(2., 13. - i / 256.))); - v = (v + 2) & ~3; - *p++ = v; - *p++ = -v; - } - while (p < cltable + FM_CLENTS) - { - //*p++ = p[-512] / 2; - *p = p[-512] / 2; - p++; - } - -// for (i=0; i<13*256; i++) -// printf("%4d, %d, %d\n", i, cltable[i*2], cltable[i*2+1]); - - // サインテーブルの作成 - double log2 = log(2.); - for (i=0; iGetMulValue(detune2_, multiple_); - pg_diff_lfo_ = pg_diff_ >> 11; - - // EG Part - key_scale_rate_ = bn_ >> (3-ks_); - tl_out_ = mute_ ? 0x3ff : tl_ * 8; - - switch (eg_phase_) - { - case attack: - SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); - break; - case decay: - SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); - eg_level_on_next_phase_ = sl_ * 8; - break; - case sustain: - SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); - break; - case release: - SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); - break; - default: - break; - } - - // SSG-EG - inverted_ = false; - held_ = false; - if (ssg_type_ && (eg_phase_ != release)) - { -#if 1 // libOPNMIDI: experimental SSG-EG - inverted_ = (ssg_type_ & 4) != 0; - inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN -#else - int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); - - assert(0 <= ssg_phase_ && ssg_phase_ <= 2); - const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; - - ssg_offset_ = table[0] * 0x200; - ssg_vector_ = table[1]; -#endif - } - // LFO - ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; - EGUpdate(); - - dbgopout_ = 0; - } -} - -void Operator::DataSave(struct OperatorData* data) -{ - data->out_ = out_; - data->out2_ = out2_; - data->in2_ = in2_; - data->dp_ = dp_; - data->detune_ = detune_; - data->detune2_ = detune2_; - data->multiple_ = multiple_; - data->pg_count_ = pg_count_; - data->pg_diff_ = pg_diff_; - data->pg_diff_lfo_ = pg_diff_lfo_; - data->type_ = type_; - data->bn_ = bn_; - data->eg_level_ = eg_level_; - data->eg_level_on_next_phase_ = eg_level_on_next_phase_; - data->eg_count_ = eg_count_; - data->eg_count_diff_ = eg_count_diff_; - data->eg_out_ = eg_out_; - data->tl_out_ = tl_out_; - data->eg_rate_ = eg_rate_; - data->eg_curve_count_ = eg_curve_count_; -#if 0 // libOPNMIDI: experimental SSG-EG - data->ssg_offset_ = ssg_offset_; - data->ssg_vector_ = ssg_vector_; - data->ssg_phase_ = ssg_phase_; -#endif - data->key_scale_rate_ = key_scale_rate_; - data->eg_phase_ = eg_phase_; - data->ms_ = ms_; - data->tl_ = tl_; - data->tl_latch_ = tl_latch_; - data->ar_ = ar_; - data->dr_ = dr_; - data->sr_ = sr_; - data->sl_ = sl_; - data->rr_ = rr_; - data->ks_ = ks_; - data->ssg_type_ = ssg_type_; - data->keyon_ = keyon_; - data->amon_ = amon_; - data->param_changed_ = param_changed_; - data->mute_ = mute_; - data->inverted_ = inverted_; - data->held_ = held_; -} - -void Operator::DataLoad(struct OperatorData* data) -{ - out_ = data->out_; - out2_ = data->out2_; - in2_ = data->in2_; - dp_ = data->dp_; - detune_ = data->detune_; - detune2_ = data->detune2_; - multiple_ = data->multiple_; - pg_count_ = data->pg_count_; - pg_diff_ = data->pg_diff_; - pg_diff_lfo_ = data->pg_diff_lfo_; - type_ = data->type_; - bn_ = data->bn_; - eg_level_ = data->eg_level_; - eg_level_on_next_phase_ = data->eg_level_on_next_phase_; - eg_count_ = data->eg_count_; - eg_count_diff_ = data->eg_count_diff_; - eg_out_ = data->eg_out_; - tl_out_ = data->tl_out_; - eg_rate_ = data->eg_rate_; - eg_curve_count_ = data->eg_curve_count_; -#if 0 // libOPNMIDI: experimental SSG-EG - ssg_offset_ = data->ssg_offset_; - ssg_vector_ = data->ssg_vector_; - ssg_phase_ = data->ssg_phase_; -#endif - key_scale_rate_ = data->key_scale_rate_; - eg_phase_ = data->eg_phase_; - ms_ = data->ms_; - tl_ = data->tl_; - tl_latch_ = data->tl_latch_; - ar_ = data->ar_; - dr_ = data->dr_; - sr_ = data->sr_; - sl_ = data->sl_; - rr_ = data->rr_; - ks_ = data->ks_; - ssg_type_ = data->ssg_type_; - keyon_ = data->keyon_; - amon_ = data->amon_; - param_changed_ = data->param_changed_; - mute_ = data->mute_; - inverted_ = data->inverted_; - held_ = data->held_; - ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; -} - - -// envelop の eg_phase_ 変更 -void Operator::ShiftPhase(EGPhase nextphase) -{ - switch (nextphase) - { - case attack: // Attack Phase - tl_ = tl_latch_; - if (ssg_type_) - { -#if 0 // libOPNMIDI: experimental SSG-EG - ssg_phase_ = ssg_phase_ + 1; - if (ssg_phase_ > 2) - ssg_phase_ = 1; - - int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); - - assert(0 <= ssg_phase_ && ssg_phase_ <= 2); - const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; - - ssg_offset_ = table[0] * 0x200; - ssg_vector_ = table[1]; -#endif - } - if ((ar_ + key_scale_rate_) < 62) - { - SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); - eg_phase_ = attack; - break; - } - // fall through - case decay: // Decay Phase - if (sl_) - { - eg_level_ = 0; - eg_level_on_next_phase_ = ssg_type_ ? Min(sl_ * 8, 0x200) : sl_ * 8; - - SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); - eg_phase_ = decay; - break; - } - // fall through - case sustain: // Sustain Phase - eg_level_ = sl_ * 8; - eg_level_on_next_phase_ = ssg_type_ ? 0x200 : 0x400; - - SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); - eg_phase_ = sustain; - break; - - case release: // Release Phase - inverted_ = false; - held_ = false; -#if 0 // libOPNMIDI: experimental SSG-EG - if (ssg_type_) - { - eg_level_ = eg_level_ * ssg_vector_ + ssg_offset_; - ssg_vector_ = 1; - ssg_offset_ = 0; - } -#endif - if (eg_phase_ == attack || (eg_level_ < FM_EG_BOTTOM)) //0x400/* && eg_phase_ != off*/)) - { - eg_level_on_next_phase_ = 0x400; - SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); - eg_phase_ = release; - break; - } - // fall through - case off: // off - default: - eg_level_ = FM_EG_BOTTOM; - eg_level_on_next_phase_ = FM_EG_BOTTOM; - EGUpdate(); - SetEGRate(0); - eg_phase_ = off; - break; - } -} - -// Block/F-Num -void Operator::SetFNum(uint f) -{ - dp_ = (f & 2047) << ((f >> 11) & 7); - bn_ = notetable[(f >> 7) & 127]; - param_changed_ = true; - PARAMCHANGE(2); -} - -// 1サンプル合成 - -// ISample を envelop count (2π) に変換するシフト量 -#define IS2EC_SHIFT ((20 + FM_PGBITS) - 13) - - -// 入力: s = 20+FM_PGBITS = 29 -#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)] -#define SINE(s) sinetable[(s) & (FM_OPSINENTS-1)] - -inline FM::ISample Operator::LogToLin(uint a) -{ -#if 1 // FM_CLENTS < 0xc00 // 400 for TL, 400 for ENV, 400 for LFO. - return (a < FM_CLENTS) ? cltable[a] : 0; -#else - return cltable[a]; -#endif -} - -inline void Operator::EGUpdate() -{ -#if 1 // libOPNMIDI: experimental SSG-EG - int level = eg_level_; - level = (!inverted_) ? level : (512 - level) & 0x3ff; - eg_out_ = Min(tl_out_ + level, 0x3ff) << (1 + 2); -#else - if (!ssg_type_) - { - eg_out_ = Min(tl_out_ + eg_level_, 0x3ff) << (1 + 2); - } - else - { - eg_out_ = Min(tl_out_ + eg_level_ * ssg_vector_ + ssg_offset_, 0x3ff) << (1 + 2); - } -#endif -} - -inline void Operator::SetEGRate(uint rate) -{ - eg_rate_ = rate; - eg_count_diff_ = decaytable2[rate / 4] * chip_->GetRatio(); -} - -// EG 計算 -void FM::Operator::EGCalc() -{ - eg_count_ = (2047 * 3) << FM_RATIOBITS; // ##この手抜きは再現性を低下させる - - if (eg_phase_ == attack) - { - int c = attacktable[eg_rate_][eg_curve_count_ & 7]; - if (c >= 0) - { - eg_level_ -= 1 + (eg_level_ >> c); - if (eg_level_ <= 0) - ShiftPhase(decay); - } - EGUpdate(); - } - else - { - if (!ssg_type_) - { - eg_level_ += decaytable1[eg_rate_][eg_curve_count_ & 7]; - if (eg_level_ >= eg_level_on_next_phase_) - ShiftPhase(EGPhase(eg_phase_+1)); - EGUpdate(); - } - else - { - if (!held_) - eg_level_ += 4 * decaytable1[eg_rate_][eg_curve_count_ & 7]; - else - eg_level_ = (((ssg_type_ & 4) != 0) ^ ((ssg_type_ & 2) != 0)) ? 0 : 1024;; - EGUpdate(); // libOPNMIDI: experimental SSG-EG - if (eg_level_ >= eg_level_on_next_phase_) - { - switch (eg_phase_) - { - case decay: - ShiftPhase(sustain); - break; - case sustain: -#if 1 // libOPNMIDI: experimental SSG-EG - if (ssg_type_ & 1) - { - inverted_ = false; - held_ = true; - } - if (!held_) - { - inverted_ ^= (ssg_type_ & 2) && (ar_ == 62); // try to match polarity with nuked OPN - ShiftPhase(attack); - } -#else - ShiftPhase(attack); -#endif - break; - case release: - ShiftPhase(off); - break; - default: - break; - } - } - } - } - eg_curve_count_++; -} - -inline void FM::Operator::EGStep() -{ - eg_count_ -= eg_count_diff_; - - // EG の変化は全スロットで同期しているという噂もある - if (eg_count_ <= 0) - EGCalc(); -} - -// PG 計算 -// ret:2^(20+PGBITS) / cycle -inline uint32 FM::Operator::PGCalc() -{ - uint32 ret = pg_count_; - pg_count_ += pg_diff_; - dbgpgout_ = ret; - return ret; -} - -inline uint32 FM::Operator::PGCalcL() -{ - uint32 ret = pg_count_; - pg_count_ += pg_diff_ + ((pg_diff_lfo_ * chip_->GetPMV()) >> 5);// & -(1 << (2+IS2EC_SHIFT))); - dbgpgout_ = ret; - return ret /* + pmv * pg_diff_;*/; -} - -// OP 計算 -// in: ISample (最大 8π) -inline FM::ISample FM::Operator::Calc(ISample in) -{ - EGStep(); - out2_ = out_; - - int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); - pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); - out_ = LogToLin(eg_out_ + SINE(pgin)); - - dbgopout_ = out_; - return out_; -} - -inline FM::ISample FM::Operator::CalcL(ISample in) -{ - EGStep(); - - int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); - pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); - out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); - - dbgopout_ = out_; - return out_; -} - -inline FM::ISample FM::Operator::CalcN(uint noise) -{ - EGStep(); - - int lv = Max(0, 0x3ff - (tl_out_ + eg_level_)) << 1; - - // noise & 1 ? lv : -lv と等価 - noise = (noise & 1) - 1; - out_ = (lv + noise) ^ noise; - - dbgopout_ = out_; - return out_; -} - -// OP (FB) 計算 -// Self Feedback の変調最大 = 4π -inline FM::ISample FM::Operator::CalcFB(uint fb) -{ - EGStep(); - - ISample in = out_ + out2_; - out2_ = out_; - - int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); - if (fb < 31) - { - pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); - } - out_ = LogToLin(eg_out_ + SINE(pgin)); - dbgopout_ = out2_; - - return out2_; -} - -inline FM::ISample FM::Operator::CalcFBL(uint fb) -{ - EGStep(); - - ISample in = out_ + out2_; - out2_ = out_; - - int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); - if (fb < 31) - { - pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); - } - - out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); - dbgopout_ = out_; - - return out_; -} - -#undef Sine - -// --------------------------------------------------------------------------- -// 4-op Channel -// -const uint8 Channel4::fbtable[8] = { 31, 7, 6, 5, 4, 3, 2, 1 }; -int Channel4::kftable[64]; - -bool Channel4::tablehasmade = false; - - -Channel4::Channel4() -{ - if (!tablehasmade) - MakeTable(); - - SetAlgorithm(0); - pms = pmtable[0][0]; -} - -void Channel4::MakeTable() -{ - // 100/64 cent = 2^(i*100/64*1200) - for (int i=0; i<64; i++) - { - kftable[i] = int(0x10000 * pow(2., i / 768.) ); - } -} - -// リセット -void Channel4::Reset() -{ - op[0].Reset(); - op[1].Reset(); - op[2].Reset(); - op[3].Reset(); -} - -// Calc の用意 -int Channel4::Prepare() -{ - op[0].Prepare(); - op[1].Prepare(); - op[2].Prepare(); - op[3].Prepare(); - - pms = pmtable[op[0].type_][op[0].ms_ & 7]; - int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0; - int lfo = op[0].ms_ & ((op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_) ? 0x37 : 7) ? 2 : 0; - return key | lfo; -} - -// F-Number/BLOCK を設定 -void Channel4::SetFNum(uint f) -{ - for (int i=0; i<4; i++) - op[i].SetFNum(f); -} - -// KC/KF を設定 -void Channel4::SetKCKF(uint kc, uint kf) -{ - const static uint kctable[16] = - { - 5197, 5506, 5833, 6180, 6180, 6547, 6937, 7349, - 7349, 7786, 8249, 8740, 8740, 9259, 9810, 10394, - }; - - int oct = 19 - ((kc >> 4) & 7); - -//printf("%p", this); - uint kcv = kctable[kc & 0x0f]; - kcv = (kcv + 2) / 4 * 4; -//printf(" %.4x", kcv); - uint dp = kcv * kftable[kf & 0x3f]; -//printf(" %.4x %.4x %.8x", kcv, kftable[kf & 0x3f], dp >> oct); - dp >>= 16 + 3; - dp <<= 16 + 3; - dp >>= oct; - uint bn = (kc >> 2) & 31; - op[0].SetDPBN(dp, bn); - op[1].SetDPBN(dp, bn); - op[2].SetDPBN(dp, bn); - op[3].SetDPBN(dp, bn); -//printf(" %.8x\n", dp); -} - -// キー制御 -void Channel4::KeyControl(uint key) -{ - if (key & 0x1) op[0].KeyOn(); else op[0].KeyOff(); - if (key & 0x2) op[1].KeyOn(); else op[1].KeyOff(); - if (key & 0x4) op[2].KeyOn(); else op[2].KeyOff(); - if (key & 0x8) op[3].KeyOn(); else op[3].KeyOff(); -} - -// アルゴリズムを設定 -void Channel4::SetAlgorithm(uint algo) -{ - static const uint8 table1[8][6] = - { - { 0, 1, 1, 2, 2, 3 }, { 1, 0, 0, 1, 1, 2 }, - { 1, 1, 1, 0, 0, 2 }, { 0, 1, 2, 1, 1, 2 }, - { 0, 1, 2, 2, 2, 1 }, { 0, 1, 0, 1, 0, 1 }, - { 0, 1, 2, 1, 2, 1 }, { 1, 0, 1, 0, 1, 0 }, - }; - - in [0] = &buf[table1[algo][0]]; - out[0] = &buf[table1[algo][1]]; - in [1] = &buf[table1[algo][2]]; - out[1] = &buf[table1[algo][3]]; - in [2] = &buf[table1[algo][4]]; - out[2] = &buf[table1[algo][5]]; - - op[0].ResetFB(); - algo_ = algo; -} - -// 合成 -ISample Channel4::Calc() -{ - int r; - switch (algo_) - { - case 0: - op[2].Calc(op[1].Out()); - op[1].Calc(op[0].Out()); - r = op[3].Calc(op[2].Out()); - op[0].CalcFB(fb); - break; - case 1: - op[2].Calc(op[0].Out() + op[1].Out()); - op[1].Calc(0); - r = op[3].Calc(op[2].Out()); - op[0].CalcFB(fb); - break; - case 2: - op[2].Calc(op[1].Out()); - op[1].Calc(0); - r = op[3].Calc(op[0].Out() + op[2].Out()); - op[0].CalcFB(fb); - break; - case 3: - op[2].Calc(0); - op[1].Calc(op[0].Out()); - r = op[3].Calc(op[1].Out() + op[2].Out()); - op[0].CalcFB(fb); - break; - case 4: - op[2].Calc(0); - r = op[1].Calc(op[0].Out()); - r += op[3].Calc(op[2].Out()); - op[0].CalcFB(fb); - break; - case 5: - r = op[2].Calc(op[0].Out()); - r += op[1].Calc(op[0].Out()); - r += op[3].Calc(op[0].Out()); - op[0].CalcFB(fb); - break; - case 6: - r = op[2].Calc(0); - r += op[1].Calc(op[0].Out()); - r += op[3].Calc(0); - op[0].CalcFB(fb); - break; - case 7: - r = op[2].Calc(0); - r += op[1].Calc(0); - r += op[3].Calc(0); - r += op[0].CalcFB(fb); - break; - default: - assert(false); - r = 0; - break; - } - return r; -} - -// 合成 -ISample Channel4::CalcL() -{ - chip_->SetPMV(pms[chip_->GetPML()]); - - int r; - switch (algo_) - { - case 0: - op[2].CalcL(op[1].Out()); - op[1].CalcL(op[0].Out()); - r = op[3].CalcL(op[2].Out()); - op[0].CalcFBL(fb); - break; - case 1: - op[2].CalcL(op[0].Out() + op[1].Out()); - op[1].CalcL(0); - r = op[3].CalcL(op[2].Out()); - op[0].CalcFBL(fb); - break; - case 2: - op[2].CalcL(op[1].Out()); - op[1].CalcL(0); - r = op[3].CalcL(op[0].Out() + op[2].Out()); - op[0].CalcFBL(fb); - break; - case 3: - op[2].CalcL(0); - op[1].CalcL(op[0].Out()); - r = op[3].CalcL(op[1].Out() + op[2].Out()); - op[0].CalcFBL(fb); - break; - case 4: - op[2].CalcL(0); - r = op[1].CalcL(op[0].Out()); - r += op[3].CalcL(op[2].Out()); - op[0].CalcFBL(fb); - break; - case 5: - r = op[2].CalcL(op[0].Out()); - r += op[1].CalcL(op[0].Out()); - r += op[3].CalcL(op[0].Out()); - op[0].CalcFBL(fb); - break; - case 6: - r = op[2].CalcL(0); - r += op[1].CalcL(op[0].Out()); - r += op[3].CalcL(0); - op[0].CalcFBL(fb); - break; - case 7: - r = op[2].CalcL(0); - r += op[1].CalcL(0); - r += op[3].CalcL(0); - r += op[0].CalcFBL(fb); - break; - default: - assert(false); - r = 0; - break; - } - return r; -} - -// 合成 -ISample Channel4::CalcN(uint noise) -{ - buf[1] = buf[2] = buf[3] = 0; - - buf[0] = op[0].out_; op[0].CalcFB(fb); - *out[0] += op[1].Calc(*in[0]); - *out[1] += op[2].Calc(*in[1]); - int o = op[3].out_; - op[3].CalcN(noise); - return *out[2] + o; -} - -// 合成 -ISample Channel4::CalcLN(uint noise) -{ - chip_->SetPMV(pms[chip_->GetPML()]); - buf[1] = buf[2] = buf[3] = 0; - - buf[0] = op[0].out_; op[0].CalcFBL(fb); - *out[0] += op[1].CalcL(*in[0]); - *out[1] += op[2].CalcL(*in[1]); - int o = op[3].out_; - op[3].CalcN(noise); - return *out[2] + o; -} - -void Channel4::DataSave(struct Channel4Data* data) { - data->fb = fb; - memcpy(data->buf, buf, sizeof(int) * 4); - data->algo_ = algo_; - for(int i = 0; i < 4; i++) { - op[i].DataSave(&data->op[i]); - } -} - -void Channel4::DataLoad(struct Channel4Data* data) { - fb = data->fb; - memcpy(buf, data->buf, sizeof(int) * 4); - algo_ = data->algo_; - SetAlgorithm(algo_); - for(int i = 0; i < 4; i++) { - op[i].DataLoad(&data->op[i]); - } - pms = pmtable[op[0].type_][op[0].ms_ & 7]; -} -} // namespace FM +// --------------------------------------------------------------------------- +// FM Sound Generator - Core Unit +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: fmgen.cpp,v 1.49 2003/09/02 14:51:04 cisc Exp $ +// --------------------------------------------------------------------------- +// 参考: +// FM sound generator for M.A.M.E., written by Tatsuyuki Satoh. +// +// 謎: +// OPNB の CSM モード(仕様がよくわからない) +// +// 制限: +// ・AR!=31 で SSGEC を使うと波形が実際と異なる可能性あり +// +// 謝辞: +// Tatsuyuki Satoh さん(fm.c) +// Hiromitsu Shioya さん(ADPCM-A) +// DMP-SOFT. さん(OPNB) +// KAJA さん(test program) +// ほか掲示板等で様々なご助言,ご支援をお寄せいただいた皆様に +// --------------------------------------------------------------------------- + +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_fmgen.h" +#include "fmgen_fmgeninl.h" + +#define LOGNAME "fmgen" + +// --------------------------------------------------------------------------- + +#define FM_EG_BOTTOM 955 + +// --------------------------------------------------------------------------- +// Table/etc +// +namespace FM +{ + const uint8 Operator::notetable[128] = + { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, + }; + + const int8 Operator::dttable[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, + 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, 16, 16, 16, 16, + 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, + 10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32, + 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, + 16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4, + -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16, + -2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10, + -10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32, + -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14, + -16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44, + }; + + const int8 Operator::decaytable1[64][8] = + { + {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 0, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0}, + {1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 1, 1}, {2, 1, 1, 1, 2, 1, 1, 1}, + {2, 1, 2, 1, 2, 1, 2, 1}, {2, 2, 2, 1, 2, 2, 2, 1}, + {2, 2, 2, 2, 2, 2, 2, 2}, {4, 2, 2, 2, 4, 2, 2, 2}, + {4, 2, 4, 2, 4, 2, 4, 2}, {4, 4, 4, 2, 4, 4, 4, 2}, + {4, 4, 4, 4, 4, 4, 4, 4}, {8, 4, 4, 4, 8, 4, 4, 4}, + {8, 4, 8, 4, 8, 4, 8, 4}, {8, 8, 8, 4, 8, 8, 8, 4}, + {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, + {16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16}, + }; + + const int Operator::decaytable2[16] = + { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2047, 2047, 2047, 2047, 2047 + }; + + const int8 Operator::attacktable[64][8] = + { + {-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4,-1, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1}, + { 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1}, + { 4, 4, 4, 4, 4, 4, 4, 4}, { 3, 4, 4, 4, 3, 4, 4, 4}, + { 3, 4, 3, 4, 3, 4, 3, 4}, { 3, 3, 3, 4, 3, 3, 3, 4}, + { 3, 3, 3, 3, 3, 3, 3, 3}, { 2, 3, 3, 3, 2, 3, 3, 3}, + { 2, 3, 2, 3, 2, 3, 2, 3}, { 2, 2, 2, 3, 2, 2, 2, 3}, + { 2, 2, 2, 2, 2, 2, 2, 2}, { 1, 2, 2, 2, 1, 2, 2, 2}, + { 1, 2, 1, 2, 1, 2, 1, 2}, { 1, 1, 1, 2, 1, 1, 1, 2}, + { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0}, + }; + +#if 0 // libOPNMIDI: experimental SSG-EG + const int Operator::ssgenvtable[8][2][3][2] = + { + {{{1, 1}, {1, 1}, {1, 1}}, // 08 + {{0, 1}, {1, 1}, {1, 1}}}, // 08 56~ + {{{0, 1}, {2, 0}, {2, 0}}, // 09 + {{0, 1}, {2, 0}, {2, 0}}}, // 09 + {{{1,-1}, {0, 1}, {1,-1}}, // 10 + {{0, 1}, {1,-1}, {0, 1}}}, // 10 60~ + {{{1,-1}, {0, 0}, {0, 0}}, // 11 + {{0, 1}, {0, 0}, {0, 0}}}, // 11 60~ + {{{2,-1}, {2,-1}, {2,-1}}, // 12 + {{1,-1}, {2,-1}, {2,-1}}}, // 12 56~ + {{{1,-1}, {0, 0}, {0, 0}}, // 13 + {{1,-1}, {0, 0}, {0, 0}}}, // 13 + {{{0, 1}, {1,-1}, {0, 1}}, // 14 + {{1,-1}, {0, 1}, {1,-1}}}, // 14 60~ + {{{0, 1}, {2, 0}, {2, 0}}, // 15 + {{1,-1}, {2, 0}, {2, 0}}}, // 15 60~ + }; +#endif + + // fixed equasion-based tables + int pmtable[2][8][FM_LFOENTS]; + uint amtable[2][4][FM_LFOENTS]; + + static bool tablemade = false; +} + +namespace FM +{ + +// --------------------------------------------------------------------------- +// テーブル作成 +// +void MakeLFOTable() +{ + if (tablemade) + return; + + tablemade = true; + + int i; + + static const double pms[2][8] = + { + { 0, 1/360., 2/360., 3/360., 4/360., 6/360., 12/360., 24/360., }, // OPNA +// { 0, 1/240., 2/240., 4/240., 10/240., 20/240., 80/240., 140/240., }, // OPM + { 0, 1/480., 2/480., 4/480., 10/480., 20/480., 80/480., 140/480., }, // OPM +// { 0, 1/960., 2/960., 4/960., 10/960., 20/960., 80/960., 140/960., }, // OPM + }; + // 3 6, 12 30 60 240 420 / 720 + // 1.000963 + // lfofref[level * max * wave]; + // pre = lfofref[level][pms * wave >> 8]; + static const uint8 amt[2][4] = + { + { 31, 6, 4, 3 }, // OPNA + { 31, 2, 1, 0 }, // OPM + }; + + for (int type = 0; type < 2; type++) + { + for (i=0; i<8; i++) + { + double pmb = pms[type][i]; + for (int j=0; j> amt[type][i]) * 2) << 2; + } + } + } +} + + +// --------------------------------------------------------------------------- +// チップ内で共通な部分 +// +Chip::Chip() +: ratio_(0), aml_(0), pml_(0), pmv_(0) +//, optype_(typeN) +{ +} + +// クロック・サンプリングレート比に依存するテーブルを作成 +void Chip::SetRatio(uint ratio) +{ + if (ratio_ != ratio) + { + ratio_ = ratio; + MakeTable(); + } +} + +void Chip::MakeTable() +{ + int h, l; + + // PG Part + static const float dt2lv[4] = { 1.f, 1.414f, 1.581f, 1.732f }; + for (h=0; h<4; h++) + { + assert(2 + FM_RATIOBITS - FM_PGBITS >= 0); + double rr = dt2lv[h] * double(ratio_) / (1 << (2 + FM_RATIOBITS - FM_PGBITS)); + for (l=0; l<16; l++) + { + int mul = l ? l * 2 : 1; + multable_[h][l] = uint(mul * rr); + } + } +} + +void Chip::DataSave(struct ChipData* data) +{ + data->ratio_ = ratio_; + data->aml_ = aml_; + data->pml_ = pml_; + data->pmv_ = pmv_; + memcpy(data->multable_, multable_, sizeof(uint32) * 4 * 16); +} + +void Chip::DataLoad(struct ChipData* data) +{ + ratio_ = data->ratio_; + aml_ = data->aml_; + pml_ = data->pml_; + pmv_ = data->pmv_; + memcpy(multable_, data->multable_, sizeof(uint32) * 4 * 16); +} + +// --------------------------------------------------------------------------- +// Operator +// +bool FM::Operator::tablehasmade = false; +uint FM::Operator::sinetable[1024]; +int32 FM::Operator::cltable[FM_CLENTS]; + +// 構築 +FM::Operator::Operator() +: chip_(0) +{ + if (!tablehasmade) + MakeTable(); + + // EG Part + ar_ = dr_ = sr_ = rr_ = key_scale_rate_ = 0; + ams_ = amtable[0][0]; + mute_ = false; + keyon_ = false; + tl_out_ = false; + ssg_type_ = 0; +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = false; + held_ = false; +#endif + + // PG Part + multiple_ = 0; + detune_ = 0; + detune2_ = 0; + + // LFO + ms_ = 0; + +// Reset(); +} + +// 初期化 +void FM::Operator::Reset() +{ + // EG part + tl_ = tl_latch_ = 127; + ShiftPhase(off); + eg_count_ = 0; + eg_curve_count_ = 0; +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = false; + held_ = false; +#else + ssg_phase_ = 0; +#endif + + // PG part + pg_count_ = 0; + + // OP part + out_ = out2_ = 0; + + param_changed_ = true; + PARAMCHANGE(0); +} + +void Operator::MakeTable() +{ + // 対数テーブルの作成 + assert(FM_CLENTS >= 256); + + int* p = cltable; + int i; + for (i=0; i<256; i++) + { + int v = int(floor(pow(2., 13. - i / 256.))); + v = (v + 2) & ~3; + *p++ = v; + *p++ = -v; + } + while (p < cltable + FM_CLENTS) + { + //*p++ = p[-512] / 2; + *p = p[-512] / 2; + p++; + } + +// for (i=0; i<13*256; i++) +// printf("%4d, %d, %d\n", i, cltable[i*2], cltable[i*2+1]); + + // サインテーブルの作成 + double log2 = log(2.); + for (i=0; iGetMulValue(detune2_, multiple_); + pg_diff_lfo_ = pg_diff_ >> 11; + + // EG Part + key_scale_rate_ = bn_ >> (3-ks_); + tl_out_ = mute_ ? 0x3ff : tl_ * 8; + + switch (eg_phase_) + { + case attack: + SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); + break; + case decay: + SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); + eg_level_on_next_phase_ = sl_ * 8; + break; + case sustain: + SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); + break; + case release: + SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); + break; + default: + break; + } + + // SSG-EG + inverted_ = false; + held_ = false; + if (ssg_type_ && (eg_phase_ != release)) + { +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = (ssg_type_ & 4) != 0; + inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN +#else + int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); + + assert(0 <= ssg_phase_ && ssg_phase_ <= 2); + const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; + + ssg_offset_ = table[0] * 0x200; + ssg_vector_ = table[1]; +#endif + } + // LFO + ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; + EGUpdate(); + + dbgopout_ = 0; + } +} + +void Operator::DataSave(struct OperatorData* data) +{ + data->out_ = out_; + data->out2_ = out2_; + data->in2_ = in2_; + data->dp_ = dp_; + data->detune_ = detune_; + data->detune2_ = detune2_; + data->multiple_ = multiple_; + data->pg_count_ = pg_count_; + data->pg_diff_ = pg_diff_; + data->pg_diff_lfo_ = pg_diff_lfo_; + data->type_ = type_; + data->bn_ = bn_; + data->eg_level_ = eg_level_; + data->eg_level_on_next_phase_ = eg_level_on_next_phase_; + data->eg_count_ = eg_count_; + data->eg_count_diff_ = eg_count_diff_; + data->eg_out_ = eg_out_; + data->tl_out_ = tl_out_; + data->eg_rate_ = eg_rate_; + data->eg_curve_count_ = eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + data->ssg_offset_ = ssg_offset_; + data->ssg_vector_ = ssg_vector_; + data->ssg_phase_ = ssg_phase_; +#endif + data->key_scale_rate_ = key_scale_rate_; + data->eg_phase_ = eg_phase_; + data->ms_ = ms_; + data->tl_ = tl_; + data->tl_latch_ = tl_latch_; + data->ar_ = ar_; + data->dr_ = dr_; + data->sr_ = sr_; + data->sl_ = sl_; + data->rr_ = rr_; + data->ks_ = ks_; + data->ssg_type_ = ssg_type_; + data->keyon_ = keyon_; + data->amon_ = amon_; + data->param_changed_ = param_changed_; + data->mute_ = mute_; + data->inverted_ = inverted_; + data->held_ = held_; +} + +void Operator::DataLoad(struct OperatorData* data) +{ + out_ = data->out_; + out2_ = data->out2_; + in2_ = data->in2_; + dp_ = data->dp_; + detune_ = data->detune_; + detune2_ = data->detune2_; + multiple_ = data->multiple_; + pg_count_ = data->pg_count_; + pg_diff_ = data->pg_diff_; + pg_diff_lfo_ = data->pg_diff_lfo_; + type_ = data->type_; + bn_ = data->bn_; + eg_level_ = data->eg_level_; + eg_level_on_next_phase_ = data->eg_level_on_next_phase_; + eg_count_ = data->eg_count_; + eg_count_diff_ = data->eg_count_diff_; + eg_out_ = data->eg_out_; + tl_out_ = data->tl_out_; + eg_rate_ = data->eg_rate_; + eg_curve_count_ = data->eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + ssg_offset_ = data->ssg_offset_; + ssg_vector_ = data->ssg_vector_; + ssg_phase_ = data->ssg_phase_; +#endif + key_scale_rate_ = data->key_scale_rate_; + eg_phase_ = data->eg_phase_; + ms_ = data->ms_; + tl_ = data->tl_; + tl_latch_ = data->tl_latch_; + ar_ = data->ar_; + dr_ = data->dr_; + sr_ = data->sr_; + sl_ = data->sl_; + rr_ = data->rr_; + ks_ = data->ks_; + ssg_type_ = data->ssg_type_; + keyon_ = data->keyon_; + amon_ = data->amon_; + param_changed_ = data->param_changed_; + mute_ = data->mute_; + inverted_ = data->inverted_; + held_ = data->held_; + ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0]; +} + + +// envelop の eg_phase_ 変更 +void Operator::ShiftPhase(EGPhase nextphase) +{ + switch (nextphase) + { + case attack: // Attack Phase + tl_ = tl_latch_; + if (ssg_type_) + { +#if 0 // libOPNMIDI: experimental SSG-EG + ssg_phase_ = ssg_phase_ + 1; + if (ssg_phase_ > 2) + ssg_phase_ = 1; + + int m = static_cast(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u)); + + assert(0 <= ssg_phase_ && ssg_phase_ <= 2); + const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_]; + + ssg_offset_ = table[0] * 0x200; + ssg_vector_ = table[1]; +#endif + } + if ((ar_ + key_scale_rate_) < 62) + { + SetEGRate(static_cast(ar_ ? Min(63, ar_ + key_scale_rate_) : 0)); + eg_phase_ = attack; + break; + } + // fall through + case decay: // Decay Phase + if (sl_) + { + eg_level_ = 0; + eg_level_on_next_phase_ = ssg_type_ ? Min(sl_ * 8, 0x200) : sl_ * 8; + + SetEGRate(static_cast(dr_ ? Min(63, dr_ + key_scale_rate_) : 0)); + eg_phase_ = decay; + break; + } + // fall through + case sustain: // Sustain Phase + eg_level_ = sl_ * 8; + eg_level_on_next_phase_ = ssg_type_ ? 0x200 : 0x400; + + SetEGRate(static_cast(sr_ ? Min(63, sr_ + key_scale_rate_) : 0)); + eg_phase_ = sustain; + break; + + case release: // Release Phase + inverted_ = false; + held_ = false; +#if 0 // libOPNMIDI: experimental SSG-EG + if (ssg_type_) + { + eg_level_ = eg_level_ * ssg_vector_ + ssg_offset_; + ssg_vector_ = 1; + ssg_offset_ = 0; + } +#endif + if (eg_phase_ == attack || (eg_level_ < FM_EG_BOTTOM)) //0x400/* && eg_phase_ != off*/)) + { + eg_level_on_next_phase_ = 0x400; + SetEGRate(static_cast(Min(63, rr_ + key_scale_rate_))); + eg_phase_ = release; + break; + } + // fall through + case off: // off + default: + eg_level_ = FM_EG_BOTTOM; + eg_level_on_next_phase_ = FM_EG_BOTTOM; + EGUpdate(); + SetEGRate(0); + eg_phase_ = off; + break; + } +} + +// Block/F-Num +void Operator::SetFNum(uint f) +{ + dp_ = (f & 2047) << ((f >> 11) & 7); + bn_ = notetable[(f >> 7) & 127]; + param_changed_ = true; + PARAMCHANGE(2); +} + +// 1サンプル合成 + +// ISample を envelop count (2π) に変換するシフト量 +#define IS2EC_SHIFT ((20 + FM_PGBITS) - 13) + + +// 入力: s = 20+FM_PGBITS = 29 +#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)] +#define SINE(s) sinetable[(s) & (FM_OPSINENTS-1)] + +inline FM::ISample Operator::LogToLin(uint a) +{ +#if 1 // FM_CLENTS < 0xc00 // 400 for TL, 400 for ENV, 400 for LFO. + return (a < FM_CLENTS) ? cltable[a] : 0; +#else + return cltable[a]; +#endif +} + +inline void Operator::EGUpdate() +{ +#if 1 // libOPNMIDI: experimental SSG-EG + int level = eg_level_; + level = (!inverted_) ? level : (512 - level) & 0x3ff; + eg_out_ = Min(tl_out_ + level, 0x3ff) << (1 + 2); +#else + if (!ssg_type_) + { + eg_out_ = Min(tl_out_ + eg_level_, 0x3ff) << (1 + 2); + } + else + { + eg_out_ = Min(tl_out_ + eg_level_ * ssg_vector_ + ssg_offset_, 0x3ff) << (1 + 2); + } +#endif +} + +inline void Operator::SetEGRate(uint rate) +{ + eg_rate_ = rate; + eg_count_diff_ = decaytable2[rate / 4] * chip_->GetRatio(); +} + +// EG 計算 +void FM::Operator::EGCalc() +{ + eg_count_ = (2047 * 3) << FM_RATIOBITS; // ##この手抜きは再現性を低下させる + + if (eg_phase_ == attack) + { + int c = attacktable[eg_rate_][eg_curve_count_ & 7]; + if (c >= 0) + { + eg_level_ -= 1 + (eg_level_ >> c); + if (eg_level_ <= 0) + ShiftPhase(decay); + } + EGUpdate(); + } + else + { + if (!ssg_type_) + { + eg_level_ += decaytable1[eg_rate_][eg_curve_count_ & 7]; + if (eg_level_ >= eg_level_on_next_phase_) + ShiftPhase(EGPhase(eg_phase_+1)); + EGUpdate(); + } + else + { + if (!held_) + eg_level_ += 4 * decaytable1[eg_rate_][eg_curve_count_ & 7]; + else + eg_level_ = (((ssg_type_ & 4) != 0) ^ ((ssg_type_ & 2) != 0)) ? 0 : 1024;; + EGUpdate(); // libOPNMIDI: experimental SSG-EG + if (eg_level_ >= eg_level_on_next_phase_) + { + switch (eg_phase_) + { + case decay: + ShiftPhase(sustain); + break; + case sustain: +#if 1 // libOPNMIDI: experimental SSG-EG + if (ssg_type_ & 1) + { + inverted_ = false; + held_ = true; + } + if (!held_) + { + inverted_ ^= (ssg_type_ & 2) && (ar_ == 62); // try to match polarity with nuked OPN + ShiftPhase(attack); + } +#else + ShiftPhase(attack); +#endif + break; + case release: + ShiftPhase(off); + break; + default: + break; + } + } + } + } + eg_curve_count_++; +} + +inline void FM::Operator::EGStep() +{ + eg_count_ -= eg_count_diff_; + + // EG の変化は全スロットで同期しているという噂もある + if (eg_count_ <= 0) + EGCalc(); +} + +// PG 計算 +// ret:2^(20+PGBITS) / cycle +inline uint32 FM::Operator::PGCalc() +{ + uint32 ret = pg_count_; + pg_count_ += pg_diff_; + dbgpgout_ = ret; + return ret; +} + +inline uint32 FM::Operator::PGCalcL() +{ + uint32 ret = pg_count_; + pg_count_ += pg_diff_ + ((pg_diff_lfo_ * chip_->GetPMV()) >> 5);// & -(1 << (2+IS2EC_SHIFT))); + dbgpgout_ = ret; + return ret /* + pmv * pg_diff_;*/; +} + +// OP 計算 +// in: ISample (最大 8π) +inline FM::ISample FM::Operator::Calc(ISample in) +{ + EGStep(); + out2_ = out_; + + int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); + pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); + out_ = LogToLin(eg_out_ + SINE(pgin)); + + dbgopout_ = out_; + return out_; +} + +inline FM::ISample FM::Operator::CalcL(ISample in) +{ + EGStep(); + + int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); + pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT)); + out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); + + dbgopout_ = out_; + return out_; +} + +inline FM::ISample FM::Operator::CalcN(uint noise) +{ + EGStep(); + + int lv = Max(0, 0x3ff - (tl_out_ + eg_level_)) << 1; + + // noise & 1 ? lv : -lv と等価 + noise = (noise & 1) - 1; + out_ = (lv + noise) ^ noise; + + dbgopout_ = out_; + return out_; +} + +// OP (FB) 計算 +// Self Feedback の変調最大 = 4π +inline FM::ISample FM::Operator::CalcFB(uint fb) +{ + EGStep(); + + ISample in = out_ + out2_; + out2_ = out_; + + int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS); + if (fb < 31) + { + pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); + } + out_ = LogToLin(eg_out_ + SINE(pgin)); + dbgopout_ = out2_; + + return out2_; +} + +inline FM::ISample FM::Operator::CalcFBL(uint fb) +{ + EGStep(); + + ISample in = out_ + out2_; + out2_ = out_; + + int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS); + if (fb < 31) + { + pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS); + } + + out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]); + dbgopout_ = out_; + + return out_; +} + +#undef Sine + +// --------------------------------------------------------------------------- +// 4-op Channel +// +const uint8 Channel4::fbtable[8] = { 31, 7, 6, 5, 4, 3, 2, 1 }; +int Channel4::kftable[64]; + +bool Channel4::tablehasmade = false; + + +Channel4::Channel4() +{ + if (!tablehasmade) + MakeTable(); + + SetAlgorithm(0); + pms = pmtable[0][0]; +} + +void Channel4::MakeTable() +{ + // 100/64 cent = 2^(i*100/64*1200) + for (int i=0; i<64; i++) + { + kftable[i] = int(0x10000 * pow(2., i / 768.) ); + } +} + +// リセット +void Channel4::Reset() +{ + op[0].Reset(); + op[1].Reset(); + op[2].Reset(); + op[3].Reset(); +} + +// Calc の用意 +int Channel4::Prepare() +{ + op[0].Prepare(); + op[1].Prepare(); + op[2].Prepare(); + op[3].Prepare(); + + pms = pmtable[op[0].type_][op[0].ms_ & 7]; + int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0; + int lfo = op[0].ms_ & ((op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_) ? 0x37 : 7) ? 2 : 0; + return key | lfo; +} + +// F-Number/BLOCK を設定 +void Channel4::SetFNum(uint f) +{ + for (int i=0; i<4; i++) + op[i].SetFNum(f); +} + +// KC/KF を設定 +void Channel4::SetKCKF(uint kc, uint kf) +{ + const static uint kctable[16] = + { + 5197, 5506, 5833, 6180, 6180, 6547, 6937, 7349, + 7349, 7786, 8249, 8740, 8740, 9259, 9810, 10394, + }; + + int oct = 19 - ((kc >> 4) & 7); + +//printf("%p", this); + uint kcv = kctable[kc & 0x0f]; + kcv = (kcv + 2) / 4 * 4; +//printf(" %.4x", kcv); + uint dp = kcv * kftable[kf & 0x3f]; +//printf(" %.4x %.4x %.8x", kcv, kftable[kf & 0x3f], dp >> oct); + dp >>= 16 + 3; + dp <<= 16 + 3; + dp >>= oct; + uint bn = (kc >> 2) & 31; + op[0].SetDPBN(dp, bn); + op[1].SetDPBN(dp, bn); + op[2].SetDPBN(dp, bn); + op[3].SetDPBN(dp, bn); +//printf(" %.8x\n", dp); +} + +// キー制御 +void Channel4::KeyControl(uint key) +{ + if (key & 0x1) op[0].KeyOn(); else op[0].KeyOff(); + if (key & 0x2) op[1].KeyOn(); else op[1].KeyOff(); + if (key & 0x4) op[2].KeyOn(); else op[2].KeyOff(); + if (key & 0x8) op[3].KeyOn(); else op[3].KeyOff(); +} + +// アルゴリズムを設定 +void Channel4::SetAlgorithm(uint algo) +{ + static const uint8 table1[8][6] = + { + { 0, 1, 1, 2, 2, 3 }, { 1, 0, 0, 1, 1, 2 }, + { 1, 1, 1, 0, 0, 2 }, { 0, 1, 2, 1, 1, 2 }, + { 0, 1, 2, 2, 2, 1 }, { 0, 1, 0, 1, 0, 1 }, + { 0, 1, 2, 1, 2, 1 }, { 1, 0, 1, 0, 1, 0 }, + }; + + in [0] = &buf[table1[algo][0]]; + out[0] = &buf[table1[algo][1]]; + in [1] = &buf[table1[algo][2]]; + out[1] = &buf[table1[algo][3]]; + in [2] = &buf[table1[algo][4]]; + out[2] = &buf[table1[algo][5]]; + + op[0].ResetFB(); + algo_ = algo; +} + +// 合成 +ISample Channel4::Calc() +{ + int r; + switch (algo_) + { + case 0: + op[2].Calc(op[1].Out()); + op[1].Calc(op[0].Out()); + r = op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 1: + op[2].Calc(op[0].Out() + op[1].Out()); + op[1].Calc(0); + r = op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 2: + op[2].Calc(op[1].Out()); + op[1].Calc(0); + r = op[3].Calc(op[0].Out() + op[2].Out()); + op[0].CalcFB(fb); + break; + case 3: + op[2].Calc(0); + op[1].Calc(op[0].Out()); + r = op[3].Calc(op[1].Out() + op[2].Out()); + op[0].CalcFB(fb); + break; + case 4: + op[2].Calc(0); + r = op[1].Calc(op[0].Out()); + r += op[3].Calc(op[2].Out()); + op[0].CalcFB(fb); + break; + case 5: + r = op[2].Calc(op[0].Out()); + r += op[1].Calc(op[0].Out()); + r += op[3].Calc(op[0].Out()); + op[0].CalcFB(fb); + break; + case 6: + r = op[2].Calc(0); + r += op[1].Calc(op[0].Out()); + r += op[3].Calc(0); + op[0].CalcFB(fb); + break; + case 7: + r = op[2].Calc(0); + r += op[1].Calc(0); + r += op[3].Calc(0); + r += op[0].CalcFB(fb); + break; + default: + assert(false); + r = 0; + break; + } + return r; +} + +// 合成 +ISample Channel4::CalcL() +{ + chip_->SetPMV(pms[chip_->GetPML()]); + + int r; + switch (algo_) + { + case 0: + op[2].CalcL(op[1].Out()); + op[1].CalcL(op[0].Out()); + r = op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 1: + op[2].CalcL(op[0].Out() + op[1].Out()); + op[1].CalcL(0); + r = op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 2: + op[2].CalcL(op[1].Out()); + op[1].CalcL(0); + r = op[3].CalcL(op[0].Out() + op[2].Out()); + op[0].CalcFBL(fb); + break; + case 3: + op[2].CalcL(0); + op[1].CalcL(op[0].Out()); + r = op[3].CalcL(op[1].Out() + op[2].Out()); + op[0].CalcFBL(fb); + break; + case 4: + op[2].CalcL(0); + r = op[1].CalcL(op[0].Out()); + r += op[3].CalcL(op[2].Out()); + op[0].CalcFBL(fb); + break; + case 5: + r = op[2].CalcL(op[0].Out()); + r += op[1].CalcL(op[0].Out()); + r += op[3].CalcL(op[0].Out()); + op[0].CalcFBL(fb); + break; + case 6: + r = op[2].CalcL(0); + r += op[1].CalcL(op[0].Out()); + r += op[3].CalcL(0); + op[0].CalcFBL(fb); + break; + case 7: + r = op[2].CalcL(0); + r += op[1].CalcL(0); + r += op[3].CalcL(0); + r += op[0].CalcFBL(fb); + break; + default: + assert(false); + r = 0; + break; + } + return r; +} + +// 合成 +ISample Channel4::CalcN(uint noise) +{ + buf[1] = buf[2] = buf[3] = 0; + + buf[0] = op[0].out_; op[0].CalcFB(fb); + *out[0] += op[1].Calc(*in[0]); + *out[1] += op[2].Calc(*in[1]); + int o = op[3].out_; + op[3].CalcN(noise); + return *out[2] + o; +} + +// 合成 +ISample Channel4::CalcLN(uint noise) +{ + chip_->SetPMV(pms[chip_->GetPML()]); + buf[1] = buf[2] = buf[3] = 0; + + buf[0] = op[0].out_; op[0].CalcFBL(fb); + *out[0] += op[1].CalcL(*in[0]); + *out[1] += op[2].CalcL(*in[1]); + int o = op[3].out_; + op[3].CalcN(noise); + return *out[2] + o; +} + +void Channel4::DataSave(struct Channel4Data* data) { + data->fb = fb; + memcpy(data->buf, buf, sizeof(int) * 4); + data->algo_ = algo_; + for(int i = 0; i < 4; i++) { + op[i].DataSave(&data->op[i]); + } +} + +void Channel4::DataLoad(struct Channel4Data* data) { + fb = data->fb; + memcpy(buf, data->buf, sizeof(int) * 4); + algo_ = data->algo_; + SetAlgorithm(algo_); + for(int i = 0; i < 4; i++) { + op[i].DataLoad(&data->op[i]); + } + pms = pmtable[op[0].type_][op[0].ms_ & 7]; +} +} // namespace FM diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h index 398173cbb..2a8972989 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgen.h @@ -1,342 +1,342 @@ -// --------------------------------------------------------------------------- -// FM Sound Generator -// Copyright (C) cisc 1998, 2001. -// --------------------------------------------------------------------------- -// $Id: fmgen.h,v 1.37 2003/08/25 13:33:11 cisc Exp $ - -#ifndef FM_GEN_H -#define FM_GEN_H - -#include "fmgen_types.h" - -// --------------------------------------------------------------------------- -// 出力サンプルの型 -// -// libOPNMIDI: change int32 to int16 -#define FM_SAMPLETYPE int16 // int16 or int32 - -// --------------------------------------------------------------------------- -// 定数その1 -// 静的テーブルのサイズ - -#define FM_LFOBITS 8 // 変更不可 -#define FM_TLBITS 7 - -// --------------------------------------------------------------------------- - -#define FM_TLENTS (1 << FM_TLBITS) -#define FM_LFOENTS (1 << FM_LFOBITS) -#define FM_TLPOS (FM_TLENTS/4) - -// サイン波の精度は 2^(1/256) -#define FM_CLENTS (0x1000 * 2) // sin + TL + LFO - -// --------------------------------------------------------------------------- - -namespace FM -{ - // Types ---------------------------------------------------------------- - typedef FM_SAMPLETYPE Sample; - typedef int32 ISample; - - enum OpType { typeN=0, typeM=1 }; - enum EGPhase { next, attack, decay, sustain, release, off }; - - void StoreSample(ISample& dest, int data); - - class Chip; - struct ChipData; - - // Operator ------------------------------------------------------------- - struct OperatorData { - ISample out_; - ISample out2_; - ISample in2_; - uint dp_; - uint detune_; - uint detune2_; - uint multiple_; - uint32 pg_count_; - uint32 pg_diff_; - int32 pg_diff_lfo_; - OpType type_; - uint bn_; - int eg_level_; - int eg_level_on_next_phase_; - int eg_count_; - int eg_count_diff_; - int eg_out_; - int tl_out_; - int eg_rate_; - int eg_curve_count_; -#if 0 // libOPNMIDI: experimental SSG-EG - int ssg_offset_; - int ssg_vector_; - int ssg_phase_; -#endif - uint key_scale_rate_; - EGPhase eg_phase_; - uint ms_; - - uint tl_; - uint tl_latch_; - uint ar_; - uint dr_; - uint sr_; - uint sl_; - uint rr_; - uint ks_; - uint ssg_type_; - - bool keyon_; - bool amon_; - bool param_changed_; - bool mute_; - bool inverted_; - bool held_; - }; - - class Operator - { - public: - Operator(); - void SetChip(Chip* chip) { chip_ = chip; } - - static void MakeTimeTable(uint ratio); - - ISample Calc(ISample in); - ISample CalcL(ISample in); - ISample CalcFB(uint fb); - ISample CalcFBL(uint fb); - ISample CalcN(uint noise); - void Prepare(); - void KeyOn(); - void KeyOff(); - void Reset(); - void ResetFB(); - int IsOn(); - - void SetDT(uint dt); - void SetDT2(uint dt2); - void SetMULTI(uint multi); - void SetTL(uint tl, bool csm); - void SetKS(uint ks); - void SetAR(uint ar); - void SetDR(uint dr); - void SetSR(uint sr); - void SetRR(uint rr); - void SetSL(uint sl); - void SetSSGEC(uint ssgec); - void SetFNum(uint fnum); - void SetDPBN(uint dp, uint bn); - void SetMode(bool modulator); - void SetAMON(bool on); - void SetMS(uint ms); - void Mute(bool); - -// static void SetAML(uint l); -// static void SetPML(uint l); - - int Out() { return out_; } - - int dbgGetIn2() { return in2_; } - void dbgStopPG() { pg_diff_ = 0; pg_diff_lfo_ = 0; } - - void DataSave(struct OperatorData* data); - void DataLoad(struct OperatorData* data); - - private: - typedef uint32 Counter; - - Chip* chip_; - ISample out_, out2_; - ISample in2_; - - // Phase Generator ------------------------------------------------------ - uint32 PGCalc(); - uint32 PGCalcL(); - - uint dp_; // ΔP - uint detune_; // Detune - uint detune2_; // DT2 - uint multiple_; // Multiple - uint32 pg_count_; // Phase 現在値 - uint32 pg_diff_; // Phase 差分値 - int32 pg_diff_lfo_; // Phase 差分値 >> x - - // Envelop Generator --------------------------------------------------- - void EGCalc(); - void EGStep(); - void ShiftPhase(EGPhase nextphase); - void SSGShiftPhase(int mode); - void SetEGRate(uint); - void EGUpdate(); - int FBCalc(int fb); - ISample LogToLin(uint a); - - - OpType type_; // OP の種類 (M, N...) - uint bn_; // Block/Note - int eg_level_; // EG の出力値 - int eg_level_on_next_phase_; // 次の eg_phase_ に移る値 - int eg_count_; // EG の次の変移までの時間 - int eg_count_diff_; // eg_count_ の差分 - int eg_out_; // EG+TL を合わせた出力値 - int tl_out_; // TL 分の出力値 -// int pm_depth_; // PM depth -// int am_depth_; // AM depth - int eg_rate_; - int eg_curve_count_; -#if 0 // libOPNMIDI: experimental SSG-EG - int ssg_offset_; - int ssg_vector_; - int ssg_phase_; -#endif - - uint key_scale_rate_; // key scale rate - EGPhase eg_phase_; - uint* ams_; - uint ms_; - - uint tl_; // Total Level (0-127) - uint tl_latch_; // Total Level Latch (for CSM mode) - uint ar_; // Attack Rate (0-63) - uint dr_; // Decay Rate (0-63) - uint sr_; // Sustain Rate (0-63) - uint sl_; // Sustain Level (0-127) - uint rr_; // Release Rate (0-63) - uint ks_; // Keyscale (0-3) - uint ssg_type_; // SSG-Type Envelop Control - - bool keyon_; - bool amon_; // enable Amplitude Modulation - bool param_changed_; // パラメータが更新された - bool mute_; - bool inverted_; - bool held_; - - // Tables --------------------------------------------------------------- - static Counter rate_table[16]; - static uint32 multable[4][16]; - - static const uint8 notetable[128]; - static const int8 dttable[256]; - static const int8 decaytable1[64][8]; - static const int decaytable2[16]; - static const int8 attacktable[64][8]; - static const int ssgenvtable[8][2][3][2]; - - static uint sinetable[1024]; - static int32 cltable[FM_CLENTS]; - - static bool tablehasmade; - static void MakeTable(); - - - - // friends -------------------------------------------------------------- - friend class Channel4; - - public: - int dbgopout_; - int dbgpgout_; - static const int32* dbgGetClTable() { return cltable; } - static const uint* dbgGetSineTable() { return sinetable; } - }; - - // 4-op Channel --------------------------------------------------------- - struct Channel4Data { - uint fb; - int buf[4]; - int algo_; - struct OperatorData op[4]; - }; - - class Channel4 - { - public: - Channel4(); - void SetChip(Chip* chip); - void SetType(OpType type); - - ISample Calc(); - ISample CalcL(); - ISample CalcN(uint noise); - ISample CalcLN(uint noise); - void SetFNum(uint fnum); - void SetFB(uint fb); - void SetKCKF(uint kc, uint kf); - void SetAlgorithm(uint algo); - int Prepare(); - void KeyControl(uint key); - void Reset(); - void SetMS(uint ms); - void Mute(bool); - void Refresh(); - - void dbgStopPG() { for (int i=0; i<4; i++) op[i].dbgStopPG(); } - - void DataSave(struct Channel4Data* data); - void DataLoad(struct Channel4Data* data); - - private: - static const uint8 fbtable[8]; - uint fb; - int buf[4]; - int* in[3]; // 各 OP の入力ポインタ - int* out[3]; // 各 OP の出力ポインタ - int* pms; - int algo_; - Chip* chip_; - - static void MakeTable(); - - static bool tablehasmade; - static int kftable[64]; - - - public: - Operator op[4]; - }; - - // Chip resource - struct ChipData { - uint ratio_; - uint aml_; - uint pml_; - int pmv_; - OpType optype_; - uint32 multable_[4][16]; - }; - - class Chip - { - public: - Chip(); - void SetRatio(uint ratio); - void SetAML(uint l); - void SetPML(uint l); - void SetPMV(int pmv) { pmv_ = pmv; } - - uint32 GetMulValue(uint dt2, uint mul) { return multable_[dt2][mul]; } - uint GetAML() { return aml_; } - uint GetPML() { return pml_; } - int GetPMV() { return pmv_; } - uint GetRatio() { return ratio_; } - - void DataSave(struct ChipData* data); - void DataLoad(struct ChipData* data); - - private: - void MakeTable(); - - uint ratio_; - uint aml_; - uint pml_; - int pmv_; -// OpType optype_; - uint32 multable_[4][16]; - }; -} - -#endif // FM_GEN_H +// --------------------------------------------------------------------------- +// FM Sound Generator +// Copyright (C) cisc 1998, 2001. +// --------------------------------------------------------------------------- +// $Id: fmgen.h,v 1.37 2003/08/25 13:33:11 cisc Exp $ + +#ifndef FM_GEN_H +#define FM_GEN_H + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- +// 出力サンプルの型 +// +// libOPNMIDI: change int32 to int16 +#define FM_SAMPLETYPE int16 // int16 or int32 + +// --------------------------------------------------------------------------- +// 定数その1 +// 静的テーブルのサイズ + +#define FM_LFOBITS 8 // 変更不可 +#define FM_TLBITS 7 + +// --------------------------------------------------------------------------- + +#define FM_TLENTS (1 << FM_TLBITS) +#define FM_LFOENTS (1 << FM_LFOBITS) +#define FM_TLPOS (FM_TLENTS/4) + +// サイン波の精度は 2^(1/256) +#define FM_CLENTS (0x1000 * 2) // sin + TL + LFO + +// --------------------------------------------------------------------------- + +namespace FM +{ + // Types ---------------------------------------------------------------- + typedef FM_SAMPLETYPE Sample; + typedef int32 ISample; + + enum OpType { typeN=0, typeM=1 }; + enum EGPhase { next, attack, decay, sustain, release, off }; + + void StoreSample(ISample& dest, int data); + + class Chip; + struct ChipData; + + // Operator ------------------------------------------------------------- + struct OperatorData { + ISample out_; + ISample out2_; + ISample in2_; + uint dp_; + uint detune_; + uint detune2_; + uint multiple_; + uint32 pg_count_; + uint32 pg_diff_; + int32 pg_diff_lfo_; + OpType type_; + uint bn_; + int eg_level_; + int eg_level_on_next_phase_; + int eg_count_; + int eg_count_diff_; + int eg_out_; + int tl_out_; + int eg_rate_; + int eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + int ssg_offset_; + int ssg_vector_; + int ssg_phase_; +#endif + uint key_scale_rate_; + EGPhase eg_phase_; + uint ms_; + + uint tl_; + uint tl_latch_; + uint ar_; + uint dr_; + uint sr_; + uint sl_; + uint rr_; + uint ks_; + uint ssg_type_; + + bool keyon_; + bool amon_; + bool param_changed_; + bool mute_; + bool inverted_; + bool held_; + }; + + class Operator + { + public: + Operator(); + void SetChip(Chip* chip) { chip_ = chip; } + + static void MakeTimeTable(uint ratio); + + ISample Calc(ISample in); + ISample CalcL(ISample in); + ISample CalcFB(uint fb); + ISample CalcFBL(uint fb); + ISample CalcN(uint noise); + void Prepare(); + void KeyOn(); + void KeyOff(); + void Reset(); + void ResetFB(); + int IsOn(); + + void SetDT(uint dt); + void SetDT2(uint dt2); + void SetMULTI(uint multi); + void SetTL(uint tl, bool csm); + void SetKS(uint ks); + void SetAR(uint ar); + void SetDR(uint dr); + void SetSR(uint sr); + void SetRR(uint rr); + void SetSL(uint sl); + void SetSSGEC(uint ssgec); + void SetFNum(uint fnum); + void SetDPBN(uint dp, uint bn); + void SetMode(bool modulator); + void SetAMON(bool on); + void SetMS(uint ms); + void Mute(bool); + +// static void SetAML(uint l); +// static void SetPML(uint l); + + int Out() { return out_; } + + int dbgGetIn2() { return in2_; } + void dbgStopPG() { pg_diff_ = 0; pg_diff_lfo_ = 0; } + + void DataSave(struct OperatorData* data); + void DataLoad(struct OperatorData* data); + + private: + typedef uint32 Counter; + + Chip* chip_; + ISample out_, out2_; + ISample in2_; + + // Phase Generator ------------------------------------------------------ + uint32 PGCalc(); + uint32 PGCalcL(); + + uint dp_; // ΔP + uint detune_; // Detune + uint detune2_; // DT2 + uint multiple_; // Multiple + uint32 pg_count_; // Phase 現在値 + uint32 pg_diff_; // Phase 差分値 + int32 pg_diff_lfo_; // Phase 差分値 >> x + + // Envelop Generator --------------------------------------------------- + void EGCalc(); + void EGStep(); + void ShiftPhase(EGPhase nextphase); + void SSGShiftPhase(int mode); + void SetEGRate(uint); + void EGUpdate(); + int FBCalc(int fb); + ISample LogToLin(uint a); + + + OpType type_; // OP の種類 (M, N...) + uint bn_; // Block/Note + int eg_level_; // EG の出力値 + int eg_level_on_next_phase_; // 次の eg_phase_ に移る値 + int eg_count_; // EG の次の変移までの時間 + int eg_count_diff_; // eg_count_ の差分 + int eg_out_; // EG+TL を合わせた出力値 + int tl_out_; // TL 分の出力値 +// int pm_depth_; // PM depth +// int am_depth_; // AM depth + int eg_rate_; + int eg_curve_count_; +#if 0 // libOPNMIDI: experimental SSG-EG + int ssg_offset_; + int ssg_vector_; + int ssg_phase_; +#endif + + uint key_scale_rate_; // key scale rate + EGPhase eg_phase_; + uint* ams_; + uint ms_; + + uint tl_; // Total Level (0-127) + uint tl_latch_; // Total Level Latch (for CSM mode) + uint ar_; // Attack Rate (0-63) + uint dr_; // Decay Rate (0-63) + uint sr_; // Sustain Rate (0-63) + uint sl_; // Sustain Level (0-127) + uint rr_; // Release Rate (0-63) + uint ks_; // Keyscale (0-3) + uint ssg_type_; // SSG-Type Envelop Control + + bool keyon_; + bool amon_; // enable Amplitude Modulation + bool param_changed_; // パラメータが更新された + bool mute_; + bool inverted_; + bool held_; + + // Tables --------------------------------------------------------------- + static Counter rate_table[16]; + static uint32 multable[4][16]; + + static const uint8 notetable[128]; + static const int8 dttable[256]; + static const int8 decaytable1[64][8]; + static const int decaytable2[16]; + static const int8 attacktable[64][8]; + static const int ssgenvtable[8][2][3][2]; + + static uint sinetable[1024]; + static int32 cltable[FM_CLENTS]; + + static bool tablehasmade; + static void MakeTable(); + + + + // friends -------------------------------------------------------------- + friend class Channel4; + + public: + int dbgopout_; + int dbgpgout_; + static const int32* dbgGetClTable() { return cltable; } + static const uint* dbgGetSineTable() { return sinetable; } + }; + + // 4-op Channel --------------------------------------------------------- + struct Channel4Data { + uint fb; + int buf[4]; + int algo_; + struct OperatorData op[4]; + }; + + class Channel4 + { + public: + Channel4(); + void SetChip(Chip* chip); + void SetType(OpType type); + + ISample Calc(); + ISample CalcL(); + ISample CalcN(uint noise); + ISample CalcLN(uint noise); + void SetFNum(uint fnum); + void SetFB(uint fb); + void SetKCKF(uint kc, uint kf); + void SetAlgorithm(uint algo); + int Prepare(); + void KeyControl(uint key); + void Reset(); + void SetMS(uint ms); + void Mute(bool); + void Refresh(); + + void dbgStopPG() { for (int i=0; i<4; i++) op[i].dbgStopPG(); } + + void DataSave(struct Channel4Data* data); + void DataLoad(struct Channel4Data* data); + + private: + static const uint8 fbtable[8]; + uint fb; + int buf[4]; + int* in[3]; // 各 OP の入力ポインタ + int* out[3]; // 各 OP の出力ポインタ + int* pms; + int algo_; + Chip* chip_; + + static void MakeTable(); + + static bool tablehasmade; + static int kftable[64]; + + + public: + Operator op[4]; + }; + + // Chip resource + struct ChipData { + uint ratio_; + uint aml_; + uint pml_; + int pmv_; + OpType optype_; + uint32 multable_[4][16]; + }; + + class Chip + { + public: + Chip(); + void SetRatio(uint ratio); + void SetAML(uint l); + void SetPML(uint l); + void SetPMV(int pmv) { pmv_ = pmv; } + + uint32 GetMulValue(uint dt2, uint mul) { return multable_[dt2][mul]; } + uint GetAML() { return aml_; } + uint GetPML() { return pml_; } + int GetPMV() { return pmv_; } + uint GetRatio() { return ratio_; } + + void DataSave(struct ChipData* data); + void DataLoad(struct ChipData* data); + + private: + void MakeTable(); + + uint ratio_; + uint aml_; + uint pml_; + int pmv_; +// OpType optype_; + uint32 multable_[4][16]; + }; +} + +#endif // FM_GEN_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h index 35ca7b2f1..229c4d93f 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmgeninl.h @@ -1,274 +1,274 @@ -// --------------------------------------------------------------------------- -// FM Sound Generator -// Copyright (C) cisc 1998, 2003. -// --------------------------------------------------------------------------- -// $Id: fmgeninl.h,v 1.26 2003/06/12 13:14:36 cisc Exp $ - -#ifndef FM_GEN_INL_H -#define FM_GEN_INL_H - -// --------------------------------------------------------------------------- -// 定数その2 -// -#define FM_PI 3.14159265358979323846 - -#define FM_SINEPRESIS 2 // EGとサイン波の精度の差 0(低)-2(高) - - -#define FM_OPSINBITS 10 -#define FM_OPSINENTS (1 << FM_OPSINBITS) - -#define FM_EGCBITS 18 // eg の count のシフト値 -#define FM_LFOCBITS 14 - -#ifdef FM_TUNEBUILD - #define FM_PGBITS 2 - #define FM_RATIOBITS 0 -#else - #define FM_PGBITS 9 - #define FM_RATIOBITS 7 // 8-12 くらいまで? -#endif - -#define FM_EGBITS 16 - -//extern int paramcount[]; -//#define PARAMCHANGE(i) paramcount[i]++; -#define PARAMCHANGE(i) - -namespace FM -{ - -// --------------------------------------------------------------------------- -// Operator -// -// フィードバックバッファをクリア -inline void Operator::ResetFB() -{ - out_ = out2_ = 0; -} - -// キーオン -inline void Operator::KeyOn() -{ - if (!keyon_) - { - keyon_ = true; - held_ = false; - if (eg_phase_ == off || eg_phase_ == release) - { -#if 1 // libOPNMIDI: experimental SSG-EG - inverted_ = (ssg_type_ & 4) != 0; - inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN -#else - ssg_phase_ = -1; -#endif - ShiftPhase(attack); - EGUpdate(); - in2_ = out_ = out2_ = 0; - pg_count_ = 0; - } - } -} - -// キーオフ -inline void Operator::KeyOff() -{ - if (keyon_) - { - keyon_ = false; - ShiftPhase(release); - } -} - -// オペレータは稼働中か? -inline int Operator::IsOn() -{ - return eg_phase_ - off; -} - -// Detune (0-7) -inline void Operator::SetDT(uint dt) -{ - detune_ = dt * 0x20, param_changed_ = true; - PARAMCHANGE(4); -} - -// DT2 (0-3) -inline void Operator::SetDT2(uint dt2) -{ - detune2_ = dt2 & 3, param_changed_ = true; - PARAMCHANGE(5); -} - -// Multiple (0-15) -inline void Operator::SetMULTI(uint mul) -{ - multiple_ = mul, param_changed_ = true; - PARAMCHANGE(6); -} - -// Total Level (0-127) (0.75dB step) -inline void Operator::SetTL(uint tl, bool csm) -{ - if (!csm) - { - tl_ = tl, param_changed_ = true; - PARAMCHANGE(7); - } - tl_latch_ = tl; -} - -// Attack Rate (0-63) -inline void Operator::SetAR(uint ar) -{ - ar_ = ar; - param_changed_ = true; - PARAMCHANGE(8); -} - -// Decay Rate (0-63) -inline void Operator::SetDR(uint dr) -{ - dr_ = dr; - param_changed_ = true; - PARAMCHANGE(9); -} - -// Sustain Rate (0-63) -inline void Operator::SetSR(uint sr) -{ - sr_ = sr; - param_changed_ = true; - PARAMCHANGE(10); -} - -// Sustain Level (0-127) -inline void Operator::SetSL(uint sl) -{ - sl_ = sl; - param_changed_ = true; - PARAMCHANGE(11); -} - -// Release Rate (0-63) -inline void Operator::SetRR(uint rr) -{ - rr_ = rr; - param_changed_ = true; - PARAMCHANGE(12); -} - -// Keyscale (0-3) -inline void Operator::SetKS(uint ks) -{ - ks_ = ks; - param_changed_ = true; - PARAMCHANGE(13); -} - -// SSG-type Envelop (0-15) -inline void Operator::SetSSGEC(uint ssgec) -{ - if (ssgec & 8) - ssg_type_ = ssgec; - else - ssg_type_ = 0; -} - -inline void Operator::SetAMON(bool amon) -{ - amon_ = amon; - param_changed_ = true; - PARAMCHANGE(14); -} - -inline void Operator::Mute(bool mute) -{ - mute_ = mute; - param_changed_ = true; - PARAMCHANGE(15); -} - -inline void Operator::SetMS(uint ms) -{ - ms_ = ms; - param_changed_ = true; - PARAMCHANGE(16); -} - -// --------------------------------------------------------------------------- -// 4-op Channel - -// オペレータの種類 (LFO) を設定 -inline void Channel4::SetType(OpType type) -{ - for (int i=0; i<4; i++) - op[i].type_ = type; -} - -// セルフ・フィードバックレートの設定 (0-7) -inline void Channel4::SetFB(uint feedback) -{ - fb = fbtable[feedback]; -} - -// OPNA 系 LFO の設定 -inline void Channel4::SetMS(uint ms) -{ - op[0].SetMS(ms); - op[1].SetMS(ms); - op[2].SetMS(ms); - op[3].SetMS(ms); -} - -// チャンネル・マスク -inline void Channel4::Mute(bool m) -{ - for (int i=0; i<4; i++) - op[i].Mute(m); -} - -// 内部パラメータを再計算 -inline void Channel4::Refresh() -{ - for (int i=0; i<4; i++) - op[i].param_changed_ = true; - PARAMCHANGE(3); -} - -inline void Channel4::SetChip(Chip* chip) -{ - chip_ = chip; - for (int i=0; i<4; i++) - op[i].SetChip(chip); -} - -// --------------------------------------------------------------------------- -// -// -inline void StoreSample(Sample& dest, ISample data) -{ - if (sizeof(Sample) == 2) - dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); - else - dest += data; -} - - -// --------------------------------------------------------------------------- -// AM のレベルを設定 -inline void Chip::SetAML(uint l) -{ - aml_ = l & (FM_LFOENTS - 1); -} - -// PM のレベルを設定 -inline void Chip::SetPML(uint l) -{ - pml_ = l & (FM_LFOENTS - 1); -} - - -} - -#endif // FM_GEN_INL_H +// --------------------------------------------------------------------------- +// FM Sound Generator +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: fmgeninl.h,v 1.26 2003/06/12 13:14:36 cisc Exp $ + +#ifndef FM_GEN_INL_H +#define FM_GEN_INL_H + +// --------------------------------------------------------------------------- +// 定数その2 +// +#define FM_PI 3.14159265358979323846 + +#define FM_SINEPRESIS 2 // EGとサイン波の精度の差 0(低)-2(高) + + +#define FM_OPSINBITS 10 +#define FM_OPSINENTS (1 << FM_OPSINBITS) + +#define FM_EGCBITS 18 // eg の count のシフト値 +#define FM_LFOCBITS 14 + +#ifdef FM_TUNEBUILD + #define FM_PGBITS 2 + #define FM_RATIOBITS 0 +#else + #define FM_PGBITS 9 + #define FM_RATIOBITS 7 // 8-12 くらいまで? +#endif + +#define FM_EGBITS 16 + +//extern int paramcount[]; +//#define PARAMCHANGE(i) paramcount[i]++; +#define PARAMCHANGE(i) + +namespace FM +{ + +// --------------------------------------------------------------------------- +// Operator +// +// フィードバックバッファをクリア +inline void Operator::ResetFB() +{ + out_ = out2_ = 0; +} + +// キーオン +inline void Operator::KeyOn() +{ + if (!keyon_) + { + keyon_ = true; + held_ = false; + if (eg_phase_ == off || eg_phase_ == release) + { +#if 1 // libOPNMIDI: experimental SSG-EG + inverted_ = (ssg_type_ & 4) != 0; + inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN +#else + ssg_phase_ = -1; +#endif + ShiftPhase(attack); + EGUpdate(); + in2_ = out_ = out2_ = 0; + pg_count_ = 0; + } + } +} + +// キーオフ +inline void Operator::KeyOff() +{ + if (keyon_) + { + keyon_ = false; + ShiftPhase(release); + } +} + +// オペレータは稼働中か? +inline int Operator::IsOn() +{ + return eg_phase_ - off; +} + +// Detune (0-7) +inline void Operator::SetDT(uint dt) +{ + detune_ = dt * 0x20, param_changed_ = true; + PARAMCHANGE(4); +} + +// DT2 (0-3) +inline void Operator::SetDT2(uint dt2) +{ + detune2_ = dt2 & 3, param_changed_ = true; + PARAMCHANGE(5); +} + +// Multiple (0-15) +inline void Operator::SetMULTI(uint mul) +{ + multiple_ = mul, param_changed_ = true; + PARAMCHANGE(6); +} + +// Total Level (0-127) (0.75dB step) +inline void Operator::SetTL(uint tl, bool csm) +{ + if (!csm) + { + tl_ = tl, param_changed_ = true; + PARAMCHANGE(7); + } + tl_latch_ = tl; +} + +// Attack Rate (0-63) +inline void Operator::SetAR(uint ar) +{ + ar_ = ar; + param_changed_ = true; + PARAMCHANGE(8); +} + +// Decay Rate (0-63) +inline void Operator::SetDR(uint dr) +{ + dr_ = dr; + param_changed_ = true; + PARAMCHANGE(9); +} + +// Sustain Rate (0-63) +inline void Operator::SetSR(uint sr) +{ + sr_ = sr; + param_changed_ = true; + PARAMCHANGE(10); +} + +// Sustain Level (0-127) +inline void Operator::SetSL(uint sl) +{ + sl_ = sl; + param_changed_ = true; + PARAMCHANGE(11); +} + +// Release Rate (0-63) +inline void Operator::SetRR(uint rr) +{ + rr_ = rr; + param_changed_ = true; + PARAMCHANGE(12); +} + +// Keyscale (0-3) +inline void Operator::SetKS(uint ks) +{ + ks_ = ks; + param_changed_ = true; + PARAMCHANGE(13); +} + +// SSG-type Envelop (0-15) +inline void Operator::SetSSGEC(uint ssgec) +{ + if (ssgec & 8) + ssg_type_ = ssgec; + else + ssg_type_ = 0; +} + +inline void Operator::SetAMON(bool amon) +{ + amon_ = amon; + param_changed_ = true; + PARAMCHANGE(14); +} + +inline void Operator::Mute(bool mute) +{ + mute_ = mute; + param_changed_ = true; + PARAMCHANGE(15); +} + +inline void Operator::SetMS(uint ms) +{ + ms_ = ms; + param_changed_ = true; + PARAMCHANGE(16); +} + +// --------------------------------------------------------------------------- +// 4-op Channel + +// オペレータの種類 (LFO) を設定 +inline void Channel4::SetType(OpType type) +{ + for (int i=0; i<4; i++) + op[i].type_ = type; +} + +// セルフ・フィードバックレートの設定 (0-7) +inline void Channel4::SetFB(uint feedback) +{ + fb = fbtable[feedback]; +} + +// OPNA 系 LFO の設定 +inline void Channel4::SetMS(uint ms) +{ + op[0].SetMS(ms); + op[1].SetMS(ms); + op[2].SetMS(ms); + op[3].SetMS(ms); +} + +// チャンネル・マスク +inline void Channel4::Mute(bool m) +{ + for (int i=0; i<4; i++) + op[i].Mute(m); +} + +// 内部パラメータを再計算 +inline void Channel4::Refresh() +{ + for (int i=0; i<4; i++) + op[i].param_changed_ = true; + PARAMCHANGE(3); +} + +inline void Channel4::SetChip(Chip* chip) +{ + chip_ = chip; + for (int i=0; i<4; i++) + op[i].SetChip(chip); +} + +// --------------------------------------------------------------------------- +// +// +inline void StoreSample(Sample& dest, ISample data) +{ + if (sizeof(Sample) == 2) + dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); + else + dest += data; +} + + +// --------------------------------------------------------------------------- +// AM のレベルを設定 +inline void Chip::SetAML(uint l) +{ + aml_ = l & (FM_LFOENTS - 1); +} + +// PM のレベルを設定 +inline void Chip::SetPML(uint l) +{ + pml_ = l & (FM_LFOENTS - 1); +} + + +} + +#endif // FM_GEN_INL_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp index 1ec2f4daf..7bbfc3e41 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.cpp @@ -1,217 +1,217 @@ -// --------------------------------------------------------------------------- -// FM sound generator common timer module -// Copyright (C) cisc 1998, 2000. -// --------------------------------------------------------------------------- -// $Id: fmtimer.cpp,v 1.1 2000/09/08 13:45:56 cisc Exp $ - -#include "fmgen_headers.h" -#include "fmgen_fmtimer.h" - -using namespace FM; - -// --------------------------------------------------------------------------- -// タイマー制御 -// -void Timer::SetTimerControl(uint data) -{ - uint tmp = regtc ^ data; - regtc = uint8(data); - - if (data & 0x10) - ResetStatus(1); - if (data & 0x20) - ResetStatus(2); - - if (tmp & 0x01) - timera_count = (data & 1) ? timera : 0; - if (tmp & 0x02) - timerb_count = (data & 2) ? timerb : 0; -} - -#if 1 - -// --------------------------------------------------------------------------- -// タイマーA 周期設定 -// -void Timer::SetTimerA(uint addr, uint data) -{ - uint tmp; - regta[addr & 1] = uint8(data); - tmp = (regta[0] << 2) + (regta[1] & 3); - timera = (1024-tmp) * timer_step; -// LOG2("Timer A = %d %d us\n", tmp, timera >> 16); -} - -// --------------------------------------------------------------------------- -// タイマーB 周期設定 -// -void Timer::SetTimerB(uint data) -{ - timerb = (256-data) * timer_step; -// LOG2("Timer B = %d %d us\n", data, timerb >> 12); -} - -// --------------------------------------------------------------------------- -// タイマー時間処理 -// -bool Timer::Count(int32 us) -{ - bool event = false; - - if (timera_count) - { - timera_count -= us << 16; - if (timera_count <= 0) - { - event = true; - TimerA(); - - while (timera_count <= 0) - timera_count += timera; - - if (regtc & 4) - SetStatus(1); - } - } - if (timerb_count) - { - timerb_count -= us << 12; - if (timerb_count <= 0) - { - event = true; - while (timerb_count <= 0) - timerb_count += timerb; - - if (regtc & 8) - SetStatus(2); - } - } - return event; -} - -// --------------------------------------------------------------------------- -// 次にタイマーが発生するまでの時間を求める -// -int32 Timer::GetNextEvent() -{ - uint32 ta = ((timera_count + 0xffff) >> 16) - 1; - uint32 tb = ((timerb_count + 0xfff) >> 12) - 1; - return (ta < tb ? ta : tb) + 1; -} - -// --------------------------------------------------------------------------- -void Timer::DataSave(struct TimerData* data) -{ - data->status = status; - data->regtc = regtc; - data->regta[0] = regta[0]; - data->regta[1] = regta[1]; - data->timera = timera; - data->timera_count = timera_count; - data->timerb = timerb; - data->timerb_count = timerb_count; - data->timer_step = timer_step; -} - -// --------------------------------------------------------------------------- -void Timer::DataLoad(struct TimerData* data) -{ - status = data->status; - regtc = data->regtc; - regta[0] = data->regta[0]; - regta[1] = data->regta[1]; - timera = data->timera; - timera_count = data->timera_count; - timerb = data->timerb; - timerb_count = data->timerb_count; - timer_step = data->timer_step; -} - -// --------------------------------------------------------------------------- -// タイマー基準値設定 -// -void Timer::SetTimerBase(uint clock) -{ - timer_step = int32(1000000. * 65536 / clock); -} - -#else - -// --------------------------------------------------------------------------- -// タイマーA 周期設定 -// -void Timer::SetTimerA(uint addr, uint data) -{ - regta[addr & 1] = uint8(data); - timera = (1024 - ((regta[0] << 2) + (regta[1] & 3))) << 16; -} - -// --------------------------------------------------------------------------- -// タイマーB 周期設定 -// -void Timer::SetTimerB(uint data) -{ - timerb = (256-data) << (16 + 4); -} - -// --------------------------------------------------------------------------- -// タイマー時間処理 -// -bool Timer::Count(int32 us) -{ - bool event = false; - - int tick = us * timer_step; - - if (timera_count) - { - timera_count -= tick; - if (timera_count <= 0) - { - event = true; - TimerA(); - - while (timera_count <= 0) - timera_count += timera; - - if (regtc & 4) - SetStatus(1); - } - } - if (timerb_count) - { - timerb_count -= tick; - if (timerb_count <= 0) - { - event = true; - while (timerb_count <= 0) - timerb_count += timerb; - - if (regtc & 8) - SetStatus(2); - } - } - return event; -} - -// --------------------------------------------------------------------------- -// 次にタイマーが発生するまでの時間を求める -// -int32 Timer::GetNextEvent() -{ - uint32 ta = timera_count - 1; - uint32 tb = timerb_count - 1; - uint32 t = (ta < tb ? ta : tb) + 1; - - return (t+timer_step-1) / timer_step; -} - -// --------------------------------------------------------------------------- -// タイマー基準値設定 -// -void Timer::SetTimerBase(uint clock) -{ - timer_step = clock * 1024 / 15625; -} - -#endif +// --------------------------------------------------------------------------- +// FM sound generator common timer module +// Copyright (C) cisc 1998, 2000. +// --------------------------------------------------------------------------- +// $Id: fmtimer.cpp,v 1.1 2000/09/08 13:45:56 cisc Exp $ + +#include "fmgen_headers.h" +#include "fmgen_fmtimer.h" + +using namespace FM; + +// --------------------------------------------------------------------------- +// タイマー制御 +// +void Timer::SetTimerControl(uint data) +{ + uint tmp = regtc ^ data; + regtc = uint8(data); + + if (data & 0x10) + ResetStatus(1); + if (data & 0x20) + ResetStatus(2); + + if (tmp & 0x01) + timera_count = (data & 1) ? timera : 0; + if (tmp & 0x02) + timerb_count = (data & 2) ? timerb : 0; +} + +#if 1 + +// --------------------------------------------------------------------------- +// タイマーA 周期設定 +// +void Timer::SetTimerA(uint addr, uint data) +{ + uint tmp; + regta[addr & 1] = uint8(data); + tmp = (regta[0] << 2) + (regta[1] & 3); + timera = (1024-tmp) * timer_step; +// LOG2("Timer A = %d %d us\n", tmp, timera >> 16); +} + +// --------------------------------------------------------------------------- +// タイマーB 周期設定 +// +void Timer::SetTimerB(uint data) +{ + timerb = (256-data) * timer_step; +// LOG2("Timer B = %d %d us\n", data, timerb >> 12); +} + +// --------------------------------------------------------------------------- +// タイマー時間処理 +// +bool Timer::Count(int32 us) +{ + bool event = false; + + if (timera_count) + { + timera_count -= us << 16; + if (timera_count <= 0) + { + event = true; + TimerA(); + + while (timera_count <= 0) + timera_count += timera; + + if (regtc & 4) + SetStatus(1); + } + } + if (timerb_count) + { + timerb_count -= us << 12; + if (timerb_count <= 0) + { + event = true; + while (timerb_count <= 0) + timerb_count += timerb; + + if (regtc & 8) + SetStatus(2); + } + } + return event; +} + +// --------------------------------------------------------------------------- +// 次にタイマーが発生するまでの時間を求める +// +int32 Timer::GetNextEvent() +{ + uint32 ta = ((timera_count + 0xffff) >> 16) - 1; + uint32 tb = ((timerb_count + 0xfff) >> 12) - 1; + return (ta < tb ? ta : tb) + 1; +} + +// --------------------------------------------------------------------------- +void Timer::DataSave(struct TimerData* data) +{ + data->status = status; + data->regtc = regtc; + data->regta[0] = regta[0]; + data->regta[1] = regta[1]; + data->timera = timera; + data->timera_count = timera_count; + data->timerb = timerb; + data->timerb_count = timerb_count; + data->timer_step = timer_step; +} + +// --------------------------------------------------------------------------- +void Timer::DataLoad(struct TimerData* data) +{ + status = data->status; + regtc = data->regtc; + regta[0] = data->regta[0]; + regta[1] = data->regta[1]; + timera = data->timera; + timera_count = data->timera_count; + timerb = data->timerb; + timerb_count = data->timerb_count; + timer_step = data->timer_step; +} + +// --------------------------------------------------------------------------- +// タイマー基準値設定 +// +void Timer::SetTimerBase(uint clock) +{ + timer_step = int32(1000000. * 65536 / clock); +} + +#else + +// --------------------------------------------------------------------------- +// タイマーA 周期設定 +// +void Timer::SetTimerA(uint addr, uint data) +{ + regta[addr & 1] = uint8(data); + timera = (1024 - ((regta[0] << 2) + (regta[1] & 3))) << 16; +} + +// --------------------------------------------------------------------------- +// タイマーB 周期設定 +// +void Timer::SetTimerB(uint data) +{ + timerb = (256-data) << (16 + 4); +} + +// --------------------------------------------------------------------------- +// タイマー時間処理 +// +bool Timer::Count(int32 us) +{ + bool event = false; + + int tick = us * timer_step; + + if (timera_count) + { + timera_count -= tick; + if (timera_count <= 0) + { + event = true; + TimerA(); + + while (timera_count <= 0) + timera_count += timera; + + if (regtc & 4) + SetStatus(1); + } + } + if (timerb_count) + { + timerb_count -= tick; + if (timerb_count <= 0) + { + event = true; + while (timerb_count <= 0) + timerb_count += timerb; + + if (regtc & 8) + SetStatus(2); + } + } + return event; +} + +// --------------------------------------------------------------------------- +// 次にタイマーが発生するまでの時間を求める +// +int32 Timer::GetNextEvent() +{ + uint32 ta = timera_count - 1; + uint32 tb = timerb_count - 1; + uint32 t = (ta < tb ? ta : tb) + 1; + + return (t+timer_step-1) / timer_step; +} + +// --------------------------------------------------------------------------- +// タイマー基準値設定 +// +void Timer::SetTimerBase(uint clock) +{ + timer_step = clock * 1024 / 15625; +} + +#endif diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h index 880498801..38e880cc3 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_fmtimer.h @@ -1,67 +1,67 @@ -// --------------------------------------------------------------------------- -// FM sound generator common timer module -// Copyright (C) cisc 1998, 2000. -// --------------------------------------------------------------------------- -// $Id: fmtimer.h,v 1.2 2003/04/22 13:12:53 cisc Exp $ - -#ifndef FM_TIMER_H -#define FM_TIMER_H - -#include "fmgen_types.h" - -// --------------------------------------------------------------------------- - -namespace FM -{ - struct TimerData { - uint8 status; - uint8 regtc; - uint8 regta[2]; - int32 timera, timera_count; - int32 timerb, timerb_count; - int32 timer_step; - }; - - class Timer - { - public: - void Reset(); - bool Count(int32 us); - int32 GetNextEvent(); - - void DataSave(struct TimerData* data); - void DataLoad(struct TimerData* data); - - protected: - virtual void SetStatus(uint bit) = 0; - virtual void ResetStatus(uint bit) = 0; - - void SetTimerBase(uint clock); - void SetTimerA(uint addr, uint data); - void SetTimerB(uint data); - void SetTimerControl(uint data); - - uint8 status; - uint8 regtc; - - private: - virtual void TimerA() {} - uint8 regta[2]; - - int32 timera, timera_count; - int32 timerb, timerb_count; - int32 timer_step; - }; - -// --------------------------------------------------------------------------- -// -// -inline void Timer::Reset() -{ - timera_count = 0; - timerb_count = 0; -} - -} // namespace FM - -#endif // FM_TIMER_H +// --------------------------------------------------------------------------- +// FM sound generator common timer module +// Copyright (C) cisc 1998, 2000. +// --------------------------------------------------------------------------- +// $Id: fmtimer.h,v 1.2 2003/04/22 13:12:53 cisc Exp $ + +#ifndef FM_TIMER_H +#define FM_TIMER_H + +#include "fmgen_types.h" + +// --------------------------------------------------------------------------- + +namespace FM +{ + struct TimerData { + uint8 status; + uint8 regtc; + uint8 regta[2]; + int32 timera, timera_count; + int32 timerb, timerb_count; + int32 timer_step; + }; + + class Timer + { + public: + void Reset(); + bool Count(int32 us); + int32 GetNextEvent(); + + void DataSave(struct TimerData* data); + void DataLoad(struct TimerData* data); + + protected: + virtual void SetStatus(uint bit) = 0; + virtual void ResetStatus(uint bit) = 0; + + void SetTimerBase(uint clock); + void SetTimerA(uint addr, uint data); + void SetTimerB(uint data); + void SetTimerControl(uint data); + + uint8 status; + uint8 regtc; + + private: + virtual void TimerA() {} + uint8 regta[2]; + + int32 timera, timera_count; + int32 timerb, timerb_count; + int32 timer_step; + }; + +// --------------------------------------------------------------------------- +// +// +inline void Timer::Reset() +{ + timera_count = 0; + timerb_count = 0; +} + +} // namespace FM + +#endif // FM_TIMER_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_headers.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_headers.h index f677a0930..b432aed11 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_headers.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_headers.h @@ -1,10 +1,10 @@ -#ifndef WIN_HEADERS_H -#define WIN_HEADERS_H - -#include -#include -#include -#include -#include - -#endif // WIN_HEADERS_H +#ifndef WIN_HEADERS_H +#define WIN_HEADERS_H + +#include +#include +#include +#include +#include + +#endif // WIN_HEADERS_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_misc.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_misc.h index e654fbd78..a156572d1 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_misc.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_misc.h @@ -1,69 +1,69 @@ -// --------------------------------------------------------------------------- -// misc.h -// Copyright (C) cisc 1998, 1999. -// --------------------------------------------------------------------------- -// $Id: misc.h,v 1.5 2002/05/31 09:45:20 cisc Exp $ - -#ifndef MISC_H -#define MISC_H - -inline int Max(int x, int y) { return (x > y) ? x : y; } -inline int Min(int x, int y) { return (x < y) ? x : y; } -inline int Abs(int x) { return x >= 0 ? x : -x; } - -inline int Limit(int v, int max, int min) -{ - return v > max ? max : (v < min ? min : v); -} - -inline unsigned int BSwap(unsigned int a) -{ - return (a >> 24) | ((a >> 8) & 0xff00) | ((a << 8) & 0xff0000) | (a << 24); -} - -inline unsigned int NtoBCD(unsigned int a) -{ - return ((a / 10) << 4) + (a % 10); -} - -inline unsigned int BCDtoN(unsigned int v) -{ - return (v >> 4) * 10 + (v & 15); -} - - -template -inline T gcd(T x, T y) -{ - T t; - while (y) - { - t = x % y; - x = y; - y = t; - } - return x; -} - - -template -T bessel0(T x) -{ - T p, r, s; - - r = 1.0; - s = 1.0; - p = (x / 2.0) / s; - - while (p > 1.0E-10) - { - r += p * p; - s += 1.0; - p *= (x / 2.0) / s; - } - return r; -} - - -#endif // MISC_H - +// --------------------------------------------------------------------------- +// misc.h +// Copyright (C) cisc 1998, 1999. +// --------------------------------------------------------------------------- +// $Id: misc.h,v 1.5 2002/05/31 09:45:20 cisc Exp $ + +#ifndef MISC_H +#define MISC_H + +inline int Max(int x, int y) { return (x > y) ? x : y; } +inline int Min(int x, int y) { return (x < y) ? x : y; } +inline int Abs(int x) { return x >= 0 ? x : -x; } + +inline int Limit(int v, int max, int min) +{ + return v > max ? max : (v < min ? min : v); +} + +inline unsigned int BSwap(unsigned int a) +{ + return (a >> 24) | ((a >> 8) & 0xff00) | ((a << 8) & 0xff0000) | (a << 24); +} + +inline unsigned int NtoBCD(unsigned int a) +{ + return ((a / 10) << 4) + (a % 10); +} + +inline unsigned int BCDtoN(unsigned int v) +{ + return (v >> 4) * 10 + (v & 15); +} + + +template +inline T gcd(T x, T y) +{ + T t; + while (y) + { + t = x % y; + x = y; + y = t; + } + return x; +} + + +template +T bessel0(T x) +{ + T p, r, s; + + r = 1.0; + s = 1.0; + p = (x / 2.0) / s; + + while (p > 1.0E-10) + { + r += p * p; + s += 1.0; + p *= (x / 2.0) / s; + } + return r; +} + + +#endif // MISC_H + diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp index d6e3812f3..b1282755f 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp @@ -1,2142 +1,2142 @@ -// --------------------------------------------------------------------------- -// OPN/A/B interface with ADPCM support -// Copyright (C) cisc 1998, 2001. -// --------------------------------------------------------------------------- -// $Id: opna.cpp,v 1.68 2003/06/12 14:03:44 cisc Exp $ - -#include "fmgen_types.h" -#include "fmgen_headers.h" -#include "fmgen_misc.h" -#include "fmgen_opna.h" -#include "fmgen_fmgeninl.h" - -#define BUILD_OPN -#define BUILD_OPNA -#define BUILD_OPNB - - -// TOFIX: -// OPN ch3 �����Prepare�̑ΏۂƂȂ��Ă��܂���Q - - -// --------------------------------------------------------------------------- -// OPNA: ADPCM �f�[�^�̊i�[�����̈Ⴂ (8bit/1bit) ���G�~�����[�g���Ȃ� -// ���̃I�v�V������L���ɂ���� ADPCM �������ւ̃A�N�Z�X(���� 8bit ���[�h)�� -// �����y���Ȃ邩�� -// -//#define NO_BITTYPE_EMULATION - -#ifdef BUILD_OPNA -#include "fmgen_file.h" -#endif - -namespace FM -{ - -// --------------------------------------------------------------------------- -// OPNBase - -#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) - -uint32 OPNBase::lfotable[8]; // OPNA/B �p - -OPNBase::OPNBase() -{ - prescale = 0; -} - -// �p�����[�^�Z�b�g -void OPNBase::SetParameter(Channel4* ch, uint addr, uint data) -{ - const static uint slottable[4] = { 0, 2, 1, 3 }; - const static uint8 sltable[16] = - { - 0, 4, 8, 12, 16, 20, 24, 28, - 32, 36, 40, 44, 48, 52, 56, 124, - }; - - if ((addr & 3) < 3) - { - uint slot = slottable[(addr >> 2) & 3]; - Operator* op = &ch->op[slot]; - - switch ((addr >> 4) & 15) - { - case 3: // 30-3E DT/MULTI - op->SetDT((data >> 4) & 0x07); - op->SetMULTI(data & 0x0f); - break; - - case 4: // 40-4E TL - op->SetTL(data & 0x7f, (regtc & 0x80) && (csmch == ch)); - break; - - case 5: // 50-5E KS/AR - op->SetKS((data >> 6) & 3); - op->SetAR((data & 0x1f) * 2); - break; - - case 6: // 60-6E DR/AMON - op->SetDR((data & 0x1f) * 2); - op->SetAMON((data & 0x80) != 0); - break; - - case 7: // 70-7E SR - op->SetSR((data & 0x1f) * 2); - break; - - case 8: // 80-8E SL/RR - op->SetSL(sltable[(data >> 4) & 15]); - op->SetRR((data & 0x0f) * 4 + 2); - break; - - case 9: // 90-9E SSG-EC - op->SetSSGEC(data & 0x0f); - break; - } - } -} - -// ���Z�b�g -void OPNBase::Reset() -{ - status = 0; - SetPrescaler(0); - Timer::Reset(); - psg.Reset(); -} - -// �v���X�P�[���ݒ� -void OPNBase::SetPrescaler(uint p) -{ - static const char table[3][2] = { { 6, 4 }, { 3, 2 }, { 2, 1 } }; - static const uint8 table2[8] = { 108, 77, 71, 67, 62, 44, 8, 5 }; - // 512 - if (prescale != p) - { - prescale = p; - /*warning: comparison is always true due to limited range of data type [-Wtype-limits]*/ - // assert(0 <= prescale && prescale < 3); - assert(prescale < 3); - - uint fmclock = clock / table[p][0] / 12; - - rate = psgrate; - - // �������g���Əo�͎��g���̔� - assert(fmclock < (0x80000000 >> FM_RATIOBITS)); - uint ratio = ((fmclock << FM_RATIOBITS) + rate/2) / rate; - - SetTimerBase(fmclock); -// MakeTimeTable(ratio); - chip.SetRatio(ratio); - psg.SetClock(clock / table[p][1], psgrate); - - for (int i=0; i<8; i++) - { - lfotable[i] = (ratio << (2+FM_LFOCBITS-FM_RATIOBITS)) / table2[i]; - } - } -} - -// ������ -bool OPNBase::Init(uint c, uint r) -{ - clock = c; - psgrate = r; - - return true; -} - -// ���ʐݒ� -void OPNBase::SetVolumeFM(int db) -{ - db = Min(db, 20); - if (db > -192) - fmvolume = int(16384.0 * pow(10.0, db / 40.0)); - else - fmvolume = 0; -} - -// �^�C�}�[���ԏ��� -void OPNBase::TimerA() -{ - if (regtc & 0x80) - { - csmch->KeyControl(0x00); - csmch->KeyControl(0x0f); - } -} - -void OPNBase::DataSave(struct OPNBaseData* data) { - Timer::DataSave(&data->timer); - data->fmvolume = fmvolume; - data->clock = clock; - data->rate = rate; - data->psgrate = psgrate; - data->status = status; - data->prescale = prescale; - chip.DataSave(&data->chip); - psg.DataSave(&data->psg); -} - -void OPNBase::DataLoad(struct OPNBaseData* data) { - Timer::DataLoad(&data->timer); - fmvolume = data->fmvolume; - clock = data->clock; - rate = data->rate; - psgrate = data->psgrate; - status = data->status; - prescale = data->prescale; - chip.DataLoad(&data->chip); - psg.DataLoad(&data->psg); -} - -#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) - -// --------------------------------------------------------------------------- -// YM2203 -// -#ifdef BUILD_OPN - -OPN::OPN() -{ - SetVolumeFM(0); - SetVolumePSG(0); - - csmch = &ch[2]; - - for (int i=0; i<3; i++) - { - ch[i].SetChip(&chip); - ch[i].SetType(typeN); - } -} - -// ������ -bool OPN::Init(uint c, uint r, bool ip, const char*) -{ - if (!SetRate(c, r, ip)) - return false; - - Reset(); - - SetVolumeFM(0); - SetVolumePSG(0); - SetChannelMask(0); - return true; -} - -// �T���v�����O���[�g�ύX -bool OPN::SetRate(uint c, uint r, bool) -{ - OPNBase::Init(c, r); - RebuildTimeTable(); - return true; -} - - -// ���Z�b�g -void OPN::Reset() -{ - int i; - for (i=0x20; i<0x28; i++) SetReg(i, 0); - for (i=0x30; i<0xc0; i++) SetReg(i, 0); - OPNBase::Reset(); - ch[0].Reset(); - ch[1].Reset(); - ch[2].Reset(); -} - - -// ���W�X�^�ǂݍ��� -uint OPN::GetReg(uint addr) -{ - if (addr < 0x10) - return psg.GetReg(addr); - else - return 0; -} - - -// ���W�X�^�A���C�Ƀf�[�^��ݒ� -void OPN::SetReg(uint addr, uint data) -{ -// LOG2("reg[%.2x] <- %.2x\n", addr, data); - if (addr >= 0x100) - return; - - int c = addr & 3; - switch (addr) - { - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: - psg.SetReg(addr, data); - break; - - case 0x24: case 0x25: - SetTimerA(addr, data); - break; - - case 0x26: - SetTimerB(data); - break; - - case 0x27: - SetTimerControl(data); - break; - - case 0x28: // Key On/Off - if ((data & 3) < 3) - ch[data & 3].KeyControl(data >> 4); - break; - - case 0x2d: case 0x2e: case 0x2f: - SetPrescaler(addr-0x2d); - break; - - // F-Number - case 0xa0: case 0xa1: case 0xa2: - fnum[c] = data + fnum2[c] * 0x100; - break; - - case 0xa4: case 0xa5: case 0xa6: - fnum2[c] = uint8(data); - break; - - case 0xa8: case 0xa9: case 0xaa: - fnum3[c] = data + fnum2[c+3] * 0x100; - break; - - case 0xac: case 0xad: case 0xae: - fnum2[c+3] = uint8(data); - break; - - case 0xb0: case 0xb1: case 0xb2: - ch[c].SetFB((data >> 3) & 7); - ch[c].SetAlgorithm(data & 7); - break; - - default: - if (c < 3) - { - if ((addr & 0xf0) == 0x60) - data &= 0x1f; - OPNBase::SetParameter(&ch[c], addr, data); - } - break; - } -} - -// �X�e�[�^�X�t���O�ݒ� -void OPN::SetStatus(uint bits) -{ - if (!(status & bits)) - { - status |= bits; - Intr(true); - } -} - -void OPN::ResetStatus(uint bit) -{ - status &= ~bit; - if (!status) - Intr(false); -} - -// �}�X�N�ݒ� -void OPN::SetChannelMask(uint mask) -{ - for (int i=0; i<3; i++) - ch[i].Mute(!!(mask & (1 << i))); - psg.SetChannelMask(mask >> 6); -} - -void OPN::DataSave(struct OPNData* data) { - OPNBase::DataSave(&data->opnbase); - memcpy(data->fnum, fnum, sizeof(uint) * 3); - memcpy(data->fnum3, fnum3, sizeof(uint) * 3); - memcpy(data->fnum2, fnum2, 6); - for(int i = 0; i < 3; i++) { - ch[i].DataSave(&data->ch[i]); - } -} - -void OPN::DataLoad(struct OPNData* data) { - OPNBase::DataLoad(&data->opnbase); - memcpy(fnum, data->fnum, sizeof(uint) * 3); - memcpy(fnum3, data->fnum3, sizeof(uint) * 3); - memcpy(fnum2, data->fnum2, 6); - for(int i = 0; i < 3; i++) { - ch[i].DataLoad(&data->ch[i]); - } - csmch = &ch[2]; - for (int i=0; i<3; i++) - { - ch[i].SetChip(&chip); - ch[i].SetType(typeN); - } -} - -// ����(2ch) -void OPN::Mix(Sample* buffer, int nsamples) -{ -//printf("M:%d\n",nsamples); -#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) - - psg.Mix(buffer, nsamples); - - // Set F-Number - ch[0].SetFNum(fnum[0]); - ch[1].SetFNum(fnum[1]); - if (!(regtc & 0xc0)) - ch[2].SetFNum(fnum[2]); - else - { // ���ʉ� - ch[2].op[0].SetFNum(fnum3[1]); - ch[2].op[1].SetFNum(fnum3[2]); - ch[2].op[2].SetFNum(fnum3[0]); - ch[2].op[3].SetFNum(fnum[2]); - } - - int actch = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); -//printf("a %X\n",actch); - if (actch & 0x15) - { - Sample* limit = buffer + nsamples * 2; - for (Sample* dest = buffer; dest < limit; dest+=2) - { - ISample s = 0; - if (actch & 0x01) s = ch[0].Calc(); - if (actch & 0x04) s += ch[1].Calc(); - if (actch & 0x10) s += ch[2].Calc(); - s = IStoSample(s); - StoreSample(dest[0], s); - StoreSample(dest[1], s); -//printf("%08X,%08X\n",dest[0],dest[1]); - } - } -#undef IStoSample -} - -#endif // BUILD_OPN - -// --------------------------------------------------------------------------- -// YM2608/2610 common part -// --------------------------------------------------------------------------- - -#if defined(BUILD_OPNA) || defined(BUILD_OPNB) - -int OPNABase::amtable[FM_LFOENTS] = { -1, }; -int OPNABase::pmtable[FM_LFOENTS]; - -int32 OPNABase::tltable[FM_TLENTS+FM_TLPOS]; -bool OPNABase::tablehasmade = false; - -OPNABase::OPNABase() -{ - adpcmbuf = 0; - memaddr = 0; - startaddr = 0; - deltan = 256; - - adpcmvol = 0; - control2 = 0; - - MakeTable2(); - BuildLFOTable(); - for (int i=0; i<6; i++) - { - ch[i].SetChip(&chip); - ch[i].SetType(typeN); - } -} - -OPNABase::~OPNABase() -{ -} - -// --------------------------------------------------------------------------- -// ������ -// -bool OPNABase::Init(uint, uint, bool) -{ - RebuildTimeTable(); - - Reset(); - - SetVolumeFM(0); - SetVolumePSG(0); - SetChannelMask(0); - return true; -} - -// --------------------------------------------------------------------------- -// �e�[�u���쐬 -// -void OPNABase::MakeTable2() -{ - if (!tablehasmade) - { - for (int i=-FM_TLPOS; i> 16; - - RebuildTimeTable(); - - lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0; - return true; -} - - -// --------------------------------------------------------------------------- -// �`�����l���}�X�N�̐ݒ� -// -void OPNABase::SetChannelMask(uint mask) -{ - for (int i=0; i<6; i++) - ch[i].Mute(!!(mask & (1 << i))); - psg.SetChannelMask(mask >> 6); - adpcmmask_ = (mask & (1 << 9)) != 0; - rhythmmask_ = (mask >> 10) & ((1 << 6) - 1); -} - -void OPNABase::SetPan(uint c, uint8 p) -{ - panvolume_l[c] = panlawtable[p & 0x7f]; - panvolume_r[c] = panlawtable[0x7f - (p & 0x7f)]; -} - -// --------------------------------------------------------------------------- -void OPNABase::DataSave(struct OPNABaseData* data) { - OPNBase::DataSave(&data->opnbase); - memcpy(data->pan, pan, 6); - memcpy(data->panvolume_l, panvolume_l, sizeof(uint16) * 6); - memcpy(data->panvolume_r, panvolume_r, sizeof(uint16) * 6); - memcpy(data->fnum2, fnum2, 9); - data->reg22 = reg22; - data->reg29 = reg29; - data->stmask = stmask; - data->statusnext = statusnext; - data->lfocount = lfocount; - data->lfodcount = lfodcount; - memcpy(data->fnum, fnum, sizeof(uint) * 6); - memcpy(data->fnum3, fnum3, sizeof(uint) * 3); - data->is_adpcmbuf = 0; - if(adpcmbuf) { - data->is_adpcmbuf = 1; - memcpy(data->adpcmbuf, adpcmbuf, 0x40000); - } - data->adpcmmask = adpcmmask; - data->adpcmnotice = adpcmnotice; - data->startaddr = startaddr; - data->stopaddr = stopaddr; - data->memaddr = memaddr; - data->limitaddr = limitaddr; - data->adpcmlevel = adpcmlevel; - data->adpcmvolume = adpcmvolume; - data->adpcmvol = adpcmvol; - data->deltan = deltan; - data->adplc = adplc; - data->adpld = adpld; - data->adplbase = adplbase; - data->adpcmx = adpcmx; - data->adpcmd = adpcmd; - data->adpcmout = adpcmout; - data->apout0 = apout0; - data->apout1 = apout1; - data->adpcmreadbuf = adpcmreadbuf; - data->adpcmplay = adpcmplay; - data->granuality = granuality; - data->control1 = control1; - data->control2 = control2; - memcpy(data->adpcmreg, adpcmreg, 8); - data->rhythmmask_ = rhythmmask_; - for(int i = 0; i < 6; i++) { - ch[i].DataSave(&data->ch[i]); - } -} - -// --------------------------------------------------------------------------- -void OPNABase::DataLoad(struct OPNABaseData* data) { - OPNBase::DataLoad(&data->opnbase); - memcpy(pan, data->pan, 6); - memcpy(panvolume_l, data->panvolume_l, sizeof(uint16) * 6); - memcpy(panvolume_r, data->panvolume_r, sizeof(uint16) * 6); - memcpy(fnum2, data->fnum2, 9); - reg22 = data->reg22; - reg29 = data->reg29; - stmask = data->stmask; - statusnext = data->statusnext; - lfocount = data->lfocount; - lfodcount = data->lfodcount; - memcpy(fnum, data->fnum, sizeof(uint) * 6); - memcpy(fnum3, data->fnum3, sizeof(uint) * 3); - if(data->is_adpcmbuf) { - if(!adpcmbuf) - adpcmbuf = new uint8[0x40000]; - memcpy(adpcmbuf, data->adpcmbuf, 0x40000); - } - adpcmmask = data->adpcmmask; - adpcmnotice = data->adpcmnotice; - startaddr = data->startaddr; - stopaddr = data->stopaddr; - memaddr = data->memaddr; - limitaddr = data->limitaddr; - adpcmlevel = data->adpcmlevel; - adpcmvolume = data->adpcmvolume; - adpcmvol = data->adpcmvol; - deltan = data->deltan; - adplc = data->adplc; - adpld = data->adpld; - adplbase = data->adplbase; - adpcmx = data->adpcmx; - adpcmd = data->adpcmd; - adpcmout = data->adpcmout; - apout0 = data->apout0; - apout1 = data->apout1; - adpcmreadbuf = data->adpcmreadbuf; - adpcmplay = data->adpcmplay; - granuality = data->granuality; - control1 = data->control1; - control2 = data->control2; - memcpy(adpcmreg, data->adpcmreg, 8); - rhythmmask_ = data->rhythmmask_; - for(int i = 0; i < 6; i++) { - ch[i].DataLoad(&data->ch[i]); - ch[i].SetChip(&chip); - } -} - -// --------------------------------------------------------------------------- -// ���W�X�^�A���C�Ƀf�[�^��ݒ� -// -void OPNABase::SetReg(uint addr, uint data) -{ - int c = addr & 3; - switch (addr) - { - uint modified; - - // Timer ----------------------------------------------------------------- - case 0x24: case 0x25: - SetTimerA(addr, data); - break; - - case 0x26: - SetTimerB(data); - break; - - case 0x27: - SetTimerControl(data); - break; - - // Misc ------------------------------------------------------------------ - case 0x28: // Key On/Off - if ((data & 3) < 3) - { - c = (data & 3) + (data & 4 ? 3 : 0); - ch[c].KeyControl(data >> 4); - } - break; - - // Status Mask ----------------------------------------------------------- - case 0x29: - reg29 = data; -// UpdateStatus(); //? - break; - - // Prescaler ------------------------------------------------------------- - case 0x2d: case 0x2e: case 0x2f: - SetPrescaler(addr-0x2d); - break; - - // F-Number -------------------------------------------------------------- - case 0x1a0: case 0x1a1: case 0x1a2: - c += 3; - // fall through - case 0xa0: case 0xa1: case 0xa2: - fnum[c] = data + fnum2[c] * 0x100; - ch[c].SetFNum(fnum[c]); - break; - - case 0x1a4: case 0x1a5: case 0x1a6: - c += 3; - // fall through - case 0xa4 : case 0xa5: case 0xa6: - fnum2[c] = uint8(data); - break; - - case 0xa8: case 0xa9: case 0xaa: - fnum3[c] = data + fnum2[c+6] * 0x100; - break; - - case 0xac : case 0xad: case 0xae: - fnum2[c+6] = uint8(data); - break; - - // Algorithm ------------------------------------------------------------- - - case 0x1b0: case 0x1b1: case 0x1b2: - c += 3; - // fall through - case 0xb0: case 0xb1: case 0xb2: - ch[c].SetFB((data >> 3) & 7); - ch[c].SetAlgorithm(data & 7); - break; - - case 0x1b4: case 0x1b5: case 0x1b6: - c += 3; - // fall through - case 0xb4: case 0xb5: case 0xb6: - pan[c] = (data >> 6) & 3; - ch[c].SetMS(data); - break; - - // LFO ------------------------------------------------------------------- - case 0x22: - modified = reg22 ^ data; - reg22 = data; - if (modified & 0x8) - lfocount = 0; - lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0; - break; - - // PSG ------------------------------------------------------------------- - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: - psg.SetReg(addr, data); - break; - - // ���F ------------------------------------------------------------------ - default: - if (c < 3) - { - if (addr & 0x100) - c += 3; - OPNBase::SetParameter(&ch[c], addr, data); - } - break; - } -} - -// --------------------------------------------------------------------------- -// ADPCM B -// -void OPNABase::SetADPCMBReg(uint addr, uint data) -{ - switch (addr) - { - case 0x00: // Control Register 1 - if ((data & 0x80) && !adpcmplay) - { - adpcmplay = true; - memaddr = startaddr; - adpcmx = 0, adpcmd = 127; - adplc = 0; - } - if (data & 1) - { - adpcmplay = false; - } - control1 = data; - break; - - case 0x01: // Control Register 2 - control2 = data; - granuality = control2 & 2 ? 1 : 4; - break; - - case 0x02: // Start Address L - case 0x03: // Start Address H - adpcmreg[addr - 0x02 + 0] = data; - startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 6; - memaddr = startaddr; -// LOG1(" startaddr %.6x", startaddr); - break; - - case 0x04: // Stop Address L - case 0x05: // Stop Address H - adpcmreg[addr - 0x04 + 2] = data; - stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 6; -// LOG1(" stopaddr %.6x", stopaddr); - break; - - case 0x08: // ADPCM data - if ((control1 & 0x60) == 0x60) - { -// LOG2(" Wr [0x%.5x] = %.2x", memaddr, data); - WriteRAM(data); - } - break; - - case 0x09: // delta-N L - case 0x0a: // delta-N H - adpcmreg[addr - 0x09 + 4] = data; - deltan = adpcmreg[5]*256+adpcmreg[4]; - deltan = Max(256, deltan); - adpld = deltan * adplbase >> 16; - break; - - case 0x0b: // Level Control - adpcmlevel = data; - adpcmvolume = (adpcmvol * adpcmlevel) >> 12; - break; - - case 0x0c: // Limit Address L - case 0x0d: // Limit Address H - adpcmreg[addr - 0x0c + 6] = data; - limitaddr = (adpcmreg[7]*256+adpcmreg[6] + 1) << 6; -// LOG1(" limitaddr %.6x", limitaddr); - break; - - case 0x10: // Flag Control - if (data & 0x80) - { - status = 0; - UpdateStatus(); - } - else - { - stmask = ~(data & 0x1f); -// UpdateStatus(); //??? - } - break; - } -} - - -// --------------------------------------------------------------------------- -// ���W�X�^�擾 -// -uint OPNA::GetReg(uint addr) -{ - if (addr < 0x10) - return psg.GetReg(addr); - - if (addr == 0x108) - { -// LOG1("%d:reg[108] -> ", Diag::GetCPUTick()); - - uint data = adpcmreadbuf & 0xff; - adpcmreadbuf >>= 8; - if ((control1 & 0x60) == 0x20) - { - adpcmreadbuf |= ReadRAM() << 8; -// LOG2("Rd [0x%.6x:%.2x] ", memaddr, adpcmreadbuf >> 8); - } -// LOG0("%.2x\n"); - return data; - } - - if (addr == 0xff) - return 1; - - return 0; -} - - - - -// --------------------------------------------------------------------------- -// �X�e�[�^�X�t���O�ݒ� -// -void OPNABase::SetStatus(uint bits) -{ - if (!(status & bits)) - { -// LOG2("SetStatus(%.2x %.2x)\n", bits, stmask); - status |= bits & stmask; - UpdateStatus(); - } -// else -// LOG1("SetStatus(%.2x) - ignored\n", bits); -} - -void OPNABase::ResetStatus(uint bits) -{ - status &= ~bits; -// LOG1("ResetStatus(%.2x)\n", bits); - UpdateStatus(); -} - -inline void OPNABase::UpdateStatus() -{ -// LOG2("%d:INT = %d\n", Diag::GetCPUTick(), (status & stmask & reg29) != 0); - Intr((status & stmask & reg29) != 0); -} - -// --------------------------------------------------------------------------- -// ADPCM RAM �ւ̏����ݑ��� -// -void OPNABase::WriteRAM(uint data) -{ -#ifndef NO_BITTYPE_EMULATION - if (!(control2 & 2)) - { - // 1 bit mode - adpcmbuf[(memaddr >> 4) & 0x3ffff] = data; - memaddr += 16; - } - else - { - // 8 bit mode - uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; - uint bank = (memaddr >> 1) & 7; - uint8 mask = 1 << bank; - data <<= bank; - - p[0x00000] = (p[0x00000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x08000] = (p[0x08000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x10000] = (p[0x10000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x18000] = (p[0x18000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x20000] = (p[0x20000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x28000] = (p[0x28000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x30000] = (p[0x30000] & ~mask) | (uint8(data) & mask); data >>= 1; - p[0x38000] = (p[0x38000] & ~mask) | (uint8(data) & mask); - memaddr += 2; - } -#else - adpcmbuf[(memaddr >> granuality) & 0x3ffff] = data; - memaddr += 1 << granuality; -#endif - - if (memaddr == stopaddr) - { - SetStatus(4); - statusnext = 0x04; // EOS - memaddr &= 0x3fffff; - } - if (memaddr == limitaddr) - { -// LOG1("Limit ! (%.8x)\n", limitaddr); - memaddr = 0; - } - SetStatus(8); -} - -// --------------------------------------------------------------------------- -// ADPCM RAM ����̓ǂݍ��ݑ��� -// -uint OPNABase::ReadRAM() -{ - uint data; -#ifndef NO_BITTYPE_EMULATION - if (!(control2 & 2)) - { - // 1 bit mode - data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; - memaddr += 16; - } - else - { - // 8 bit mode - uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; - uint bank = (memaddr >> 1) & 7; - uint8 mask = 1 << bank; - - data = (p[0x38000] & mask); - data = data * 2 + (p[0x30000] & mask); - data = data * 2 + (p[0x28000] & mask); - data = data * 2 + (p[0x20000] & mask); - data = data * 2 + (p[0x18000] & mask); - data = data * 2 + (p[0x10000] & mask); - data = data * 2 + (p[0x08000] & mask); - data = data * 2 + (p[0x00000] & mask); - data >>= bank; - memaddr += 2; - } -#else - data = adpcmbuf[(memaddr >> granuality) & 0x3ffff]; - memaddr += 1 << granuality; -#endif - if (memaddr == stopaddr) - { - SetStatus(4); - statusnext = 0x04; // EOS - memaddr &= 0x3fffff; - } - if (memaddr == limitaddr) - { -// LOG1("Limit ! (%.8x)\n", limitaddr); - memaddr = 0; - } - if (memaddr < stopaddr) - SetStatus(8); - return data; -} - - -inline int OPNABase::DecodeADPCMBSample(uint data) -{ - static const int table1[16] = - { - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9, -11, -13, -15, - }; - static const int table2[16] = - { - 57, 57, 57, 57, 77, 102, 128, 153, - 57, 57, 57, 57, 77, 102, 128, 153, - }; - adpcmx = Limit(adpcmx + table1[data] * adpcmd / 8, 32767, -32768); - adpcmd = Limit(adpcmd * table2[data] / 64, 24576, 127); - return adpcmx; -} - - -// --------------------------------------------------------------------------- -// ADPCM RAM ����� nibble �ǂݍ��݋y�� ADPCM �W�J -// -int OPNABase::ReadRAMN() -{ - uint data; - if (granuality > 0) - { -#ifndef NO_BITTYPE_EMULATION - if (!(control2 & 2)) - { - data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; - memaddr += 8; - if (memaddr & 8) - return DecodeADPCMBSample(data >> 4); - data &= 0x0f; - } - else - { - uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff] + ((~memaddr & 1) << 17); - uint bank = (memaddr >> 1) & 7; - uint8 mask = 1 << bank; - - data = (p[0x18000] & mask); - data = data * 2 + (p[0x10000] & mask); - data = data * 2 + (p[0x08000] & mask); - data = data * 2 + (p[0x00000] & mask); - data >>= bank; - memaddr ++; - if (memaddr & 1) - return DecodeADPCMBSample(data); - } -#else - data = adpcmbuf[(memaddr >> granuality) & adpcmmask]; - memaddr += 1 << (granuality-1); - if (memaddr & (1 << (granuality-1))) - return DecodeADPCMBSample(data >> 4); - data &= 0x0f; -#endif - } - else - { - data = adpcmbuf[(memaddr >> 1) & adpcmmask]; - ++memaddr; - if (memaddr & 1) - return DecodeADPCMBSample(data >> 4); - data &= 0x0f; - } - - DecodeADPCMBSample(data); - - // check - if (memaddr == stopaddr) - { - if (control1 & 0x10) - { - memaddr = startaddr; - data = adpcmx; - adpcmx = 0, adpcmd = 127; - return data; - } - else - { - memaddr &= adpcmmask; //0x3fffff; - SetStatus(adpcmnotice); - adpcmplay = false; - } - } - - if (memaddr == limitaddr) - memaddr = 0; - - return adpcmx; -} - -// --------------------------------------------------------------------------- -// �g���X�e�[�^�X��ǂ݂��� -// -uint OPNABase::ReadStatusEx() -{ - uint r = ((status | 8) & stmask) | (adpcmplay ? 0x20 : 0); - status |= statusnext; - statusnext = 0; - return r; -} - -// --------------------------------------------------------------------------- -// ADPCM �W�J -// -inline void OPNABase::DecodeADPCMB() -{ - apout0 = apout1; - int n = (ReadRAMN() * adpcmvolume) >> 13; - apout1 = adpcmout + n; - adpcmout = n; -} - -// --------------------------------------------------------------------------- -// ADPCM ���� -// -void OPNABase::ADPCMBMix(Sample* dest, uint count) -{ - uint maskl = control2 & 0x80 ? -1 : 0; - uint maskr = control2 & 0x40 ? -1 : 0; - if (adpcmmask_) - { - maskl = maskr = 0; - } - - if (adpcmplay) - { -// LOG2("ADPCM Play: %d DeltaN: %d\n", adpld, deltan); - if (adpld <= 8192) // fplay < fsamp - { - for (; count>0; count--) - { - if (adplc < 0) - { - adplc += 8192; - DecodeADPCMB(); - if (!adpcmplay) - break; - } - int s = (adplc * apout0 + (8192-adplc) * apout1) >> 13; - StoreSample(dest[0], s & maskl); - StoreSample(dest[1], s & maskr); - dest += 2; - adplc -= adpld; - } - for (; count>0 && apout0; count--) - { - if (adplc < 0) - { - apout0 = apout1, apout1 = 0; - adplc += 8192; - } - int s = (adplc * apout1) >> 13; - StoreSample(dest[0], s & maskl); - StoreSample(dest[1], s & maskr); - dest += 2; - adplc -= adpld; - } - } - else // fplay > fsamp (adpld = fplay/famp*8192) - { - int t = (-8192*8192)/adpld; - for (; count>0; count--) - { - int s = apout0 * (8192+adplc); - while (adplc < 0) - { - DecodeADPCMB(); - if (!adpcmplay) - goto stop; - s -= apout0 * Max(adplc, t); - adplc -= t; - } - adplc -= 8192; - s >>= 13; - StoreSample(dest[0], s & maskl); - StoreSample(dest[1], s & maskr); - dest += 2; - } -stop: - ; - } - } - if (!adpcmplay) - { - apout0 = apout1 = adpcmout = 0; - adplc = 0; - } -} - -// --------------------------------------------------------------------------- -// ���� -// in: buffer ������ -// nsamples �����T���v���� -// -void OPNABase::FMMix(Sample* buffer, int nsamples) -{ - if (fmvolume > 0) - { - // ���� - // Set F-Number - if (!(regtc & 0xc0)) - csmch->SetFNum(fnum[csmch-ch]); - else - { - // ���ʉ����[�h - csmch->op[0].SetFNum(fnum3[1]); csmch->op[1].SetFNum(fnum3[2]); - csmch->op[2].SetFNum(fnum3[0]); csmch->op[3].SetFNum(fnum[2]); - } - - int act = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); - if (reg29 & 0x80) - act |= (ch[3].Prepare() | ((ch[4].Prepare() | (ch[5].Prepare() << 2)) << 2)) << 6; - if (!(reg22 & 0x08)) - act &= 0x555; - - if (act & 0x555) - { - Mix6(buffer, nsamples, act); - } - } -} - -// --------------------------------------------------------------------------- - -void OPNABase::MixSubSL(int activech, ISample** dest) -{ - if (activech & 0x001) (*dest[0] = ch[0].CalcL()); - if (activech & 0x004) (*dest[1] += ch[1].CalcL()); - if (activech & 0x010) (*dest[2] += ch[2].CalcL()); - if (activech & 0x040) (*dest[3] += ch[3].CalcL()); - if (activech & 0x100) (*dest[4] += ch[4].CalcL()); - if (activech & 0x400) (*dest[5] += ch[5].CalcL()); -} - -inline void OPNABase::MixSubS(int activech, ISample** dest) -{ - if (activech & 0x001) (*dest[0] = ch[0].Calc()); - if (activech & 0x004) (*dest[1] += ch[1].Calc()); - if (activech & 0x010) (*dest[2] += ch[2].Calc()); - if (activech & 0x040) (*dest[3] += ch[3].Calc()); - if (activech & 0x100) (*dest[4] += ch[4].Calc()); - if (activech & 0x400) (*dest[5] += ch[5].Calc()); -} - -// --------------------------------------------------------------------------- - -void OPNABase::BuildLFOTable() -{ - if (amtable[0] == -1) - { - for (int c=0; c<256; c++) - { - int v; - if (c < 0x40) v = c * 2 + 0x80; - else if (c < 0xc0) v = 0x7f - (c - 0x40) * 2 + 0x80; - else v = (c - 0xc0) * 2; - pmtable[c] = c; - - if (c < 0x80) v = 0xff - c * 2; - else v = (c - 0x80) * 2; - amtable[c] = v & ~3; - } - } -} - -// --------------------------------------------------------------------------- - -inline void OPNABase::LFO() -{ -// LOG3("%4d - %8d, %8d\n", c, lfocount, lfodcount); - -// Operator::SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); -// Operator::SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); - chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); - chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); - lfocount += lfodcount; -} - -// --------------------------------------------------------------------------- -// ���� -// -#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) - -void OPNABase::Mix6(Sample* buffer, int nsamples, int activech) -{ - // Mix - // libOPNMIDI: rewrite for panning support - - const uint activechmask[6] = {0x001, 0x004, 0x010, 0x040, 0x100, 0x400}; - - Sample* limit = buffer + nsamples * 2; - for (Sample* dest = buffer; dest < limit; dest+=2) - { - ISample out[6]; - if (activech & 0xaaa) - { - LFO(); - for (uint c = 0; c<6; ++c) - out[c] = (activechmask[c] & activech) ? ch[c].CalcL() : 0; - } - else - { - for (uint c = 0; c<6; ++c) - out[c] = (activechmask[c] & activech) ? ch[c].Calc() : 0; - } - - int lrouts[2] = {0, 0}; - for (uint c = 0; c<6; ++c) - { - int panl = panvolume_l[c]; - int panr = panvolume_r[c]; - panl = (pan[c] & 2) ? panl : 0; - panr = (pan[c] & 1) ? panr : 0; - lrouts[0] += out[c] * panl / 65535; - lrouts[1] += out[c] * panr / 65535; - } - - StoreSample(dest[0], lrouts[0]); - StoreSample(dest[1], lrouts[1]); - } -} - -#endif // defined(BUILD_OPNA) || defined(BUILD_OPNB) - -// --------------------------------------------------------------------------- -// YM2608(OPNA) -// --------------------------------------------------------------------------- - -#ifdef BUILD_OPNA - -// --------------------------------------------------------------------------- -// �\�z -// -OPNA::OPNA() -{ - for (int i=0; i<6; i++) - { - rhythm[i].sample = 0; - rhythm[i].pos = 0; - rhythm[i].size = 0; - rhythm[i].volume = 0; - } - rhythmtvol = 0; - adpcmmask = 0x3ffff; - adpcmnotice = 4; - csmch = &ch[2]; -} - -// --------------------------------------------------------------------------- - -OPNA::~OPNA() -{ - delete[] adpcmbuf; - for (int i=0; i<6; i++) - delete[] rhythm[i].sample; -} - - - -// --------------------------------------------------------------------------- -// ������ -// -bool OPNA::Init(uint c, uint r, bool ipflag, const char* path) -{ - rate = 8000; - LoadRhythmSample(path); - - if (!adpcmbuf) - adpcmbuf = new uint8[0x40000]; - if (!adpcmbuf) - return false; - - if (!SetRate(c, r, ipflag)) - return false; - if (!OPNABase::Init(c, r, ipflag)) - return false; - - Reset(); - - SetVolumeADPCM(0); - SetVolumeRhythmTotal(0); - for (int i=0; i<6; i++) - SetVolumeRhythm(0, 0); - return true; -} - -// --------------------------------------------------------------------------- -// ���Z�b�g -// -void OPNA::Reset() -{ - reg29 = 0x1f; - rhythmkey = 0; - limitaddr = 0x3ffff; - OPNABase::Reset(); -} - -// --------------------------------------------------------------------------- -// �T���v�����O���[�g�ύX -// -bool OPNA::SetRate(uint c, uint r, bool ipflag) -{ - if (!OPNABase::SetRate(c, r, ipflag)) - return false; - - for (int i=0; i<6; i++) - { - rhythm[i].step = rhythm[i].rate * 1024 / r; - } - return true; -} - - -// --------------------------------------------------------------------------- -// ���Y������ǂ݂��� -// -bool OPNA::LoadRhythmSample(const char* path) -{ - static const char* rhythmname[6] = - { - "bd", "sd", "top", "hh", "tom", "rim", - }; - - int i; - for (i=0; i<6; i++) - rhythm[i].pos = ~0u; - - for (i=0; i<6; i++) - { - FileIO file; - uint32 fsize; - char buf[MAX_PATH + 1] = ""; - if (path) - strncpy(buf, path, MAX_PATH); - strncat(buf, "2608_", MAX_PATH); - strncat(buf, rhythmname[i], MAX_PATH); - strncat(buf, ".wav", MAX_PATH); - - if (!file.Open(buf, FileIO::readonly)) - { - if (i != 5) - break; - if (path) - strncpy(buf, path, MAX_PATH); - strncpy(buf, "2608_rym.wav", MAX_PATH); - if (!file.Open(buf, FileIO::readonly)) - break; - } - - struct - { - uint32 chunksize; - uint16 tag; - uint16 nch; - uint32 rate; - uint32 avgbytes; - uint16 align; - uint16 bps; - uint16 size; - } whdr; - - file.Seek(0x10, FileIO::begin); - file.Read(&whdr, sizeof(whdr)); - - uint8 subchunkname[4]; - fsize = 4 + whdr.chunksize - sizeof(whdr); - do - { - file.Seek(fsize, FileIO::current); - file.Read(&subchunkname, 4); - file.Read(&fsize, 4); - } while (memcmp("data", subchunkname, 4)); - - fsize /= 2; - if (fsize >= 0x100000 || whdr.tag != 1 || whdr.nch != 1) - break; - fsize = Max(fsize, (1<<31)/1024); - - if(!rhythm[i].sample) - delete rhythm[i].sample; - rhythm[i].sample = new int16[fsize]; - if (!rhythm[i].sample) - break; - - file.Read(rhythm[i].sample, fsize * 2); - - rhythm[i].rate = whdr.rate; - rhythm[i].step = rhythm[i].rate * 1024 / rate; - rhythm[i].pos = rhythm[i].size = fsize * 1024; - } - if (i != 6) - { - for (i=0; i<6; i++) - { - delete[] rhythm[i].sample; - rhythm[i].sample = 0; - } - return false; - } - return true; -} - - - -// --------------------------------------------------------------------------- -// ���W�X�^�A���C�Ƀf�[�^��ݒ� -// -void OPNA::SetReg(uint addr, uint data) -{ - addr &= 0x1ff; - - switch (addr) - { - case 0x29: - reg29 = data; -// UpdateStatus(); //? - break; - - // Rhythm ---------------------------------------------------------------- - case 0x10: // DM/KEYON - if (!(data & 0x80)) // KEY ON - { - rhythmkey |= data & 0x3f; - if (data & 0x01) rhythm[0].pos = 0; - if (data & 0x02) rhythm[1].pos = 0; - if (data & 0x04) rhythm[2].pos = 0; - if (data & 0x08) rhythm[3].pos = 0; - if (data & 0x10) rhythm[4].pos = 0; - if (data & 0x20) rhythm[5].pos = 0; - } - else - { // DUMP - rhythmkey &= ~data; - } - break; - - case 0x11: - rhythmtl = ~data & 63; - break; - - case 0x18: // Bass Drum - case 0x19: // Snare Drum - case 0x1a: // Top Cymbal - case 0x1b: // Hihat - case 0x1c: // Tom-tom - case 0x1d: // Rim shot - rhythm[addr & 7].pan = (data >> 6) & 3; - rhythm[addr & 7].level = ~data & 31; - break; - - case 0x100: case 0x101: - case 0x102: case 0x103: - case 0x104: case 0x105: - case 0x108: case 0x109: - case 0x10a: case 0x10b: - case 0x10c: case 0x10d: - case 0x110: - OPNABase::SetADPCMBReg(addr - 0x100, data); - break; - - default: - OPNABase::SetReg(addr, data); - break; - } -} - -// --------------------------------------------------------------------------- -void OPNA::DataSave(struct OPNAData* data) { - OPNABase::DataSave(&data->opnabase); - memcpy(data->rhythm, rhythm, sizeof(Rhythm) * 6); - data->rhythmtl = rhythmtl; - data->rhythmtvol = rhythmtvol; - data->rhythmkey = rhythmkey; -} - -// --------------------------------------------------------------------------- -void OPNA::DataLoad(struct OPNAData* data) { - OPNABase::DataLoad(&data->opnabase); - memcpy(rhythm, data->rhythm, sizeof(Rhythm) * 6); - rhythmtl = data->rhythmtl; - rhythmtvol = data->rhythmtvol; - rhythmkey = data->rhythmkey; - csmch = &ch[2]; -} - -// --------------------------------------------------------------------------- -// ���Y������ -// -void OPNA::RhythmMix(Sample* buffer, uint count) -{ - if (rhythmtvol < 128 && rhythm[0].sample && (rhythmkey & 0x3f)) - { - Sample* limit = buffer + count * 2; - for (int i=0; i<6; i++) - { - Rhythm& r = rhythm[i]; - if ((rhythmkey & (1 << i)) /*&& r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max - { - int db = Limit(rhythmtl+rhythmtvol+r.level+r.volume, 127, -31); - int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; - int maskl = -((r.pan >> 1) & 1); - int maskr = -(r.pan & 1); - - if (rhythmmask_ & (1 << i)) - { - maskl = maskr = 0; - } - - for (Sample* dest = buffer; dest> 12; - r.pos += r.step; - StoreSample(dest[0], sample & maskl); - StoreSample(dest[1], sample & maskr); - } - } - } - } -} - -// --------------------------------------------------------------------------- -// ���ʐݒ� -// -void OPNA::SetVolumeRhythmTotal(int db) -{ - db = Min(db, 20); - rhythmtvol = -(db * 2 / 3); -} - -void OPNA::SetVolumeRhythm(int index, int db) -{ - db = Min(db, 20); - rhythm[index].volume = -(db * 2 / 3); -} - -void OPNA::SetVolumeADPCM(int db) -{ - db = Min(db, 20); - if (db > -192) - adpcmvol = int(65536.0 * pow(10.0, db / 40.0)); - else - adpcmvol = 0; - - adpcmvolume = (adpcmvol * adpcmlevel) >> 12; -} - -// --------------------------------------------------------------------------- -// ���� -// in: buffer ������ -// nsamples �����T���v���� -// -void OPNA::Mix(Sample* buffer, int nsamples) -{ - FMMix(buffer, nsamples); - psg.Mix(buffer, nsamples); - ADPCMBMix(buffer, nsamples); - RhythmMix(buffer, nsamples); -} - -#endif // BUILD_OPNA - -// --------------------------------------------------------------------------- -// YM2610(OPNB) -// --------------------------------------------------------------------------- - -#ifdef BUILD_OPNB - -// --------------------------------------------------------------------------- -// �\�z -// -OPNB::OPNB() -{ - adpcmabuf = 0; - adpcmasize = 0; - for (int i=0; i<6; i++) - { - adpcma[i].pan = 0; - adpcma[i].level = 0; - adpcma[i].volume = 0; - adpcma[i].pos = 0; - adpcma[i].step = 0; - adpcma[i].volume = 0; - adpcma[i].start = 0; - adpcma[i].stop = 0; - adpcma[i].adpcmx = 0; - adpcma[i].adpcmd = 0; - } - adpcmatl = 0; - adpcmakey = 0; - adpcmatvol = 0; - adpcmmask = 0; - adpcmnotice = 0x8000; - granuality = -1; - csmch = &ch[2]; - - InitADPCMATable(); -} - -OPNB::~OPNB() -{ -} - -// --------------------------------------------------------------------------- -// ������ -// -bool OPNB::Init(uint c, uint r, bool ipflag, - uint8 *_adpcma, int _adpcma_size, - uint8 *_adpcmb, int _adpcmb_size) -{ - int i; - if (!SetRate(c, r, ipflag)) - return false; - if (!OPNABase::Init(c, r, ipflag)) - return false; - - adpcmabuf = _adpcma; - adpcmasize = _adpcma_size; - adpcmbuf = _adpcmb; - - for (i=0; i<=24; i++) // max 16M bytes - { - if (_adpcmb_size <= (1 << i)) - { - adpcmmask = (1 << i) - 1; - break; - } - } - -// adpcmmask = _adpcmb_size - 1; - limitaddr = adpcmmask; - - Reset(); - - SetVolumeFM(0); - SetVolumePSG(0); - SetVolumeADPCMB(0); - SetVolumeADPCMATotal(0); - for (i=0; i<6; i++) - SetVolumeADPCMA(0, 0); - SetChannelMask(0); - return true; -} - -// --------------------------------------------------------------------------- -// ���Z�b�g -// -void OPNB::Reset() -{ - OPNABase::Reset(); - - stmask = ~0; - adpcmakey = 0; - reg29 = ~0; - - for (int i=0; i<6; i++) - { - adpcma[i].pan = 0; - adpcma[i].level = 0; - adpcma[i].volume = 0; - adpcma[i].pos = 0; - adpcma[i].step = 0; - adpcma[i].volume = 0; - adpcma[i].start = 0; - adpcma[i].stop = 0; - adpcma[i].adpcmx = 0; - adpcma[i].adpcmd = 0; - } -} - -// --------------------------------------------------------------------------- -// �T���v�����O���[�g�ύX -// -bool OPNB::SetRate(uint c, uint r, bool ipflag) -{ - if (!OPNABase::SetRate(c, r, ipflag)) - return false; - - adpcmastep = int(double(c) / 54 * 8192 / r); - return true; -} - -// --------------------------------------------------------------------------- -// ���W�X�^�A���C�Ƀf�[�^��ݒ� -// -void OPNB::SetReg(uint addr, uint data) -{ - addr &= 0x1ff; - - switch (addr) - { - // omitted registers - case 0x29: - case 0x2d: case 0x2e: case 0x2f: - break; - - // ADPCM A --------------------------------------------------------------- - case 0x100: // DM/KEYON - if (!(data & 0x80)) // KEY ON - { - adpcmakey |= data & 0x3f; - for (int c=0; c<6; c++) - { - if (data & (1<> 6) & 3; - adpcma[addr & 7].level = ~data & 31; - break; - - case 0x110: case 0x111: case 0x112: // START ADDRESS (L) - case 0x113: case 0x114: case 0x115: - case 0x118: case 0x119: case 0x11a: // START ADDRESS (H) - case 0x11b: case 0x11c: case 0x11d: - adpcmareg[addr - 0x110] = data; - adpcma[addr & 7].pos = adpcma[addr & 7].start = - (adpcmareg[(addr&7)+8]*256+adpcmareg[addr&7]) << 9; - break; - - case 0x120: case 0x121: case 0x122: // END ADDRESS (L) - case 0x123: case 0x124: case 0x125: - case 0x128: case 0x129: case 0x12a: // END ADDRESS (H) - case 0x12b: case 0x12c: case 0x12d: - adpcmareg[addr - 0x110] = data; - adpcma[addr & 7].stop = - (adpcmareg[(addr&7)+24]*256+adpcmareg[(addr&7)+16] + 1) << 9; - break; - - // ADPCMB ----------------------------------------------------------------- - case 0x10: - if ((data & 0x80) && !adpcmplay) - { - adpcmplay = true; - memaddr = startaddr; - adpcmx = 0, adpcmd = 127; - adplc = 0; - } - if (data & 1) - adpcmplay = false; - control1 = data & 0x91; - break; - - - case 0x11: // Control Register 2 - control2 = data & 0xc0; - break; - - case 0x12: // Start Address L - case 0x13: // Start Address H - adpcmreg[addr - 0x12 + 0] = data; - startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 9; - memaddr = startaddr; - break; - - case 0x14: // Stop Address L - case 0x15: // Stop Address H - adpcmreg[addr - 0x14 + 2] = data; - stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 9; -// LOG1(" stopaddr %.6x", stopaddr); - break; - - case 0x19: // delta-N L - case 0x1a: // delta-N H - adpcmreg[addr - 0x19 + 4] = data; - deltan = adpcmreg[5]*256+adpcmreg[4]; - deltan = Max(256, deltan); - adpld = deltan * adplbase >> 16; - break; - - case 0x1b: // Level Control - adpcmlevel = data; - adpcmvolume = (adpcmvol * adpcmlevel) >> 12; - break; - - case 0x1c: // Flag Control - stmask = ~((data & 0xbf) << 8); - status &= stmask; - UpdateStatus(); - break; - - default: - OPNABase::SetReg(addr, data); - break; - } -// LOG0("\n"); -} - -// --------------------------------------------------------------------------- -// ���W�X�^�擾 -// -uint OPNB::GetReg(uint addr) -{ - if (addr < 0x10) - return psg.GetReg(addr); - - return 0; -} - -// --------------------------------------------------------------------------- -// �g���X�e�[�^�X��ǂ݂��� -// -uint OPNB::ReadStatusEx() -{ - return (status & stmask) >> 8; -} - -// --------------------------------------------------------------------------- -// YM2610 -// -int OPNB::jedi_table[(48+1)*16]; - -void OPNB::InitADPCMATable() -{ - const static int8 table2[] = - { - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9,-11,-13,-15, - }; - - for (int i=0; i<=48; i++) - { - int s = int(16.0 * pow (1.1, i) * 3); - for (int j=0; j<16; j++) - { - jedi_table[i*16+j] = s * table2[j] / 8; - } - } -} - -// --------------------------------------------------------------------------- -// ADPCMA ���� -// -void OPNB::ADPCMAMix(Sample* buffer, uint count) -{ - const static int decode_tableA1[16] = - { - -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16, - -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 - }; - - if (adpcmatvol < 128 && (adpcmakey & 0x3f)) - { - Sample* limit = buffer + count * 2; - for (int i=0; i<6; i++) - { - ADPCMA& r = adpcma[i]; - if ((adpcmakey & (1 << i))/* && r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max - { - uint maskl = r.pan & 2 ? -1 : 0; - uint maskr = r.pan & 1 ? -1 : 0; - if (rhythmmask_ & (1 << i)) - { - maskl = maskr = 0; - } - - int db = Limit(adpcmatl+adpcmatvol+r.level+r.volume, 127, -31); - int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; - - Sample* dest = buffer; - for ( ; dest= r.stop) - { - SetStatus(0x100 << i); - adpcmakey &= ~(1< 0x10000; r.step -= 0x10000) - { - int data; - if (!(r.pos & 1)) - { - r.nibble = adpcmabuf[r.pos>>1]; - data = r.nibble >> 4; - } - else - { - data = r.nibble & 0x0f; - } - r.pos++; - - r.adpcmx += jedi_table[r.adpcmd + data]; - r.adpcmx = Limit(r.adpcmx, 2048*3-1, -2048*3); - r.adpcmd += decode_tableA1[data]; - r.adpcmd = Limit(r.adpcmd, 48*16, 0); - } - int sample = (r.adpcmx * vol) >> 10; - StoreSample(dest[0], sample & maskl); - StoreSample(dest[1], sample & maskr); - } - } - } - } -} - -// --------------------------------------------------------------------------- -// ���ʐݒ� -// -void OPNB::SetVolumeADPCMATotal(int db) -{ - db = Min(db, 20); - adpcmatvol = -(db * 2 / 3); -} - -void OPNB::SetVolumeADPCMA(int index, int db) -{ - db = Min(db, 20); - adpcma[index].volume = -(db * 2 / 3); -} - -void OPNB::SetVolumeADPCMB(int db) -{ - db = Min(db, 20); - if (db > -192) - adpcmvol = int(65536.0 * pow(10, db / 40.0)); - else - adpcmvol = 0; -} - -// --------------------------------------------------------------------------- -void OPNB::DataSave(struct OPNBData* data, void* adpcmadata) { - OPNABase::DataSave(&data->opnabase); - if(adpcmasize) { - adpcmadata = malloc(adpcmasize); - memcpy(adpcmadata, adpcmabuf, adpcmasize); - } - data->adpcmasize = adpcmasize; - memcpy(data->adpcma, adpcma, sizeof(ADPCMA) * 6); - data->adpcmatl = adpcmatl; - data->adpcmatvol = adpcmatvol; - data->adpcmakey = adpcmakey; - data->adpcmastep = adpcmastep; - memcpy(data->adpcmareg, adpcmareg, 32); - for(int i = 0; i < 6; i++) { - ch[i].DataSave(&data->ch[i]); - } -} - -// --------------------------------------------------------------------------- -void OPNB::DataLoad(struct OPNBData* data, void* adpcmadata) { - OPNABase::DataLoad(&data->opnabase); // libOPNMIDI: bugfix DataLoad (was DataSave here) - if(data->adpcmasize) { - adpcmabuf = (uint8*)malloc(data->adpcmasize); - memcpy(adpcmabuf, adpcmadata, data->adpcmasize); - } - adpcmasize = data->adpcmasize; - memcpy(adpcma, data->adpcma, sizeof(ADPCMA) * 6); - adpcmatl = data->adpcmatl; - adpcmatvol = data->adpcmatvol; - adpcmakey = data->adpcmakey; - adpcmastep = data->adpcmastep; - memcpy(adpcmareg, data->adpcmareg, 32); - for(int i = 0; i < 6; i++) { - ch[i].DataLoad(&data->ch[i]); - } - csmch = &ch[2]; -} - -// --------------------------------------------------------------------------- -// ���� -// in: buffer ������ -// nsamples �����T���v���� -// -void OPNB::Mix(Sample* buffer, int nsamples) -{ - FMMix(buffer, nsamples); - psg.Mix(buffer, nsamples); - ADPCMBMix(buffer, nsamples); - ADPCMAMix(buffer, nsamples); -} - -#endif // BUILD_OPNB - -} // namespace FM +// --------------------------------------------------------------------------- +// OPN/A/B interface with ADPCM support +// Copyright (C) cisc 1998, 2001. +// --------------------------------------------------------------------------- +// $Id: opna.cpp,v 1.68 2003/06/12 14:03:44 cisc Exp $ + +#include "fmgen_types.h" +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_opna.h" +#include "fmgen_fmgeninl.h" + +#define BUILD_OPN +#define BUILD_OPNA +#define BUILD_OPNB + + +// TOFIX: +// OPN ch3 �����Prepare�̑ΏۂƂȂ��Ă��܂���Q + + +// --------------------------------------------------------------------------- +// OPNA: ADPCM �f�[�^�̊i�[�����̈Ⴂ (8bit/1bit) ���G�~�����[�g���Ȃ� +// ���̃I�v�V������L���ɂ���� ADPCM �������ւ̃A�N�Z�X(���� 8bit ���[�h)�� +// �����y���Ȃ邩�� +// +//#define NO_BITTYPE_EMULATION + +#ifdef BUILD_OPNA +#include "fmgen_file.h" +#endif + +namespace FM +{ + +// --------------------------------------------------------------------------- +// OPNBase + +#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) + +uint32 OPNBase::lfotable[8]; // OPNA/B �p + +OPNBase::OPNBase() +{ + prescale = 0; +} + +// �p�����[�^�Z�b�g +void OPNBase::SetParameter(Channel4* ch, uint addr, uint data) +{ + const static uint slottable[4] = { 0, 2, 1, 3 }; + const static uint8 sltable[16] = + { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 124, + }; + + if ((addr & 3) < 3) + { + uint slot = slottable[(addr >> 2) & 3]; + Operator* op = &ch->op[slot]; + + switch ((addr >> 4) & 15) + { + case 3: // 30-3E DT/MULTI + op->SetDT((data >> 4) & 0x07); + op->SetMULTI(data & 0x0f); + break; + + case 4: // 40-4E TL + op->SetTL(data & 0x7f, (regtc & 0x80) && (csmch == ch)); + break; + + case 5: // 50-5E KS/AR + op->SetKS((data >> 6) & 3); + op->SetAR((data & 0x1f) * 2); + break; + + case 6: // 60-6E DR/AMON + op->SetDR((data & 0x1f) * 2); + op->SetAMON((data & 0x80) != 0); + break; + + case 7: // 70-7E SR + op->SetSR((data & 0x1f) * 2); + break; + + case 8: // 80-8E SL/RR + op->SetSL(sltable[(data >> 4) & 15]); + op->SetRR((data & 0x0f) * 4 + 2); + break; + + case 9: // 90-9E SSG-EC + op->SetSSGEC(data & 0x0f); + break; + } + } +} + +// ���Z�b�g +void OPNBase::Reset() +{ + status = 0; + SetPrescaler(0); + Timer::Reset(); + psg.Reset(); +} + +// �v���X�P�[���ݒ� +void OPNBase::SetPrescaler(uint p) +{ + static const char table[3][2] = { { 6, 4 }, { 3, 2 }, { 2, 1 } }; + static const uint8 table2[8] = { 108, 77, 71, 67, 62, 44, 8, 5 }; + // 512 + if (prescale != p) + { + prescale = p; + /*warning: comparison is always true due to limited range of data type [-Wtype-limits]*/ + // assert(0 <= prescale && prescale < 3); + assert(prescale < 3); + + uint fmclock = clock / table[p][0] / 12; + + rate = psgrate; + + // �������g���Əo�͎��g���̔� + assert(fmclock < (0x80000000 >> FM_RATIOBITS)); + uint ratio = ((fmclock << FM_RATIOBITS) + rate/2) / rate; + + SetTimerBase(fmclock); +// MakeTimeTable(ratio); + chip.SetRatio(ratio); + psg.SetClock(clock / table[p][1], psgrate); + + for (int i=0; i<8; i++) + { + lfotable[i] = (ratio << (2+FM_LFOCBITS-FM_RATIOBITS)) / table2[i]; + } + } +} + +// ������ +bool OPNBase::Init(uint c, uint r) +{ + clock = c; + psgrate = r; + + return true; +} + +// ���ʐݒ� +void OPNBase::SetVolumeFM(int db) +{ + db = Min(db, 20); + if (db > -192) + fmvolume = int(16384.0 * pow(10.0, db / 40.0)); + else + fmvolume = 0; +} + +// �^�C�}�[���ԏ��� +void OPNBase::TimerA() +{ + if (regtc & 0x80) + { + csmch->KeyControl(0x00); + csmch->KeyControl(0x0f); + } +} + +void OPNBase::DataSave(struct OPNBaseData* data) { + Timer::DataSave(&data->timer); + data->fmvolume = fmvolume; + data->clock = clock; + data->rate = rate; + data->psgrate = psgrate; + data->status = status; + data->prescale = prescale; + chip.DataSave(&data->chip); + psg.DataSave(&data->psg); +} + +void OPNBase::DataLoad(struct OPNBaseData* data) { + Timer::DataLoad(&data->timer); + fmvolume = data->fmvolume; + clock = data->clock; + rate = data->rate; + psgrate = data->psgrate; + status = data->status; + prescale = data->prescale; + chip.DataLoad(&data->chip); + psg.DataLoad(&data->psg); +} + +#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) + +// --------------------------------------------------------------------------- +// YM2203 +// +#ifdef BUILD_OPN + +OPN::OPN() +{ + SetVolumeFM(0); + SetVolumePSG(0); + + csmch = &ch[2]; + + for (int i=0; i<3; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +// ������ +bool OPN::Init(uint c, uint r, bool ip, const char*) +{ + if (!SetRate(c, r, ip)) + return false; + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetChannelMask(0); + return true; +} + +// �T���v�����O���[�g�ύX +bool OPN::SetRate(uint c, uint r, bool) +{ + OPNBase::Init(c, r); + RebuildTimeTable(); + return true; +} + + +// ���Z�b�g +void OPN::Reset() +{ + int i; + for (i=0x20; i<0x28; i++) SetReg(i, 0); + for (i=0x30; i<0xc0; i++) SetReg(i, 0); + OPNBase::Reset(); + ch[0].Reset(); + ch[1].Reset(); + ch[2].Reset(); +} + + +// ���W�X�^�ǂݍ��� +uint OPN::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + else + return 0; +} + + +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +void OPN::SetReg(uint addr, uint data) +{ +// LOG2("reg[%.2x] <- %.2x\n", addr, data); + if (addr >= 0x100) + return; + + int c = addr & 3; + switch (addr) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + psg.SetReg(addr, data); + break; + + case 0x24: case 0x25: + SetTimerA(addr, data); + break; + + case 0x26: + SetTimerB(data); + break; + + case 0x27: + SetTimerControl(data); + break; + + case 0x28: // Key On/Off + if ((data & 3) < 3) + ch[data & 3].KeyControl(data >> 4); + break; + + case 0x2d: case 0x2e: case 0x2f: + SetPrescaler(addr-0x2d); + break; + + // F-Number + case 0xa0: case 0xa1: case 0xa2: + fnum[c] = data + fnum2[c] * 0x100; + break; + + case 0xa4: case 0xa5: case 0xa6: + fnum2[c] = uint8(data); + break; + + case 0xa8: case 0xa9: case 0xaa: + fnum3[c] = data + fnum2[c+3] * 0x100; + break; + + case 0xac: case 0xad: case 0xae: + fnum2[c+3] = uint8(data); + break; + + case 0xb0: case 0xb1: case 0xb2: + ch[c].SetFB((data >> 3) & 7); + ch[c].SetAlgorithm(data & 7); + break; + + default: + if (c < 3) + { + if ((addr & 0xf0) == 0x60) + data &= 0x1f; + OPNBase::SetParameter(&ch[c], addr, data); + } + break; + } +} + +// �X�e�[�^�X�t���O�ݒ� +void OPN::SetStatus(uint bits) +{ + if (!(status & bits)) + { + status |= bits; + Intr(true); + } +} + +void OPN::ResetStatus(uint bit) +{ + status &= ~bit; + if (!status) + Intr(false); +} + +// �}�X�N�ݒ� +void OPN::SetChannelMask(uint mask) +{ + for (int i=0; i<3; i++) + ch[i].Mute(!!(mask & (1 << i))); + psg.SetChannelMask(mask >> 6); +} + +void OPN::DataSave(struct OPNData* data) { + OPNBase::DataSave(&data->opnbase); + memcpy(data->fnum, fnum, sizeof(uint) * 3); + memcpy(data->fnum3, fnum3, sizeof(uint) * 3); + memcpy(data->fnum2, fnum2, 6); + for(int i = 0; i < 3; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +void OPN::DataLoad(struct OPNData* data) { + OPNBase::DataLoad(&data->opnbase); + memcpy(fnum, data->fnum, sizeof(uint) * 3); + memcpy(fnum3, data->fnum3, sizeof(uint) * 3); + memcpy(fnum2, data->fnum2, 6); + for(int i = 0; i < 3; i++) { + ch[i].DataLoad(&data->ch[i]); + } + csmch = &ch[2]; + for (int i=0; i<3; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +// ����(2ch) +void OPN::Mix(Sample* buffer, int nsamples) +{ +//printf("M:%d\n",nsamples); +#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) + + psg.Mix(buffer, nsamples); + + // Set F-Number + ch[0].SetFNum(fnum[0]); + ch[1].SetFNum(fnum[1]); + if (!(regtc & 0xc0)) + ch[2].SetFNum(fnum[2]); + else + { // ���ʉ� + ch[2].op[0].SetFNum(fnum3[1]); + ch[2].op[1].SetFNum(fnum3[2]); + ch[2].op[2].SetFNum(fnum3[0]); + ch[2].op[3].SetFNum(fnum[2]); + } + + int actch = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); +//printf("a %X\n",actch); + if (actch & 0x15) + { + Sample* limit = buffer + nsamples * 2; + for (Sample* dest = buffer; dest < limit; dest+=2) + { + ISample s = 0; + if (actch & 0x01) s = ch[0].Calc(); + if (actch & 0x04) s += ch[1].Calc(); + if (actch & 0x10) s += ch[2].Calc(); + s = IStoSample(s); + StoreSample(dest[0], s); + StoreSample(dest[1], s); +//printf("%08X,%08X\n",dest[0],dest[1]); + } + } +#undef IStoSample +} + +#endif // BUILD_OPN + +// --------------------------------------------------------------------------- +// YM2608/2610 common part +// --------------------------------------------------------------------------- + +#if defined(BUILD_OPNA) || defined(BUILD_OPNB) + +int OPNABase::amtable[FM_LFOENTS] = { -1, }; +int OPNABase::pmtable[FM_LFOENTS]; + +int32 OPNABase::tltable[FM_TLENTS+FM_TLPOS]; +bool OPNABase::tablehasmade = false; + +OPNABase::OPNABase() +{ + adpcmbuf = 0; + memaddr = 0; + startaddr = 0; + deltan = 256; + + adpcmvol = 0; + control2 = 0; + + MakeTable2(); + BuildLFOTable(); + for (int i=0; i<6; i++) + { + ch[i].SetChip(&chip); + ch[i].SetType(typeN); + } +} + +OPNABase::~OPNABase() +{ +} + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNABase::Init(uint, uint, bool) +{ + RebuildTimeTable(); + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetChannelMask(0); + return true; +} + +// --------------------------------------------------------------------------- +// �e�[�u���쐬 +// +void OPNABase::MakeTable2() +{ + if (!tablehasmade) + { + for (int i=-FM_TLPOS; i> 16; + + RebuildTimeTable(); + + lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0; + return true; +} + + +// --------------------------------------------------------------------------- +// �`�����l���}�X�N�̐ݒ� +// +void OPNABase::SetChannelMask(uint mask) +{ + for (int i=0; i<6; i++) + ch[i].Mute(!!(mask & (1 << i))); + psg.SetChannelMask(mask >> 6); + adpcmmask_ = (mask & (1 << 9)) != 0; + rhythmmask_ = (mask >> 10) & ((1 << 6) - 1); +} + +void OPNABase::SetPan(uint c, uint8 p) +{ + panvolume_l[c] = panlawtable[p & 0x7f]; + panvolume_r[c] = panlawtable[0x7f - (p & 0x7f)]; +} + +// --------------------------------------------------------------------------- +void OPNABase::DataSave(struct OPNABaseData* data) { + OPNBase::DataSave(&data->opnbase); + memcpy(data->pan, pan, 6); + memcpy(data->panvolume_l, panvolume_l, sizeof(uint16) * 6); + memcpy(data->panvolume_r, panvolume_r, sizeof(uint16) * 6); + memcpy(data->fnum2, fnum2, 9); + data->reg22 = reg22; + data->reg29 = reg29; + data->stmask = stmask; + data->statusnext = statusnext; + data->lfocount = lfocount; + data->lfodcount = lfodcount; + memcpy(data->fnum, fnum, sizeof(uint) * 6); + memcpy(data->fnum3, fnum3, sizeof(uint) * 3); + data->is_adpcmbuf = 0; + if(adpcmbuf) { + data->is_adpcmbuf = 1; + memcpy(data->adpcmbuf, adpcmbuf, 0x40000); + } + data->adpcmmask = adpcmmask; + data->adpcmnotice = adpcmnotice; + data->startaddr = startaddr; + data->stopaddr = stopaddr; + data->memaddr = memaddr; + data->limitaddr = limitaddr; + data->adpcmlevel = adpcmlevel; + data->adpcmvolume = adpcmvolume; + data->adpcmvol = adpcmvol; + data->deltan = deltan; + data->adplc = adplc; + data->adpld = adpld; + data->adplbase = adplbase; + data->adpcmx = adpcmx; + data->adpcmd = adpcmd; + data->adpcmout = adpcmout; + data->apout0 = apout0; + data->apout1 = apout1; + data->adpcmreadbuf = adpcmreadbuf; + data->adpcmplay = adpcmplay; + data->granuality = granuality; + data->control1 = control1; + data->control2 = control2; + memcpy(data->adpcmreg, adpcmreg, 8); + data->rhythmmask_ = rhythmmask_; + for(int i = 0; i < 6; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +// --------------------------------------------------------------------------- +void OPNABase::DataLoad(struct OPNABaseData* data) { + OPNBase::DataLoad(&data->opnbase); + memcpy(pan, data->pan, 6); + memcpy(panvolume_l, data->panvolume_l, sizeof(uint16) * 6); + memcpy(panvolume_r, data->panvolume_r, sizeof(uint16) * 6); + memcpy(fnum2, data->fnum2, 9); + reg22 = data->reg22; + reg29 = data->reg29; + stmask = data->stmask; + statusnext = data->statusnext; + lfocount = data->lfocount; + lfodcount = data->lfodcount; + memcpy(fnum, data->fnum, sizeof(uint) * 6); + memcpy(fnum3, data->fnum3, sizeof(uint) * 3); + if(data->is_adpcmbuf) { + if(!adpcmbuf) + adpcmbuf = new uint8[0x40000]; + memcpy(adpcmbuf, data->adpcmbuf, 0x40000); + } + adpcmmask = data->adpcmmask; + adpcmnotice = data->adpcmnotice; + startaddr = data->startaddr; + stopaddr = data->stopaddr; + memaddr = data->memaddr; + limitaddr = data->limitaddr; + adpcmlevel = data->adpcmlevel; + adpcmvolume = data->adpcmvolume; + adpcmvol = data->adpcmvol; + deltan = data->deltan; + adplc = data->adplc; + adpld = data->adpld; + adplbase = data->adplbase; + adpcmx = data->adpcmx; + adpcmd = data->adpcmd; + adpcmout = data->adpcmout; + apout0 = data->apout0; + apout1 = data->apout1; + adpcmreadbuf = data->adpcmreadbuf; + adpcmplay = data->adpcmplay; + granuality = data->granuality; + control1 = data->control1; + control2 = data->control2; + memcpy(adpcmreg, data->adpcmreg, 8); + rhythmmask_ = data->rhythmmask_; + for(int i = 0; i < 6; i++) { + ch[i].DataLoad(&data->ch[i]); + ch[i].SetChip(&chip); + } +} + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNABase::SetReg(uint addr, uint data) +{ + int c = addr & 3; + switch (addr) + { + uint modified; + + // Timer ----------------------------------------------------------------- + case 0x24: case 0x25: + SetTimerA(addr, data); + break; + + case 0x26: + SetTimerB(data); + break; + + case 0x27: + SetTimerControl(data); + break; + + // Misc ------------------------------------------------------------------ + case 0x28: // Key On/Off + if ((data & 3) < 3) + { + c = (data & 3) + (data & 4 ? 3 : 0); + ch[c].KeyControl(data >> 4); + } + break; + + // Status Mask ----------------------------------------------------------- + case 0x29: + reg29 = data; +// UpdateStatus(); //? + break; + + // Prescaler ------------------------------------------------------------- + case 0x2d: case 0x2e: case 0x2f: + SetPrescaler(addr-0x2d); + break; + + // F-Number -------------------------------------------------------------- + case 0x1a0: case 0x1a1: case 0x1a2: + c += 3; + // fall through + case 0xa0: case 0xa1: case 0xa2: + fnum[c] = data + fnum2[c] * 0x100; + ch[c].SetFNum(fnum[c]); + break; + + case 0x1a4: case 0x1a5: case 0x1a6: + c += 3; + // fall through + case 0xa4 : case 0xa5: case 0xa6: + fnum2[c] = uint8(data); + break; + + case 0xa8: case 0xa9: case 0xaa: + fnum3[c] = data + fnum2[c+6] * 0x100; + break; + + case 0xac : case 0xad: case 0xae: + fnum2[c+6] = uint8(data); + break; + + // Algorithm ------------------------------------------------------------- + + case 0x1b0: case 0x1b1: case 0x1b2: + c += 3; + // fall through + case 0xb0: case 0xb1: case 0xb2: + ch[c].SetFB((data >> 3) & 7); + ch[c].SetAlgorithm(data & 7); + break; + + case 0x1b4: case 0x1b5: case 0x1b6: + c += 3; + // fall through + case 0xb4: case 0xb5: case 0xb6: + pan[c] = (data >> 6) & 3; + ch[c].SetMS(data); + break; + + // LFO ------------------------------------------------------------------- + case 0x22: + modified = reg22 ^ data; + reg22 = data; + if (modified & 0x8) + lfocount = 0; + lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0; + break; + + // PSG ------------------------------------------------------------------- + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + psg.SetReg(addr, data); + break; + + // ���F ------------------------------------------------------------------ + default: + if (c < 3) + { + if (addr & 0x100) + c += 3; + OPNBase::SetParameter(&ch[c], addr, data); + } + break; + } +} + +// --------------------------------------------------------------------------- +// ADPCM B +// +void OPNABase::SetADPCMBReg(uint addr, uint data) +{ + switch (addr) + { + case 0x00: // Control Register 1 + if ((data & 0x80) && !adpcmplay) + { + adpcmplay = true; + memaddr = startaddr; + adpcmx = 0, adpcmd = 127; + adplc = 0; + } + if (data & 1) + { + adpcmplay = false; + } + control1 = data; + break; + + case 0x01: // Control Register 2 + control2 = data; + granuality = control2 & 2 ? 1 : 4; + break; + + case 0x02: // Start Address L + case 0x03: // Start Address H + adpcmreg[addr - 0x02 + 0] = data; + startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 6; + memaddr = startaddr; +// LOG1(" startaddr %.6x", startaddr); + break; + + case 0x04: // Stop Address L + case 0x05: // Stop Address H + adpcmreg[addr - 0x04 + 2] = data; + stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 6; +// LOG1(" stopaddr %.6x", stopaddr); + break; + + case 0x08: // ADPCM data + if ((control1 & 0x60) == 0x60) + { +// LOG2(" Wr [0x%.5x] = %.2x", memaddr, data); + WriteRAM(data); + } + break; + + case 0x09: // delta-N L + case 0x0a: // delta-N H + adpcmreg[addr - 0x09 + 4] = data; + deltan = adpcmreg[5]*256+adpcmreg[4]; + deltan = Max(256, deltan); + adpld = deltan * adplbase >> 16; + break; + + case 0x0b: // Level Control + adpcmlevel = data; + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; + break; + + case 0x0c: // Limit Address L + case 0x0d: // Limit Address H + adpcmreg[addr - 0x0c + 6] = data; + limitaddr = (adpcmreg[7]*256+adpcmreg[6] + 1) << 6; +// LOG1(" limitaddr %.6x", limitaddr); + break; + + case 0x10: // Flag Control + if (data & 0x80) + { + status = 0; + UpdateStatus(); + } + else + { + stmask = ~(data & 0x1f); +// UpdateStatus(); //??? + } + break; + } +} + + +// --------------------------------------------------------------------------- +// ���W�X�^�擾 +// +uint OPNA::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + + if (addr == 0x108) + { +// LOG1("%d:reg[108] -> ", Diag::GetCPUTick()); + + uint data = adpcmreadbuf & 0xff; + adpcmreadbuf >>= 8; + if ((control1 & 0x60) == 0x20) + { + adpcmreadbuf |= ReadRAM() << 8; +// LOG2("Rd [0x%.6x:%.2x] ", memaddr, adpcmreadbuf >> 8); + } +// LOG0("%.2x\n"); + return data; + } + + if (addr == 0xff) + return 1; + + return 0; +} + + + + +// --------------------------------------------------------------------------- +// �X�e�[�^�X�t���O�ݒ� +// +void OPNABase::SetStatus(uint bits) +{ + if (!(status & bits)) + { +// LOG2("SetStatus(%.2x %.2x)\n", bits, stmask); + status |= bits & stmask; + UpdateStatus(); + } +// else +// LOG1("SetStatus(%.2x) - ignored\n", bits); +} + +void OPNABase::ResetStatus(uint bits) +{ + status &= ~bits; +// LOG1("ResetStatus(%.2x)\n", bits); + UpdateStatus(); +} + +inline void OPNABase::UpdateStatus() +{ +// LOG2("%d:INT = %d\n", Diag::GetCPUTick(), (status & stmask & reg29) != 0); + Intr((status & stmask & reg29) != 0); +} + +// --------------------------------------------------------------------------- +// ADPCM RAM �ւ̏����ݑ��� +// +void OPNABase::WriteRAM(uint data) +{ +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + // 1 bit mode + adpcmbuf[(memaddr >> 4) & 0x3ffff] = data; + memaddr += 16; + } + else + { + // 8 bit mode + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + data <<= bank; + + p[0x00000] = (p[0x00000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x08000] = (p[0x08000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x10000] = (p[0x10000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x18000] = (p[0x18000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x20000] = (p[0x20000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x28000] = (p[0x28000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x30000] = (p[0x30000] & ~mask) | (uint8(data) & mask); data >>= 1; + p[0x38000] = (p[0x38000] & ~mask) | (uint8(data) & mask); + memaddr += 2; + } +#else + adpcmbuf[(memaddr >> granuality) & 0x3ffff] = data; + memaddr += 1 << granuality; +#endif + + if (memaddr == stopaddr) + { + SetStatus(4); + statusnext = 0x04; // EOS + memaddr &= 0x3fffff; + } + if (memaddr == limitaddr) + { +// LOG1("Limit ! (%.8x)\n", limitaddr); + memaddr = 0; + } + SetStatus(8); +} + +// --------------------------------------------------------------------------- +// ADPCM RAM ����̓ǂݍ��ݑ��� +// +uint OPNABase::ReadRAM() +{ + uint data; +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + // 1 bit mode + data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; + memaddr += 16; + } + else + { + // 8 bit mode + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff]; + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + + data = (p[0x38000] & mask); + data = data * 2 + (p[0x30000] & mask); + data = data * 2 + (p[0x28000] & mask); + data = data * 2 + (p[0x20000] & mask); + data = data * 2 + (p[0x18000] & mask); + data = data * 2 + (p[0x10000] & mask); + data = data * 2 + (p[0x08000] & mask); + data = data * 2 + (p[0x00000] & mask); + data >>= bank; + memaddr += 2; + } +#else + data = adpcmbuf[(memaddr >> granuality) & 0x3ffff]; + memaddr += 1 << granuality; +#endif + if (memaddr == stopaddr) + { + SetStatus(4); + statusnext = 0x04; // EOS + memaddr &= 0x3fffff; + } + if (memaddr == limitaddr) + { +// LOG1("Limit ! (%.8x)\n", limitaddr); + memaddr = 0; + } + if (memaddr < stopaddr) + SetStatus(8); + return data; +} + + +inline int OPNABase::DecodeADPCMBSample(uint data) +{ + static const int table1[16] = + { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, + }; + static const int table2[16] = + { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153, + }; + adpcmx = Limit(adpcmx + table1[data] * adpcmd / 8, 32767, -32768); + adpcmd = Limit(adpcmd * table2[data] / 64, 24576, 127); + return adpcmx; +} + + +// --------------------------------------------------------------------------- +// ADPCM RAM ����� nibble �ǂݍ��݋y�� ADPCM �W�J +// +int OPNABase::ReadRAMN() +{ + uint data; + if (granuality > 0) + { +#ifndef NO_BITTYPE_EMULATION + if (!(control2 & 2)) + { + data = adpcmbuf[(memaddr >> 4) & 0x3ffff]; + memaddr += 8; + if (memaddr & 8) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; + } + else + { + uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff] + ((~memaddr & 1) << 17); + uint bank = (memaddr >> 1) & 7; + uint8 mask = 1 << bank; + + data = (p[0x18000] & mask); + data = data * 2 + (p[0x10000] & mask); + data = data * 2 + (p[0x08000] & mask); + data = data * 2 + (p[0x00000] & mask); + data >>= bank; + memaddr ++; + if (memaddr & 1) + return DecodeADPCMBSample(data); + } +#else + data = adpcmbuf[(memaddr >> granuality) & adpcmmask]; + memaddr += 1 << (granuality-1); + if (memaddr & (1 << (granuality-1))) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; +#endif + } + else + { + data = adpcmbuf[(memaddr >> 1) & adpcmmask]; + ++memaddr; + if (memaddr & 1) + return DecodeADPCMBSample(data >> 4); + data &= 0x0f; + } + + DecodeADPCMBSample(data); + + // check + if (memaddr == stopaddr) + { + if (control1 & 0x10) + { + memaddr = startaddr; + data = adpcmx; + adpcmx = 0, adpcmd = 127; + return data; + } + else + { + memaddr &= adpcmmask; //0x3fffff; + SetStatus(adpcmnotice); + adpcmplay = false; + } + } + + if (memaddr == limitaddr) + memaddr = 0; + + return adpcmx; +} + +// --------------------------------------------------------------------------- +// �g���X�e�[�^�X��ǂ݂��� +// +uint OPNABase::ReadStatusEx() +{ + uint r = ((status | 8) & stmask) | (adpcmplay ? 0x20 : 0); + status |= statusnext; + statusnext = 0; + return r; +} + +// --------------------------------------------------------------------------- +// ADPCM �W�J +// +inline void OPNABase::DecodeADPCMB() +{ + apout0 = apout1; + int n = (ReadRAMN() * adpcmvolume) >> 13; + apout1 = adpcmout + n; + adpcmout = n; +} + +// --------------------------------------------------------------------------- +// ADPCM ���� +// +void OPNABase::ADPCMBMix(Sample* dest, uint count) +{ + uint maskl = control2 & 0x80 ? -1 : 0; + uint maskr = control2 & 0x40 ? -1 : 0; + if (adpcmmask_) + { + maskl = maskr = 0; + } + + if (adpcmplay) + { +// LOG2("ADPCM Play: %d DeltaN: %d\n", adpld, deltan); + if (adpld <= 8192) // fplay < fsamp + { + for (; count>0; count--) + { + if (adplc < 0) + { + adplc += 8192; + DecodeADPCMB(); + if (!adpcmplay) + break; + } + int s = (adplc * apout0 + (8192-adplc) * apout1) >> 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + adplc -= adpld; + } + for (; count>0 && apout0; count--) + { + if (adplc < 0) + { + apout0 = apout1, apout1 = 0; + adplc += 8192; + } + int s = (adplc * apout1) >> 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + adplc -= adpld; + } + } + else // fplay > fsamp (adpld = fplay/famp*8192) + { + int t = (-8192*8192)/adpld; + for (; count>0; count--) + { + int s = apout0 * (8192+adplc); + while (adplc < 0) + { + DecodeADPCMB(); + if (!adpcmplay) + goto stop; + s -= apout0 * Max(adplc, t); + adplc -= t; + } + adplc -= 8192; + s >>= 13; + StoreSample(dest[0], s & maskl); + StoreSample(dest[1], s & maskr); + dest += 2; + } +stop: + ; + } + } + if (!adpcmplay) + { + apout0 = apout1 = adpcmout = 0; + adplc = 0; + } +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNABase::FMMix(Sample* buffer, int nsamples) +{ + if (fmvolume > 0) + { + // ���� + // Set F-Number + if (!(regtc & 0xc0)) + csmch->SetFNum(fnum[csmch-ch]); + else + { + // ���ʉ����[�h + csmch->op[0].SetFNum(fnum3[1]); csmch->op[1].SetFNum(fnum3[2]); + csmch->op[2].SetFNum(fnum3[0]); csmch->op[3].SetFNum(fnum[2]); + } + + int act = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare(); + if (reg29 & 0x80) + act |= (ch[3].Prepare() | ((ch[4].Prepare() | (ch[5].Prepare() << 2)) << 2)) << 6; + if (!(reg22 & 0x08)) + act &= 0x555; + + if (act & 0x555) + { + Mix6(buffer, nsamples, act); + } + } +} + +// --------------------------------------------------------------------------- + +void OPNABase::MixSubSL(int activech, ISample** dest) +{ + if (activech & 0x001) (*dest[0] = ch[0].CalcL()); + if (activech & 0x004) (*dest[1] += ch[1].CalcL()); + if (activech & 0x010) (*dest[2] += ch[2].CalcL()); + if (activech & 0x040) (*dest[3] += ch[3].CalcL()); + if (activech & 0x100) (*dest[4] += ch[4].CalcL()); + if (activech & 0x400) (*dest[5] += ch[5].CalcL()); +} + +inline void OPNABase::MixSubS(int activech, ISample** dest) +{ + if (activech & 0x001) (*dest[0] = ch[0].Calc()); + if (activech & 0x004) (*dest[1] += ch[1].Calc()); + if (activech & 0x010) (*dest[2] += ch[2].Calc()); + if (activech & 0x040) (*dest[3] += ch[3].Calc()); + if (activech & 0x100) (*dest[4] += ch[4].Calc()); + if (activech & 0x400) (*dest[5] += ch[5].Calc()); +} + +// --------------------------------------------------------------------------- + +void OPNABase::BuildLFOTable() +{ + if (amtable[0] == -1) + { + for (int c=0; c<256; c++) + { + int v; + if (c < 0x40) v = c * 2 + 0x80; + else if (c < 0xc0) v = 0x7f - (c - 0x40) * 2 + 0x80; + else v = (c - 0xc0) * 2; + pmtable[c] = c; + + if (c < 0x80) v = 0xff - c * 2; + else v = (c - 0x80) * 2; + amtable[c] = v & ~3; + } + } +} + +// --------------------------------------------------------------------------- + +inline void OPNABase::LFO() +{ +// LOG3("%4d - %8d, %8d\n", c, lfocount, lfodcount); + +// Operator::SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); +// Operator::SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]); + lfocount += lfodcount; +} + +// --------------------------------------------------------------------------- +// ���� +// +#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14) + +void OPNABase::Mix6(Sample* buffer, int nsamples, int activech) +{ + // Mix + // libOPNMIDI: rewrite for panning support + + const uint activechmask[6] = {0x001, 0x004, 0x010, 0x040, 0x100, 0x400}; + + Sample* limit = buffer + nsamples * 2; + for (Sample* dest = buffer; dest < limit; dest+=2) + { + ISample out[6]; + if (activech & 0xaaa) + { + LFO(); + for (uint c = 0; c<6; ++c) + out[c] = (activechmask[c] & activech) ? ch[c].CalcL() : 0; + } + else + { + for (uint c = 0; c<6; ++c) + out[c] = (activechmask[c] & activech) ? ch[c].Calc() : 0; + } + + int lrouts[2] = {0, 0}; + for (uint c = 0; c<6; ++c) + { + int panl = panvolume_l[c]; + int panr = panvolume_r[c]; + panl = (pan[c] & 2) ? panl : 0; + panr = (pan[c] & 1) ? panr : 0; + lrouts[0] += out[c] * panl / 65535; + lrouts[1] += out[c] * panr / 65535; + } + + StoreSample(dest[0], lrouts[0]); + StoreSample(dest[1], lrouts[1]); + } +} + +#endif // defined(BUILD_OPNA) || defined(BUILD_OPNB) + +// --------------------------------------------------------------------------- +// YM2608(OPNA) +// --------------------------------------------------------------------------- + +#ifdef BUILD_OPNA + +// --------------------------------------------------------------------------- +// �\�z +// +OPNA::OPNA() +{ + for (int i=0; i<6; i++) + { + rhythm[i].sample = 0; + rhythm[i].pos = 0; + rhythm[i].size = 0; + rhythm[i].volume = 0; + } + rhythmtvol = 0; + adpcmmask = 0x3ffff; + adpcmnotice = 4; + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- + +OPNA::~OPNA() +{ + delete[] adpcmbuf; + for (int i=0; i<6; i++) + delete[] rhythm[i].sample; +} + + + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNA::Init(uint c, uint r, bool ipflag, const char* path) +{ + rate = 8000; + LoadRhythmSample(path); + + if (!adpcmbuf) + adpcmbuf = new uint8[0x40000]; + if (!adpcmbuf) + return false; + + if (!SetRate(c, r, ipflag)) + return false; + if (!OPNABase::Init(c, r, ipflag)) + return false; + + Reset(); + + SetVolumeADPCM(0); + SetVolumeRhythmTotal(0); + for (int i=0; i<6; i++) + SetVolumeRhythm(0, 0); + return true; +} + +// --------------------------------------------------------------------------- +// ���Z�b�g +// +void OPNA::Reset() +{ + reg29 = 0x1f; + rhythmkey = 0; + limitaddr = 0x3ffff; + OPNABase::Reset(); +} + +// --------------------------------------------------------------------------- +// �T���v�����O���[�g�ύX +// +bool OPNA::SetRate(uint c, uint r, bool ipflag) +{ + if (!OPNABase::SetRate(c, r, ipflag)) + return false; + + for (int i=0; i<6; i++) + { + rhythm[i].step = rhythm[i].rate * 1024 / r; + } + return true; +} + + +// --------------------------------------------------------------------------- +// ���Y������ǂ݂��� +// +bool OPNA::LoadRhythmSample(const char* path) +{ + static const char* rhythmname[6] = + { + "bd", "sd", "top", "hh", "tom", "rim", + }; + + int i; + for (i=0; i<6; i++) + rhythm[i].pos = ~0u; + + for (i=0; i<6; i++) + { + FileIO file; + uint32 fsize; + char buf[MAX_PATH + 1] = ""; + if (path) + strncpy(buf, path, MAX_PATH); + strncat(buf, "2608_", MAX_PATH); + strncat(buf, rhythmname[i], MAX_PATH); + strncat(buf, ".wav", MAX_PATH); + + if (!file.Open(buf, FileIO::readonly)) + { + if (i != 5) + break; + if (path) + strncpy(buf, path, MAX_PATH); + strncpy(buf, "2608_rym.wav", MAX_PATH); + if (!file.Open(buf, FileIO::readonly)) + break; + } + + struct + { + uint32 chunksize; + uint16 tag; + uint16 nch; + uint32 rate; + uint32 avgbytes; + uint16 align; + uint16 bps; + uint16 size; + } whdr; + + file.Seek(0x10, FileIO::begin); + file.Read(&whdr, sizeof(whdr)); + + uint8 subchunkname[4]; + fsize = 4 + whdr.chunksize - sizeof(whdr); + do + { + file.Seek(fsize, FileIO::current); + file.Read(&subchunkname, 4); + file.Read(&fsize, 4); + } while (memcmp("data", subchunkname, 4)); + + fsize /= 2; + if (fsize >= 0x100000 || whdr.tag != 1 || whdr.nch != 1) + break; + fsize = Max(fsize, (1<<31)/1024); + + if(!rhythm[i].sample) + delete rhythm[i].sample; + rhythm[i].sample = new int16[fsize]; + if (!rhythm[i].sample) + break; + + file.Read(rhythm[i].sample, fsize * 2); + + rhythm[i].rate = whdr.rate; + rhythm[i].step = rhythm[i].rate * 1024 / rate; + rhythm[i].pos = rhythm[i].size = fsize * 1024; + } + if (i != 6) + { + for (i=0; i<6; i++) + { + delete[] rhythm[i].sample; + rhythm[i].sample = 0; + } + return false; + } + return true; +} + + + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNA::SetReg(uint addr, uint data) +{ + addr &= 0x1ff; + + switch (addr) + { + case 0x29: + reg29 = data; +// UpdateStatus(); //? + break; + + // Rhythm ---------------------------------------------------------------- + case 0x10: // DM/KEYON + if (!(data & 0x80)) // KEY ON + { + rhythmkey |= data & 0x3f; + if (data & 0x01) rhythm[0].pos = 0; + if (data & 0x02) rhythm[1].pos = 0; + if (data & 0x04) rhythm[2].pos = 0; + if (data & 0x08) rhythm[3].pos = 0; + if (data & 0x10) rhythm[4].pos = 0; + if (data & 0x20) rhythm[5].pos = 0; + } + else + { // DUMP + rhythmkey &= ~data; + } + break; + + case 0x11: + rhythmtl = ~data & 63; + break; + + case 0x18: // Bass Drum + case 0x19: // Snare Drum + case 0x1a: // Top Cymbal + case 0x1b: // Hihat + case 0x1c: // Tom-tom + case 0x1d: // Rim shot + rhythm[addr & 7].pan = (data >> 6) & 3; + rhythm[addr & 7].level = ~data & 31; + break; + + case 0x100: case 0x101: + case 0x102: case 0x103: + case 0x104: case 0x105: + case 0x108: case 0x109: + case 0x10a: case 0x10b: + case 0x10c: case 0x10d: + case 0x110: + OPNABase::SetADPCMBReg(addr - 0x100, data); + break; + + default: + OPNABase::SetReg(addr, data); + break; + } +} + +// --------------------------------------------------------------------------- +void OPNA::DataSave(struct OPNAData* data) { + OPNABase::DataSave(&data->opnabase); + memcpy(data->rhythm, rhythm, sizeof(Rhythm) * 6); + data->rhythmtl = rhythmtl; + data->rhythmtvol = rhythmtvol; + data->rhythmkey = rhythmkey; +} + +// --------------------------------------------------------------------------- +void OPNA::DataLoad(struct OPNAData* data) { + OPNABase::DataLoad(&data->opnabase); + memcpy(rhythm, data->rhythm, sizeof(Rhythm) * 6); + rhythmtl = data->rhythmtl; + rhythmtvol = data->rhythmtvol; + rhythmkey = data->rhythmkey; + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- +// ���Y������ +// +void OPNA::RhythmMix(Sample* buffer, uint count) +{ + if (rhythmtvol < 128 && rhythm[0].sample && (rhythmkey & 0x3f)) + { + Sample* limit = buffer + count * 2; + for (int i=0; i<6; i++) + { + Rhythm& r = rhythm[i]; + if ((rhythmkey & (1 << i)) /*&& r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max + { + int db = Limit(rhythmtl+rhythmtvol+r.level+r.volume, 127, -31); + int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; + int maskl = -((r.pan >> 1) & 1); + int maskr = -(r.pan & 1); + + if (rhythmmask_ & (1 << i)) + { + maskl = maskr = 0; + } + + for (Sample* dest = buffer; dest> 12; + r.pos += r.step; + StoreSample(dest[0], sample & maskl); + StoreSample(dest[1], sample & maskr); + } + } + } + } +} + +// --------------------------------------------------------------------------- +// ���ʐݒ� +// +void OPNA::SetVolumeRhythmTotal(int db) +{ + db = Min(db, 20); + rhythmtvol = -(db * 2 / 3); +} + +void OPNA::SetVolumeRhythm(int index, int db) +{ + db = Min(db, 20); + rhythm[index].volume = -(db * 2 / 3); +} + +void OPNA::SetVolumeADPCM(int db) +{ + db = Min(db, 20); + if (db > -192) + adpcmvol = int(65536.0 * pow(10.0, db / 40.0)); + else + adpcmvol = 0; + + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNA::Mix(Sample* buffer, int nsamples) +{ + FMMix(buffer, nsamples); + psg.Mix(buffer, nsamples); + ADPCMBMix(buffer, nsamples); + RhythmMix(buffer, nsamples); +} + +#endif // BUILD_OPNA + +// --------------------------------------------------------------------------- +// YM2610(OPNB) +// --------------------------------------------------------------------------- + +#ifdef BUILD_OPNB + +// --------------------------------------------------------------------------- +// �\�z +// +OPNB::OPNB() +{ + adpcmabuf = 0; + adpcmasize = 0; + for (int i=0; i<6; i++) + { + adpcma[i].pan = 0; + adpcma[i].level = 0; + adpcma[i].volume = 0; + adpcma[i].pos = 0; + adpcma[i].step = 0; + adpcma[i].volume = 0; + adpcma[i].start = 0; + adpcma[i].stop = 0; + adpcma[i].adpcmx = 0; + adpcma[i].adpcmd = 0; + } + adpcmatl = 0; + adpcmakey = 0; + adpcmatvol = 0; + adpcmmask = 0; + adpcmnotice = 0x8000; + granuality = -1; + csmch = &ch[2]; + + InitADPCMATable(); +} + +OPNB::~OPNB() +{ +} + +// --------------------------------------------------------------------------- +// ������ +// +bool OPNB::Init(uint c, uint r, bool ipflag, + uint8 *_adpcma, int _adpcma_size, + uint8 *_adpcmb, int _adpcmb_size) +{ + int i; + if (!SetRate(c, r, ipflag)) + return false; + if (!OPNABase::Init(c, r, ipflag)) + return false; + + adpcmabuf = _adpcma; + adpcmasize = _adpcma_size; + adpcmbuf = _adpcmb; + + for (i=0; i<=24; i++) // max 16M bytes + { + if (_adpcmb_size <= (1 << i)) + { + adpcmmask = (1 << i) - 1; + break; + } + } + +// adpcmmask = _adpcmb_size - 1; + limitaddr = adpcmmask; + + Reset(); + + SetVolumeFM(0); + SetVolumePSG(0); + SetVolumeADPCMB(0); + SetVolumeADPCMATotal(0); + for (i=0; i<6; i++) + SetVolumeADPCMA(0, 0); + SetChannelMask(0); + return true; +} + +// --------------------------------------------------------------------------- +// ���Z�b�g +// +void OPNB::Reset() +{ + OPNABase::Reset(); + + stmask = ~0; + adpcmakey = 0; + reg29 = ~0; + + for (int i=0; i<6; i++) + { + adpcma[i].pan = 0; + adpcma[i].level = 0; + adpcma[i].volume = 0; + adpcma[i].pos = 0; + adpcma[i].step = 0; + adpcma[i].volume = 0; + adpcma[i].start = 0; + adpcma[i].stop = 0; + adpcma[i].adpcmx = 0; + adpcma[i].adpcmd = 0; + } +} + +// --------------------------------------------------------------------------- +// �T���v�����O���[�g�ύX +// +bool OPNB::SetRate(uint c, uint r, bool ipflag) +{ + if (!OPNABase::SetRate(c, r, ipflag)) + return false; + + adpcmastep = int(double(c) / 54 * 8192 / r); + return true; +} + +// --------------------------------------------------------------------------- +// ���W�X�^�A���C�Ƀf�[�^��ݒ� +// +void OPNB::SetReg(uint addr, uint data) +{ + addr &= 0x1ff; + + switch (addr) + { + // omitted registers + case 0x29: + case 0x2d: case 0x2e: case 0x2f: + break; + + // ADPCM A --------------------------------------------------------------- + case 0x100: // DM/KEYON + if (!(data & 0x80)) // KEY ON + { + adpcmakey |= data & 0x3f; + for (int c=0; c<6; c++) + { + if (data & (1<> 6) & 3; + adpcma[addr & 7].level = ~data & 31; + break; + + case 0x110: case 0x111: case 0x112: // START ADDRESS (L) + case 0x113: case 0x114: case 0x115: + case 0x118: case 0x119: case 0x11a: // START ADDRESS (H) + case 0x11b: case 0x11c: case 0x11d: + adpcmareg[addr - 0x110] = data; + adpcma[addr & 7].pos = adpcma[addr & 7].start = + (adpcmareg[(addr&7)+8]*256+adpcmareg[addr&7]) << 9; + break; + + case 0x120: case 0x121: case 0x122: // END ADDRESS (L) + case 0x123: case 0x124: case 0x125: + case 0x128: case 0x129: case 0x12a: // END ADDRESS (H) + case 0x12b: case 0x12c: case 0x12d: + adpcmareg[addr - 0x110] = data; + adpcma[addr & 7].stop = + (adpcmareg[(addr&7)+24]*256+adpcmareg[(addr&7)+16] + 1) << 9; + break; + + // ADPCMB ----------------------------------------------------------------- + case 0x10: + if ((data & 0x80) && !adpcmplay) + { + adpcmplay = true; + memaddr = startaddr; + adpcmx = 0, adpcmd = 127; + adplc = 0; + } + if (data & 1) + adpcmplay = false; + control1 = data & 0x91; + break; + + + case 0x11: // Control Register 2 + control2 = data & 0xc0; + break; + + case 0x12: // Start Address L + case 0x13: // Start Address H + adpcmreg[addr - 0x12 + 0] = data; + startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 9; + memaddr = startaddr; + break; + + case 0x14: // Stop Address L + case 0x15: // Stop Address H + adpcmreg[addr - 0x14 + 2] = data; + stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 9; +// LOG1(" stopaddr %.6x", stopaddr); + break; + + case 0x19: // delta-N L + case 0x1a: // delta-N H + adpcmreg[addr - 0x19 + 4] = data; + deltan = adpcmreg[5]*256+adpcmreg[4]; + deltan = Max(256, deltan); + adpld = deltan * adplbase >> 16; + break; + + case 0x1b: // Level Control + adpcmlevel = data; + adpcmvolume = (adpcmvol * adpcmlevel) >> 12; + break; + + case 0x1c: // Flag Control + stmask = ~((data & 0xbf) << 8); + status &= stmask; + UpdateStatus(); + break; + + default: + OPNABase::SetReg(addr, data); + break; + } +// LOG0("\n"); +} + +// --------------------------------------------------------------------------- +// ���W�X�^�擾 +// +uint OPNB::GetReg(uint addr) +{ + if (addr < 0x10) + return psg.GetReg(addr); + + return 0; +} + +// --------------------------------------------------------------------------- +// �g���X�e�[�^�X��ǂ݂��� +// +uint OPNB::ReadStatusEx() +{ + return (status & stmask) >> 8; +} + +// --------------------------------------------------------------------------- +// YM2610 +// +int OPNB::jedi_table[(48+1)*16]; + +void OPNB::InitADPCMATable() +{ + const static int8 table2[] = + { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9,-11,-13,-15, + }; + + for (int i=0; i<=48; i++) + { + int s = int(16.0 * pow (1.1, i) * 3); + for (int j=0; j<16; j++) + { + jedi_table[i*16+j] = s * table2[j] / 8; + } + } +} + +// --------------------------------------------------------------------------- +// ADPCMA ���� +// +void OPNB::ADPCMAMix(Sample* buffer, uint count) +{ + const static int decode_tableA1[16] = + { + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16, + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 + }; + + if (adpcmatvol < 128 && (adpcmakey & 0x3f)) + { + Sample* limit = buffer + count * 2; + for (int i=0; i<6; i++) + { + ADPCMA& r = adpcma[i]; + if ((adpcmakey & (1 << i))/* && r.level < 128*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max + { + uint maskl = r.pan & 2 ? -1 : 0; + uint maskr = r.pan & 1 ? -1 : 0; + if (rhythmmask_ & (1 << i)) + { + maskl = maskr = 0; + } + + int db = Limit(adpcmatl+adpcmatvol+r.level+r.volume, 127, -31); + int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4; + + Sample* dest = buffer; + for ( ; dest= r.stop) + { + SetStatus(0x100 << i); + adpcmakey &= ~(1< 0x10000; r.step -= 0x10000) + { + int data; + if (!(r.pos & 1)) + { + r.nibble = adpcmabuf[r.pos>>1]; + data = r.nibble >> 4; + } + else + { + data = r.nibble & 0x0f; + } + r.pos++; + + r.adpcmx += jedi_table[r.adpcmd + data]; + r.adpcmx = Limit(r.adpcmx, 2048*3-1, -2048*3); + r.adpcmd += decode_tableA1[data]; + r.adpcmd = Limit(r.adpcmd, 48*16, 0); + } + int sample = (r.adpcmx * vol) >> 10; + StoreSample(dest[0], sample & maskl); + StoreSample(dest[1], sample & maskr); + } + } + } + } +} + +// --------------------------------------------------------------------------- +// ���ʐݒ� +// +void OPNB::SetVolumeADPCMATotal(int db) +{ + db = Min(db, 20); + adpcmatvol = -(db * 2 / 3); +} + +void OPNB::SetVolumeADPCMA(int index, int db) +{ + db = Min(db, 20); + adpcma[index].volume = -(db * 2 / 3); +} + +void OPNB::SetVolumeADPCMB(int db) +{ + db = Min(db, 20); + if (db > -192) + adpcmvol = int(65536.0 * pow(10, db / 40.0)); + else + adpcmvol = 0; +} + +// --------------------------------------------------------------------------- +void OPNB::DataSave(struct OPNBData* data, void* adpcmadata) { + OPNABase::DataSave(&data->opnabase); + if(adpcmasize) { + adpcmadata = malloc(adpcmasize); + memcpy(adpcmadata, adpcmabuf, adpcmasize); + } + data->adpcmasize = adpcmasize; + memcpy(data->adpcma, adpcma, sizeof(ADPCMA) * 6); + data->adpcmatl = adpcmatl; + data->adpcmatvol = adpcmatvol; + data->adpcmakey = adpcmakey; + data->adpcmastep = adpcmastep; + memcpy(data->adpcmareg, adpcmareg, 32); + for(int i = 0; i < 6; i++) { + ch[i].DataSave(&data->ch[i]); + } +} + +// --------------------------------------------------------------------------- +void OPNB::DataLoad(struct OPNBData* data, void* adpcmadata) { + OPNABase::DataLoad(&data->opnabase); // libOPNMIDI: bugfix DataLoad (was DataSave here) + if(data->adpcmasize) { + adpcmabuf = (uint8*)malloc(data->adpcmasize); + memcpy(adpcmabuf, adpcmadata, data->adpcmasize); + } + adpcmasize = data->adpcmasize; + memcpy(adpcma, data->adpcma, sizeof(ADPCMA) * 6); + adpcmatl = data->adpcmatl; + adpcmatvol = data->adpcmatvol; + adpcmakey = data->adpcmakey; + adpcmastep = data->adpcmastep; + memcpy(adpcmareg, data->adpcmareg, 32); + for(int i = 0; i < 6; i++) { + ch[i].DataLoad(&data->ch[i]); + } + csmch = &ch[2]; +} + +// --------------------------------------------------------------------------- +// ���� +// in: buffer ������ +// nsamples �����T���v���� +// +void OPNB::Mix(Sample* buffer, int nsamples) +{ + FMMix(buffer, nsamples); + psg.Mix(buffer, nsamples); + ADPCMBMix(buffer, nsamples); + ADPCMAMix(buffer, nsamples); +} + +#endif // BUILD_OPNB + +} // namespace FM diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.h index f072e3405..634a0cbf7 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_opna.h @@ -1,523 +1,523 @@ -// --------------------------------------------------------------------------- -// OPN/A/B interface with ADPCM support -// Copyright (C) cisc 1998, 2003. -// --------------------------------------------------------------------------- -// $Id: opna.h,v 1.33 2003/06/12 13:14:37 cisc Exp $ - -#ifndef FM_OPNA_H -#define FM_OPNA_H - -#include "fmgen_fmgen.h" -#include "fmgen_fmtimer.h" -#include "fmgen_psg.h" - -// --------------------------------------------------------------------------- -// class OPN/OPNA -// OPN/OPNA に良く似た音を生成する音源ユニット -// -// interface: -// bool Init(uint clock, uint rate, bool, const char* path); -// 初期化.このクラスを使用する前にかならず呼んでおくこと. -// OPNA の場合はこの関数でリズムサンプルを読み込む -// -// clock: OPN/OPNA/OPNB のクロック周波数(Hz) -// -// rate: 生成する PCM の標本周波数(Hz) -// -// path: リズムサンプルのパス(OPNA のみ有効) -// 省略時はカレントディレクトリから読み込む -// 文字列の末尾には '¥' や '/' などをつけること -// -// 返り値 初期化に成功すれば true -// -// bool LoadRhythmSample(const char* path) -// (OPNA ONLY) -// Rhythm サンプルを読み直す. -// path は Init の path と同じ. -// -// bool SetRate(uint clock, uint rate, bool) -// クロックや PCM レートを変更する -// 引数等は Init を参照のこと. -// -// void Mix(FM_SAMPLETYPE* dest, int nsamples) -// Stereo PCM データを nsamples 分合成し, dest で始まる配列に -// 加える(加算する) -// ・dest には sample*2 個分の領域が必要 -// ・格納形式は L, R, L, R... となる. -// ・あくまで加算なので,あらかじめ配列をゼロクリアする必要がある -// ・FM_SAMPLETYPE が short 型の場合クリッピングが行われる. -// ・この関数は音源内部のタイマーとは独立している. -// Timer は Count と GetNextEvent で操作する必要がある. -// -// void Reset() -// 音源をリセット(初期化)する -// -// void SetReg(uint reg, uint data) -// 音源のレジスタ reg に data を書き込む -// -// uint GetReg(uint reg) -// 音源のレジスタ reg の内容を読み出す -// 読み込むことが出来るレジスタは PSG, ADPCM の一部,ID(0xff) とか -// -// uint ReadStatus()/ReadStatusEx() -// 音源のステータスレジスタを読み出す -// ReadStatusEx は拡張ステータスレジスタの読み出し(OPNA) -// busy フラグは常に 0 -// -// bool Count(uint32 t) -// 音源のタイマーを t [μ秒] 進める. -// 音源の内部状態に変化があった時(timer オーバーフロー) -// true を返す -// -// uint32 GetNextEvent() -// 音源のタイマーのどちらかがオーバーフローするまでに必要な -// 時間[μ秒]を返す -// タイマーが停止している場合は ULONG_MAX を返す… と思う -// -// void SetVolumeFM(int db)/SetVolumePSG(int db) ... -// 各音源の音量を+−方向に調節する.標準値は 0. -// 単位は約 1/2 dB,有効範囲の上限は 20 (10dB) -// -namespace FM -{ - // OPN Base ------------------------------------------------------- - struct OPNBaseData { - struct TimerData timer; - int fmvolume; - uint clock; - uint rate; - uint psgrate; - uint status; - uint8 prescale; - struct ChipData chip; - struct PSGData psg; - }; - - class OPNBase : public Timer - { - public: - OPNBase(); - - bool Init(uint c, uint r); - virtual void Reset(); - - void SetVolumeFM(int db); - void SetVolumePSG(int db); - - void DataSave(struct OPNBaseData* data); - void DataLoad(struct OPNBaseData* data); - - protected: - void SetParameter(Channel4* ch, uint addr, uint data); - void SetPrescaler(uint p); - void RebuildTimeTable(); - - int fmvolume; - - uint clock; // OPN クロック - uint rate; // FM 音源合成レート - uint psgrate; // FMGen 出力レート - uint status; - Channel4* csmch; - - - static uint32 lfotable[8]; - - private: - void TimerA(); - uint8 prescale; - - protected: - Chip chip; - PSG psg; - }; - - // OPN2 Base ------------------------------------------------------ - struct OPNABaseData { - struct OPNBaseData opnbase; - uint8 pan[6]; - uint16 panvolume_l[6]; // libOPNMIDI: soft panning - uint16 panvolume_r[6]; // libOPNMIDI: soft panning - uint8 fnum2[9]; - uint8 reg22; - uint reg29; - uint stmask; - uint statusnext; - uint32 lfocount; - uint32 lfodcount; - uint fnum[6]; - uint fnum3[3]; - bool is_adpcmbuf; - uint8 adpcmbuf[0x40000]; - uint adpcmmask; - uint adpcmnotice; - uint startaddr; - uint stopaddr; - uint memaddr; - uint limitaddr; - int adpcmlevel; - int adpcmvolume; - int adpcmvol; - uint deltan; - int adplc; - int adpld; - uint adplbase; - int adpcmx; - int adpcmd; - int adpcmout; - int apout0; - int apout1; - uint adpcmreadbuf; - bool adpcmplay; - int8 granuality; - bool adpcmmask_; - uint8 control1; - uint8 control2; - uint8 adpcmreg[8]; - int rhythmmask_; - struct Channel4Data ch[6]; - }; - - class OPNABase : public OPNBase - { - public: - OPNABase(); - ~OPNABase(); - - uint ReadStatus() { return status & 0x03; } - uint ReadStatusEx(); - void SetChannelMask(uint mask); - - // libOPNMIDI: soft panning - void SetPan(uint c, uint8 p); - - void DataSave(struct OPNABaseData* data); - void DataLoad(struct OPNABaseData* data); - - private: - virtual void Intr(bool) {} - - void MakeTable2(); - - protected: - bool Init(uint c, uint r, bool); - bool SetRate(uint c, uint r, bool); - - void Reset(); - void SetReg(uint addr, uint data); - void SetADPCMBReg(uint reg, uint data); - uint GetReg(uint addr); - - protected: - void FMMix(Sample* buffer, int nsamples); - void Mix6(Sample* buffer, int nsamples, int activech); - - void MixSubS(int activech, ISample**); - void MixSubSL(int activech, ISample**); - - void SetStatus(uint bit); - void ResetStatus(uint bit); - void UpdateStatus(); - void LFO(); - - void DecodeADPCMB(); - void ADPCMBMix(Sample* dest, uint count); - - void WriteRAM(uint data); - uint ReadRAM(); - int ReadRAMN(); - int DecodeADPCMBSample(uint); - - // FM 音源関係 - uint8 pan[6]; - uint16 panvolume_l[6]; // libOPNMIDI: soft panning - uint16 panvolume_r[6]; // libOPNMIDI: soft panning - uint8 fnum2[9]; - - uint8 reg22; - uint reg29; // OPNA only? - - uint stmask; - uint statusnext; - - uint32 lfocount; - uint32 lfodcount; - - uint fnum[6]; - uint fnum3[3]; - - // ADPCM 関係 - uint8* adpcmbuf; // ADPCM RAM - uint adpcmmask; // メモリアドレスに対するビットマスク - uint adpcmnotice; // ADPCM 再生終了時にたつビット - uint startaddr; // Start address - uint stopaddr; // Stop address - uint memaddr; // 再生中アドレス - uint limitaddr; // Limit address/mask - int adpcmlevel; // ADPCM 音量 - int adpcmvolume; - int adpcmvol; - uint deltan; // N - int adplc; // 周波数変換用変数 - int adpld; // 周波数変換用変数差分値 - uint adplbase; // adpld の元 - int adpcmx; // ADPCM 合成用 x - int adpcmd; // ADPCM 合成用 - int adpcmout; // ADPCM 合成後の出力 - int apout0; // out(t-2)+out(t-1) - int apout1; // out(t-1)+out(t) - - uint adpcmreadbuf; // ADPCM リード用バッファ - bool adpcmplay; // ADPCM 再生中 - int8 granuality; - bool adpcmmask_; - - uint8 control1; // ADPCM コントロールレジスタ1 - uint8 control2; // ADPCM コントロールレジスタ2 - uint8 adpcmreg[8]; // ADPCM レジスタの一部分 - - int rhythmmask_; - - Channel4 ch[6]; - - static void BuildLFOTable(); - static int amtable[FM_LFOENTS]; - static int pmtable[FM_LFOENTS]; - static int32 tltable[FM_TLENTS+FM_TLPOS]; - static bool tablehasmade; - }; - - // YM2203(OPN) ---------------------------------------------------- - struct OPNData { - struct OPNBaseData opnbase; - uint fnum[3]; - uint fnum3[3]; - uint8 fnum2[6]; - struct Channel4Data ch[3]; - }; - - class OPN : public OPNBase - { - public: - OPN(); - virtual ~OPN() {} - - bool Init(uint c, uint r, bool=false, const char* =0); - bool SetRate(uint c, uint r, bool=false); - - void Reset(); - void Mix(Sample* buffer, int nsamples); - void SetReg(uint addr, uint data); - uint GetReg(uint addr); - uint ReadStatus() { return status & 0x03; } - uint ReadStatusEx() { return 0xff; } - - void SetChannelMask(uint mask); - - int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } - int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } - Channel4* dbgGetCh(int c) { return &ch[c]; } - - void DataSave(struct OPNData* data); - void DataLoad(struct OPNData* data); - - private: - virtual void Intr(bool) {} - - void SetStatus(uint bit); - void ResetStatus(uint bit); - - uint fnum[3]; - uint fnum3[3]; - uint8 fnum2[6]; - - Channel4 ch[3]; - }; - - // YM2608(OPNA) --------------------------------------------------- - struct Rhythm - { - uint8 pan; // ぱん - int8 level; // おんりょう - int volume; // おんりょうせってい - int16* sample; // さんぷる - uint size; // さいず - uint pos; // いち - uint step; // すてっぷち - uint rate; // さんぷるのれーと - }; - - struct OPNAData { - struct OPNABaseData opnabase; - Rhythm rhythm[6]; - int8 rhythmtl; - int rhythmtvol; - uint8 rhythmkey; - }; - - class OPNA : public OPNABase - { - public: - OPNA(); - virtual ~OPNA(); - - bool Init(uint c, uint r, bool = false, const char* rhythmpath=0); - bool LoadRhythmSample(const char*); - - bool SetRate(uint c, uint r, bool = false); - void Mix(Sample* buffer, int nsamples); - - void Reset(); - void SetReg(uint addr, uint data); - uint GetReg(uint addr); - - void SetVolumeADPCM(int db); - void SetVolumeRhythmTotal(int db); - void SetVolumeRhythm(int index, int db); - - uint8* GetADPCMBuffer() { return adpcmbuf; } - - int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } - int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } - Channel4* dbgGetCh(int c) { return &ch[c]; } - - void DataSave(struct OPNAData* data); - void DataLoad(struct OPNAData* data); - - private: - void RhythmMix(Sample* buffer, uint count); - - // リズム音源関係 - Rhythm rhythm[6]; - int8 rhythmtl; // リズム全体の音量 - int rhythmtvol; - uint8 rhythmkey; // リズムのキー - }; - - // YM2610/B(OPNB) --------------------------------------------------- - struct ADPCMA - { - uint8 pan; // ぱん - int8 level; // おんりょう - int volume; // おんりょうせってい - uint pos; // いち - uint step; // すてっぷち - - uint start; // 開始 - uint stop; // 終了 - uint nibble; // 次の 4 bit - int adpcmx; // 変換用 - int adpcmd; // 変換用 - }; - - struct OPNBData { - struct OPNABaseData opnabase; -// uint8* adpcmabuf; - int adpcmasize; - ADPCMA adpcma[6]; - int8 adpcmatl; - int adpcmatvol; - uint8 adpcmakey; - int adpcmastep; - uint8 adpcmareg[32]; - struct Channel4Data ch[6]; - }; - - class OPNB : public OPNABase - { - public: - OPNB(); - virtual ~OPNB(); - - bool Init(uint c, uint r, bool = false, - uint8 *_adpcma = 0, int _adpcma_size = 0, - uint8 *_adpcmb = 0, int _adpcmb_size = 0); - - bool SetRate(uint c, uint r, bool = false); - void Mix(Sample* buffer, int nsamples); - - void Reset(); - void SetReg(uint addr, uint data); - uint GetReg(uint addr); - uint ReadStatusEx(); - - void SetVolumeADPCMATotal(int db); - void SetVolumeADPCMA(int index, int db); - void SetVolumeADPCMB(int db); - -// void SetChannelMask(uint mask); - - void DataSave(struct OPNBData* data, void* adpcmdata); - void DataLoad(struct OPNBData* data, void* adpcmdata); - - private: - int DecodeADPCMASample(uint); - void ADPCMAMix(Sample* buffer, uint count); - static void InitADPCMATable(); - - // ADPCMA 関係 - uint8* adpcmabuf; // ADPCMA ROM - int adpcmasize; - ADPCMA adpcma[6]; - int8 adpcmatl; // ADPCMA 全体の音量 - int adpcmatvol; - uint8 adpcmakey; // ADPCMA のキー - int adpcmastep; - uint8 adpcmareg[32]; - - static int jedi_table[(48+1)*16]; - - Channel4 ch[6]; - }; - - // YM2612/3438(OPN2) ---------------------------------------------------- - class OPN2 : public OPNBase - { - public: - OPN2(); - virtual ~OPN2() {} - - bool Init(uint c, uint r, bool=false, const char* =0); - bool SetRate(uint c, uint r, bool); - - void Reset(); - void Mix(Sample* buffer, int nsamples); - void SetReg(uint addr, uint data); - uint GetReg(uint addr); - uint ReadStatus() { return status & 0x03; } - uint ReadStatusEx() { return 0xff; } - - void SetChannelMask(uint mask); - - private: - virtual void Intr(bool) {} - - void SetStatus(uint bit); - void ResetStatus(uint bit); - - uint fnum[3]; - uint fnum3[3]; - uint8 fnum2[6]; - - // 線形補間用ワーク - int32 mixc, mixc1; - - Channel4 ch[3]; - }; -} - -// --------------------------------------------------------------------------- - -inline void FM::OPNBase::RebuildTimeTable() -{ - int p = prescale; - prescale = -1; - SetPrescaler(p); -} - -inline void FM::OPNBase::SetVolumePSG(int db) -{ - psg.SetVolume(db); -} - -#endif // FM_OPNA_H +// --------------------------------------------------------------------------- +// OPN/A/B interface with ADPCM support +// Copyright (C) cisc 1998, 2003. +// --------------------------------------------------------------------------- +// $Id: opna.h,v 1.33 2003/06/12 13:14:37 cisc Exp $ + +#ifndef FM_OPNA_H +#define FM_OPNA_H + +#include "fmgen_fmgen.h" +#include "fmgen_fmtimer.h" +#include "fmgen_psg.h" + +// --------------------------------------------------------------------------- +// class OPN/OPNA +// OPN/OPNA に良く似た音を生成する音源ユニット +// +// interface: +// bool Init(uint clock, uint rate, bool, const char* path); +// 初期化.このクラスを使用する前にかならず呼んでおくこと. +// OPNA の場合はこの関数でリズムサンプルを読み込む +// +// clock: OPN/OPNA/OPNB のクロック周波数(Hz) +// +// rate: 生成する PCM の標本周波数(Hz) +// +// path: リズムサンプルのパス(OPNA のみ有効) +// 省略時はカレントディレクトリから読み込む +// 文字列の末尾には '¥' や '/' などをつけること +// +// 返り値 初期化に成功すれば true +// +// bool LoadRhythmSample(const char* path) +// (OPNA ONLY) +// Rhythm サンプルを読み直す. +// path は Init の path と同じ. +// +// bool SetRate(uint clock, uint rate, bool) +// クロックや PCM レートを変更する +// 引数等は Init を参照のこと. +// +// void Mix(FM_SAMPLETYPE* dest, int nsamples) +// Stereo PCM データを nsamples 分合成し, dest で始まる配列に +// 加える(加算する) +// ・dest には sample*2 個分の領域が必要 +// ・格納形式は L, R, L, R... となる. +// ・あくまで加算なので,あらかじめ配列をゼロクリアする必要がある +// ・FM_SAMPLETYPE が short 型の場合クリッピングが行われる. +// ・この関数は音源内部のタイマーとは独立している. +// Timer は Count と GetNextEvent で操作する必要がある. +// +// void Reset() +// 音源をリセット(初期化)する +// +// void SetReg(uint reg, uint data) +// 音源のレジスタ reg に data を書き込む +// +// uint GetReg(uint reg) +// 音源のレジスタ reg の内容を読み出す +// 読み込むことが出来るレジスタは PSG, ADPCM の一部,ID(0xff) とか +// +// uint ReadStatus()/ReadStatusEx() +// 音源のステータスレジスタを読み出す +// ReadStatusEx は拡張ステータスレジスタの読み出し(OPNA) +// busy フラグは常に 0 +// +// bool Count(uint32 t) +// 音源のタイマーを t [μ秒] 進める. +// 音源の内部状態に変化があった時(timer オーバーフロー) +// true を返す +// +// uint32 GetNextEvent() +// 音源のタイマーのどちらかがオーバーフローするまでに必要な +// 時間[μ秒]を返す +// タイマーが停止している場合は ULONG_MAX を返す… と思う +// +// void SetVolumeFM(int db)/SetVolumePSG(int db) ... +// 各音源の音量を+−方向に調節する.標準値は 0. +// 単位は約 1/2 dB,有効範囲の上限は 20 (10dB) +// +namespace FM +{ + // OPN Base ------------------------------------------------------- + struct OPNBaseData { + struct TimerData timer; + int fmvolume; + uint clock; + uint rate; + uint psgrate; + uint status; + uint8 prescale; + struct ChipData chip; + struct PSGData psg; + }; + + class OPNBase : public Timer + { + public: + OPNBase(); + + bool Init(uint c, uint r); + virtual void Reset(); + + void SetVolumeFM(int db); + void SetVolumePSG(int db); + + void DataSave(struct OPNBaseData* data); + void DataLoad(struct OPNBaseData* data); + + protected: + void SetParameter(Channel4* ch, uint addr, uint data); + void SetPrescaler(uint p); + void RebuildTimeTable(); + + int fmvolume; + + uint clock; // OPN クロック + uint rate; // FM 音源合成レート + uint psgrate; // FMGen 出力レート + uint status; + Channel4* csmch; + + + static uint32 lfotable[8]; + + private: + void TimerA(); + uint8 prescale; + + protected: + Chip chip; + PSG psg; + }; + + // OPN2 Base ------------------------------------------------------ + struct OPNABaseData { + struct OPNBaseData opnbase; + uint8 pan[6]; + uint16 panvolume_l[6]; // libOPNMIDI: soft panning + uint16 panvolume_r[6]; // libOPNMIDI: soft panning + uint8 fnum2[9]; + uint8 reg22; + uint reg29; + uint stmask; + uint statusnext; + uint32 lfocount; + uint32 lfodcount; + uint fnum[6]; + uint fnum3[3]; + bool is_adpcmbuf; + uint8 adpcmbuf[0x40000]; + uint adpcmmask; + uint adpcmnotice; + uint startaddr; + uint stopaddr; + uint memaddr; + uint limitaddr; + int adpcmlevel; + int adpcmvolume; + int adpcmvol; + uint deltan; + int adplc; + int adpld; + uint adplbase; + int adpcmx; + int adpcmd; + int adpcmout; + int apout0; + int apout1; + uint adpcmreadbuf; + bool adpcmplay; + int8 granuality; + bool adpcmmask_; + uint8 control1; + uint8 control2; + uint8 adpcmreg[8]; + int rhythmmask_; + struct Channel4Data ch[6]; + }; + + class OPNABase : public OPNBase + { + public: + OPNABase(); + ~OPNABase(); + + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx(); + void SetChannelMask(uint mask); + + // libOPNMIDI: soft panning + void SetPan(uint c, uint8 p); + + void DataSave(struct OPNABaseData* data); + void DataLoad(struct OPNABaseData* data); + + private: + virtual void Intr(bool) {} + + void MakeTable2(); + + protected: + bool Init(uint c, uint r, bool); + bool SetRate(uint c, uint r, bool); + + void Reset(); + void SetReg(uint addr, uint data); + void SetADPCMBReg(uint reg, uint data); + uint GetReg(uint addr); + + protected: + void FMMix(Sample* buffer, int nsamples); + void Mix6(Sample* buffer, int nsamples, int activech); + + void MixSubS(int activech, ISample**); + void MixSubSL(int activech, ISample**); + + void SetStatus(uint bit); + void ResetStatus(uint bit); + void UpdateStatus(); + void LFO(); + + void DecodeADPCMB(); + void ADPCMBMix(Sample* dest, uint count); + + void WriteRAM(uint data); + uint ReadRAM(); + int ReadRAMN(); + int DecodeADPCMBSample(uint); + + // FM 音源関係 + uint8 pan[6]; + uint16 panvolume_l[6]; // libOPNMIDI: soft panning + uint16 panvolume_r[6]; // libOPNMIDI: soft panning + uint8 fnum2[9]; + + uint8 reg22; + uint reg29; // OPNA only? + + uint stmask; + uint statusnext; + + uint32 lfocount; + uint32 lfodcount; + + uint fnum[6]; + uint fnum3[3]; + + // ADPCM 関係 + uint8* adpcmbuf; // ADPCM RAM + uint adpcmmask; // メモリアドレスに対するビットマスク + uint adpcmnotice; // ADPCM 再生終了時にたつビット + uint startaddr; // Start address + uint stopaddr; // Stop address + uint memaddr; // 再生中アドレス + uint limitaddr; // Limit address/mask + int adpcmlevel; // ADPCM 音量 + int adpcmvolume; + int adpcmvol; + uint deltan; // N + int adplc; // 周波数変換用変数 + int adpld; // 周波数変換用変数差分値 + uint adplbase; // adpld の元 + int adpcmx; // ADPCM 合成用 x + int adpcmd; // ADPCM 合成用 + int adpcmout; // ADPCM 合成後の出力 + int apout0; // out(t-2)+out(t-1) + int apout1; // out(t-1)+out(t) + + uint adpcmreadbuf; // ADPCM リード用バッファ + bool adpcmplay; // ADPCM 再生中 + int8 granuality; + bool adpcmmask_; + + uint8 control1; // ADPCM コントロールレジスタ1 + uint8 control2; // ADPCM コントロールレジスタ2 + uint8 adpcmreg[8]; // ADPCM レジスタの一部分 + + int rhythmmask_; + + Channel4 ch[6]; + + static void BuildLFOTable(); + static int amtable[FM_LFOENTS]; + static int pmtable[FM_LFOENTS]; + static int32 tltable[FM_TLENTS+FM_TLPOS]; + static bool tablehasmade; + }; + + // YM2203(OPN) ---------------------------------------------------- + struct OPNData { + struct OPNBaseData opnbase; + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + struct Channel4Data ch[3]; + }; + + class OPN : public OPNBase + { + public: + OPN(); + virtual ~OPN() {} + + bool Init(uint c, uint r, bool=false, const char* =0); + bool SetRate(uint c, uint r, bool=false); + + void Reset(); + void Mix(Sample* buffer, int nsamples); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx() { return 0xff; } + + void SetChannelMask(uint mask); + + int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } + int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } + Channel4* dbgGetCh(int c) { return &ch[c]; } + + void DataSave(struct OPNData* data); + void DataLoad(struct OPNData* data); + + private: + virtual void Intr(bool) {} + + void SetStatus(uint bit); + void ResetStatus(uint bit); + + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + + Channel4 ch[3]; + }; + + // YM2608(OPNA) --------------------------------------------------- + struct Rhythm + { + uint8 pan; // ぱん + int8 level; // おんりょう + int volume; // おんりょうせってい + int16* sample; // さんぷる + uint size; // さいず + uint pos; // いち + uint step; // すてっぷち + uint rate; // さんぷるのれーと + }; + + struct OPNAData { + struct OPNABaseData opnabase; + Rhythm rhythm[6]; + int8 rhythmtl; + int rhythmtvol; + uint8 rhythmkey; + }; + + class OPNA : public OPNABase + { + public: + OPNA(); + virtual ~OPNA(); + + bool Init(uint c, uint r, bool = false, const char* rhythmpath=0); + bool LoadRhythmSample(const char*); + + bool SetRate(uint c, uint r, bool = false); + void Mix(Sample* buffer, int nsamples); + + void Reset(); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + + void SetVolumeADPCM(int db); + void SetVolumeRhythmTotal(int db); + void SetVolumeRhythm(int index, int db); + + uint8* GetADPCMBuffer() { return adpcmbuf; } + + int dbgGetOpOut(int c, int s) { return ch[c].op[s].dbgopout_; } + int dbgGetPGOut(int c, int s) { return ch[c].op[s].dbgpgout_; } + Channel4* dbgGetCh(int c) { return &ch[c]; } + + void DataSave(struct OPNAData* data); + void DataLoad(struct OPNAData* data); + + private: + void RhythmMix(Sample* buffer, uint count); + + // リズム音源関係 + Rhythm rhythm[6]; + int8 rhythmtl; // リズム全体の音量 + int rhythmtvol; + uint8 rhythmkey; // リズムのキー + }; + + // YM2610/B(OPNB) --------------------------------------------------- + struct ADPCMA + { + uint8 pan; // ぱん + int8 level; // おんりょう + int volume; // おんりょうせってい + uint pos; // いち + uint step; // すてっぷち + + uint start; // 開始 + uint stop; // 終了 + uint nibble; // 次の 4 bit + int adpcmx; // 変換用 + int adpcmd; // 変換用 + }; + + struct OPNBData { + struct OPNABaseData opnabase; +// uint8* adpcmabuf; + int adpcmasize; + ADPCMA adpcma[6]; + int8 adpcmatl; + int adpcmatvol; + uint8 adpcmakey; + int adpcmastep; + uint8 adpcmareg[32]; + struct Channel4Data ch[6]; + }; + + class OPNB : public OPNABase + { + public: + OPNB(); + virtual ~OPNB(); + + bool Init(uint c, uint r, bool = false, + uint8 *_adpcma = 0, int _adpcma_size = 0, + uint8 *_adpcmb = 0, int _adpcmb_size = 0); + + bool SetRate(uint c, uint r, bool = false); + void Mix(Sample* buffer, int nsamples); + + void Reset(); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatusEx(); + + void SetVolumeADPCMATotal(int db); + void SetVolumeADPCMA(int index, int db); + void SetVolumeADPCMB(int db); + +// void SetChannelMask(uint mask); + + void DataSave(struct OPNBData* data, void* adpcmdata); + void DataLoad(struct OPNBData* data, void* adpcmdata); + + private: + int DecodeADPCMASample(uint); + void ADPCMAMix(Sample* buffer, uint count); + static void InitADPCMATable(); + + // ADPCMA 関係 + uint8* adpcmabuf; // ADPCMA ROM + int adpcmasize; + ADPCMA adpcma[6]; + int8 adpcmatl; // ADPCMA 全体の音量 + int adpcmatvol; + uint8 adpcmakey; // ADPCMA のキー + int adpcmastep; + uint8 adpcmareg[32]; + + static int jedi_table[(48+1)*16]; + + Channel4 ch[6]; + }; + + // YM2612/3438(OPN2) ---------------------------------------------------- + class OPN2 : public OPNBase + { + public: + OPN2(); + virtual ~OPN2() {} + + bool Init(uint c, uint r, bool=false, const char* =0); + bool SetRate(uint c, uint r, bool); + + void Reset(); + void Mix(Sample* buffer, int nsamples); + void SetReg(uint addr, uint data); + uint GetReg(uint addr); + uint ReadStatus() { return status & 0x03; } + uint ReadStatusEx() { return 0xff; } + + void SetChannelMask(uint mask); + + private: + virtual void Intr(bool) {} + + void SetStatus(uint bit); + void ResetStatus(uint bit); + + uint fnum[3]; + uint fnum3[3]; + uint8 fnum2[6]; + + // 線形補間用ワーク + int32 mixc, mixc1; + + Channel4 ch[3]; + }; +} + +// --------------------------------------------------------------------------- + +inline void FM::OPNBase::RebuildTimeTable() +{ + int p = prescale; + prescale = -1; + SetPrescaler(p); +} + +inline void FM::OPNBase::SetVolumePSG(int db) +{ + psg.SetVolume(db); +} + +#endif // FM_OPNA_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp index aa1458821..e3b88d5c4 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.cpp @@ -1,395 +1,395 @@ -// --------------------------------------------------------------------------- -// PSG Sound Implementation -// Copyright (C) cisc 1997, 1999. -// --------------------------------------------------------------------------- -// $Id: psg.cpp,v 1.10 2002/05/15 21:38:01 cisc Exp $ - -#include "fmgen_headers.h" -#include "fmgen_misc.h" -#include "fmgen_psg.h" - -// --------------------------------------------------------------------------- -// コンストラクタ・デストラクタ -// -PSG::PSG() -{ - SetVolume(0); - MakeNoiseTable(); - Reset(); - mask = 0x3f; -} - -PSG::~PSG() -{ - -} - -// --------------------------------------------------------------------------- -// PSG を初期化する(RESET) -// -void PSG::Reset() -{ - for (int i=0; i<14; i++) - SetReg(i, 0); - SetReg(7, 0xff); - SetReg(14, 0xff); - SetReg(15, 0xff); -} - -// --------------------------------------------------------------------------- -// クロック周波数の設定 -// -void PSG::SetClock(int clock, int rate) -{ - tperiodbase = int((1 << toneshift ) / 4.0 * clock / rate); - eperiodbase = int((1 << envshift ) / 4.0 * clock / rate); - nperiodbase = int((1 << noiseshift) / 4.0 * clock / rate); - - // 各データの更新 - int tmp; - tmp = ((reg[0] + reg[1] * 256) & 0xfff); - speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; - tmp = ((reg[2] + reg[3] * 256) & 0xfff); - speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; - tmp = ((reg[4] + reg[5] * 256) & 0xfff); - speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; - tmp = reg[6] & 0x1f; - nperiod = tmp ? nperiodbase / tmp / 2 : nperiodbase / 2; - tmp = ((reg[11] + reg[12] * 256) & 0xffff); - eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; -} - -// --------------------------------------------------------------------------- -// ノイズテーブルを作成する -// -void PSG::MakeNoiseTable() -{ - if (!noisetable[0]) - { - int noise = 14321; - for (int i=0; i> 1) | (((noise << 14) ^ (noise << 16)) & 0x10000); - } - noisetable[i] = n; - } - } -} - -// --------------------------------------------------------------------------- -// 出力テーブルを作成 -// 素直にテーブルで持ったほうが省スペース。 -// -void PSG::SetVolume(int volume) -{ - double base = 0x4000 / 3.0 * pow(10.0, volume / 40.0); - for (int i=31; i>=2; i--) - { - EmitTable[i] = int(base); - base /= 1.189207115; - } - EmitTable[1] = 0; - EmitTable[0] = 0; - MakeEnvelopTable(); - - SetChannelMask(~mask); -} - -void PSG::SetChannelMask(int c) -{ - mask = ~c; - for (int i=0; i<3; i++) - olevel[i] = mask & (1 << i) ? EmitTable[(reg[8+i] & 15) * 2 + 1] : 0; -} - -// --------------------------------------------------------------------------- -// エンベロープ波形テーブル -// -void PSG::MakeEnvelopTable() -{ - // 0 lo 1 up 2 down 3 hi - static uint8 table1[16*2] = - { - 2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0, - 2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0, - }; - static uint8 table2[4] = { 0, 0, 31, 31 }; - static int8 table3[4] = { 0, 1, -1, 0 }; - - uint* ptr = enveloptable[0]; - - for (int i=0; i<16*2; i++) - { - uint8 v = table2[table1[i]]; - - for (int j=0; j<32; j++) - { - *ptr++ = EmitTable[v]; - v += table3[table1[i]]; - } - } -} - -// --------------------------------------------------------------------------- -// PSG のレジスタに値をセットする -// regnum レジスタの番号 (0 - 15) -// data セットする値 -// -void PSG::SetReg(uint regnum, uint8 data) -{ - if (regnum < 0x10) - { - reg[regnum] = data; - switch (regnum) - { - int tmp; - - case 0: // ChA Fine Tune - case 1: // ChA Coarse Tune - tmp = ((reg[0] + reg[1] * 256) & 0xfff); - speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; - break; - - case 2: // ChB Fine Tune - case 3: // ChB Coarse Tune - tmp = ((reg[2] + reg[3] * 256) & 0xfff); - speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; - break; - - case 4: // ChC Fine Tune - case 5: // ChC Coarse Tune - tmp = ((reg[4] + reg[5] * 256) & 0xfff); - speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; - break; - - case 6: // Noise generator control - data &= 0x1f; - nperiod = data ? nperiodbase / data : nperiodbase; - break; - - case 8: - olevel[0] = mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0; - break; - - case 9: - olevel[1] = mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0; - break; - - case 10: - olevel[2] = mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0; - break; - - case 11: // Envelop period - case 12: - tmp = ((reg[11] + reg[12] * 256) & 0xffff); - eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; - break; - - case 13: // Envelop shape - ecount = 0; - envelop = enveloptable[data & 15]; - break; - } - } -} - -// --------------------------------------------------------------------------- -void PSG::DataSave(struct PSGData* data) { - memcpy(data->reg, reg, 16); - memcpy(data->olevel, olevel, sizeof(uint) * 6); - memcpy(data->scount, scount, sizeof(uint32) * 3); - memcpy(data->speriod, speriod, sizeof(uint32) * 3); - data->ecount = ecount; - data->eperiod = eperiod; - data->ncount = ncount; - data->nperiod = nperiod; - data->tperiodbase = tperiodbase; - data->eperiodbase = eperiodbase; - data->nperiodbase = nperiodbase; - data->volume = volume; - data->mask = mask; -} - -// --------------------------------------------------------------------------- -void PSG::DataLoad(struct PSGData* data) { - memcpy(reg, data->reg, 16); - memcpy(olevel, data->olevel, sizeof(uint) * 6); - memcpy(scount, data->scount, sizeof(uint32) * 3); - memcpy(speriod, data->speriod, sizeof(uint32) * 3); - ecount = data->ecount; - eperiod = data->eperiod; - ncount = data->ncount; - nperiod = data->nperiod; - tperiodbase = data->tperiodbase; - eperiodbase = data->eperiodbase; - nperiodbase = data->nperiodbase; - volume = data->volume; - mask = data->mask; -} - -// --------------------------------------------------------------------------- -// -// -inline void PSG::StoreSample(Sample& dest, int32 data) -{ - if (sizeof(Sample) == 2) - dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); - else - dest += data; -} - -// --------------------------------------------------------------------------- -// PCM データを吐き出す(2ch) -// dest PCM データを展開するポインタ -// nsamples 展開する PCM のサンプル数 -// -void PSG::Mix(Sample* dest, int nsamples) -{ - uint8 chenable[3], nenable[3]; - uint8 r7 = ~reg[7]; - - if ((r7 & 0x3f) | ((reg[8] | reg[9] | reg[10]) & 0x1f)) - { - chenable[0] = (r7 & 0x01) && (speriod[0] <= (1 << toneshift)); - chenable[1] = (r7 & 0x02) && (speriod[1] <= (1 << toneshift)); - chenable[2] = (r7 & 0x04) && (speriod[2] <= (1 << toneshift)); - nenable[0] = (r7 >> 3) & 1; - nenable[1] = (r7 >> 4) & 1; - nenable[2] = (r7 >> 5) & 1; - - int noise, sample; - uint env; - uint* p1 = ((mask & 1) && (reg[ 8] & 0x10)) ? &env : &olevel[0]; - uint* p2 = ((mask & 2) && (reg[ 9] & 0x10)) ? &env : &olevel[1]; - uint* p3 = ((mask & 4) && (reg[10] & 0x10)) ? &env : &olevel[2]; - - #define SCOUNT(ch) (scount[ch] >> (toneshift+oversampling)) - - if (p1 != &env && p2 != &env && p3 != &env) - { - // エンベロープ無し - if ((r7 & 0x38) == 0) - { - // ノイズ無し - for (int i=0; i> (noiseshift+oversampling+6)) & (noisetablesize-1)] - >> (ncount >> (noiseshift+oversampling+1)); -#else - noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] - >> (ncount >> (noiseshift+oversampling+1) & 31); -#endif - ncount += nperiod; - - int x, y, z; - x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 - sample += (olevel[0] + x) ^ x; - scount[0] += speriod[0]; - y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; - sample += (olevel[1] + y) ^ y; - scount[1] += speriod[1]; - z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; - sample += (olevel[2] + z) ^ z; - scount[2] += speriod[2]; - } - sample /= (1 << oversampling); - StoreSample(dest[0], sample); - StoreSample(dest[1], sample); - dest += 2; - } - } - - // エンベロープの計算をさぼった帳尻あわせ - ecount = (ecount >> 8) + (eperiod >> (8-oversampling)) * nsamples; - if (ecount >= (1 << (envshift+6+oversampling-8))) - { - if ((reg[0x0d] & 0x0b) != 0x0a) - ecount |= (1 << (envshift+5+oversampling-8)); - ecount &= (1 << (envshift+6+oversampling-8)) - 1; - } - ecount <<= 8; - } - else - { - // エンベロープあり - for (int i=0; i> (envshift+oversampling)]; - ecount += eperiod; - if (ecount >= (1 << (envshift+6+oversampling))) - { - if ((reg[0x0d] & 0x0b) != 0x0a) - ecount |= (1 << (envshift+5+oversampling)); - ecount &= (1 << (envshift+6+oversampling)) - 1; - } -#ifdef _M_IX86 - noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] - >> (ncount >> (noiseshift+oversampling+1)); -#else - noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] - >> (ncount >> (noiseshift+oversampling+1) & 31); -#endif - ncount += nperiod; - - int x, y, z; - x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 - sample += (*p1 + x) ^ x; - scount[0] += speriod[0]; - y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; - sample += (*p2 + y) ^ y; - scount[1] += speriod[1]; - z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; - sample += (*p3 + z) ^ z; - scount[2] += speriod[2]; - } - sample /= (1 << oversampling); - StoreSample(dest[0], sample); - StoreSample(dest[1], sample); - dest += 2; - } - } - } -} - -// --------------------------------------------------------------------------- -// テーブル -// -uint PSG::noisetable[noisetablesize] = { 0, }; -int PSG::EmitTable[0x20] = { -1, }; -uint PSG::enveloptable[16][64] = { {0, } }; +// --------------------------------------------------------------------------- +// PSG Sound Implementation +// Copyright (C) cisc 1997, 1999. +// --------------------------------------------------------------------------- +// $Id: psg.cpp,v 1.10 2002/05/15 21:38:01 cisc Exp $ + +#include "fmgen_headers.h" +#include "fmgen_misc.h" +#include "fmgen_psg.h" + +// --------------------------------------------------------------------------- +// コンストラクタ・デストラクタ +// +PSG::PSG() +{ + SetVolume(0); + MakeNoiseTable(); + Reset(); + mask = 0x3f; +} + +PSG::~PSG() +{ + +} + +// --------------------------------------------------------------------------- +// PSG を初期化する(RESET) +// +void PSG::Reset() +{ + for (int i=0; i<14; i++) + SetReg(i, 0); + SetReg(7, 0xff); + SetReg(14, 0xff); + SetReg(15, 0xff); +} + +// --------------------------------------------------------------------------- +// クロック周波数の設定 +// +void PSG::SetClock(int clock, int rate) +{ + tperiodbase = int((1 << toneshift ) / 4.0 * clock / rate); + eperiodbase = int((1 << envshift ) / 4.0 * clock / rate); + nperiodbase = int((1 << noiseshift) / 4.0 * clock / rate); + + // 各データの更新 + int tmp; + tmp = ((reg[0] + reg[1] * 256) & 0xfff); + speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = ((reg[2] + reg[3] * 256) & 0xfff); + speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = ((reg[4] + reg[5] * 256) & 0xfff); + speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; + tmp = reg[6] & 0x1f; + nperiod = tmp ? nperiodbase / tmp / 2 : nperiodbase / 2; + tmp = ((reg[11] + reg[12] * 256) & 0xffff); + eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; +} + +// --------------------------------------------------------------------------- +// ノイズテーブルを作成する +// +void PSG::MakeNoiseTable() +{ + if (!noisetable[0]) + { + int noise = 14321; + for (int i=0; i> 1) | (((noise << 14) ^ (noise << 16)) & 0x10000); + } + noisetable[i] = n; + } + } +} + +// --------------------------------------------------------------------------- +// 出力テーブルを作成 +// 素直にテーブルで持ったほうが省スペース。 +// +void PSG::SetVolume(int volume) +{ + double base = 0x4000 / 3.0 * pow(10.0, volume / 40.0); + for (int i=31; i>=2; i--) + { + EmitTable[i] = int(base); + base /= 1.189207115; + } + EmitTable[1] = 0; + EmitTable[0] = 0; + MakeEnvelopTable(); + + SetChannelMask(~mask); +} + +void PSG::SetChannelMask(int c) +{ + mask = ~c; + for (int i=0; i<3; i++) + olevel[i] = mask & (1 << i) ? EmitTable[(reg[8+i] & 15) * 2 + 1] : 0; +} + +// --------------------------------------------------------------------------- +// エンベロープ波形テーブル +// +void PSG::MakeEnvelopTable() +{ + // 0 lo 1 up 2 down 3 hi + static uint8 table1[16*2] = + { + 2,0, 2,0, 2,0, 2,0, 1,0, 1,0, 1,0, 1,0, + 2,2, 2,0, 2,1, 2,3, 1,1, 1,3, 1,2, 1,0, + }; + static uint8 table2[4] = { 0, 0, 31, 31 }; + static int8 table3[4] = { 0, 1, -1, 0 }; + + uint* ptr = enveloptable[0]; + + for (int i=0; i<16*2; i++) + { + uint8 v = table2[table1[i]]; + + for (int j=0; j<32; j++) + { + *ptr++ = EmitTable[v]; + v += table3[table1[i]]; + } + } +} + +// --------------------------------------------------------------------------- +// PSG のレジスタに値をセットする +// regnum レジスタの番号 (0 - 15) +// data セットする値 +// +void PSG::SetReg(uint regnum, uint8 data) +{ + if (regnum < 0x10) + { + reg[regnum] = data; + switch (regnum) + { + int tmp; + + case 0: // ChA Fine Tune + case 1: // ChA Coarse Tune + tmp = ((reg[0] + reg[1] * 256) & 0xfff); + speriod[0] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 2: // ChB Fine Tune + case 3: // ChB Coarse Tune + tmp = ((reg[2] + reg[3] * 256) & 0xfff); + speriod[1] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 4: // ChC Fine Tune + case 5: // ChC Coarse Tune + tmp = ((reg[4] + reg[5] * 256) & 0xfff); + speriod[2] = tmp ? tperiodbase / tmp : tperiodbase; + break; + + case 6: // Noise generator control + data &= 0x1f; + nperiod = data ? nperiodbase / data : nperiodbase; + break; + + case 8: + olevel[0] = mask & 1 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 9: + olevel[1] = mask & 2 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 10: + olevel[2] = mask & 4 ? EmitTable[(data & 15) * 2 + 1] : 0; + break; + + case 11: // Envelop period + case 12: + tmp = ((reg[11] + reg[12] * 256) & 0xffff); + eperiod = tmp ? eperiodbase / tmp : eperiodbase * 2; + break; + + case 13: // Envelop shape + ecount = 0; + envelop = enveloptable[data & 15]; + break; + } + } +} + +// --------------------------------------------------------------------------- +void PSG::DataSave(struct PSGData* data) { + memcpy(data->reg, reg, 16); + memcpy(data->olevel, olevel, sizeof(uint) * 6); + memcpy(data->scount, scount, sizeof(uint32) * 3); + memcpy(data->speriod, speriod, sizeof(uint32) * 3); + data->ecount = ecount; + data->eperiod = eperiod; + data->ncount = ncount; + data->nperiod = nperiod; + data->tperiodbase = tperiodbase; + data->eperiodbase = eperiodbase; + data->nperiodbase = nperiodbase; + data->volume = volume; + data->mask = mask; +} + +// --------------------------------------------------------------------------- +void PSG::DataLoad(struct PSGData* data) { + memcpy(reg, data->reg, 16); + memcpy(olevel, data->olevel, sizeof(uint) * 6); + memcpy(scount, data->scount, sizeof(uint32) * 3); + memcpy(speriod, data->speriod, sizeof(uint32) * 3); + ecount = data->ecount; + eperiod = data->eperiod; + ncount = data->ncount; + nperiod = data->nperiod; + tperiodbase = data->tperiodbase; + eperiodbase = data->eperiodbase; + nperiodbase = data->nperiodbase; + volume = data->volume; + mask = data->mask; +} + +// --------------------------------------------------------------------------- +// +// +inline void PSG::StoreSample(Sample& dest, int32 data) +{ + if (sizeof(Sample) == 2) + dest = (Sample) Limit(dest + data, 0x7fff, -0x8000); + else + dest += data; +} + +// --------------------------------------------------------------------------- +// PCM データを吐き出す(2ch) +// dest PCM データを展開するポインタ +// nsamples 展開する PCM のサンプル数 +// +void PSG::Mix(Sample* dest, int nsamples) +{ + uint8 chenable[3], nenable[3]; + uint8 r7 = ~reg[7]; + + if ((r7 & 0x3f) | ((reg[8] | reg[9] | reg[10]) & 0x1f)) + { + chenable[0] = (r7 & 0x01) && (speriod[0] <= (1 << toneshift)); + chenable[1] = (r7 & 0x02) && (speriod[1] <= (1 << toneshift)); + chenable[2] = (r7 & 0x04) && (speriod[2] <= (1 << toneshift)); + nenable[0] = (r7 >> 3) & 1; + nenable[1] = (r7 >> 4) & 1; + nenable[2] = (r7 >> 5) & 1; + + int noise, sample; + uint env; + uint* p1 = ((mask & 1) && (reg[ 8] & 0x10)) ? &env : &olevel[0]; + uint* p2 = ((mask & 2) && (reg[ 9] & 0x10)) ? &env : &olevel[1]; + uint* p3 = ((mask & 4) && (reg[10] & 0x10)) ? &env : &olevel[2]; + + #define SCOUNT(ch) (scount[ch] >> (toneshift+oversampling)) + + if (p1 != &env && p2 != &env && p3 != &env) + { + // エンベロープ無し + if ((r7 & 0x38) == 0) + { + // ノイズ無し + for (int i=0; i> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1)); +#else + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1) & 31); +#endif + ncount += nperiod; + + int x, y, z; + x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 + sample += (olevel[0] + x) ^ x; + scount[0] += speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; + sample += (olevel[1] + y) ^ y; + scount[1] += speriod[1]; + z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; + sample += (olevel[2] + z) ^ z; + scount[2] += speriod[2]; + } + sample /= (1 << oversampling); + StoreSample(dest[0], sample); + StoreSample(dest[1], sample); + dest += 2; + } + } + + // エンベロープの計算をさぼった帳尻あわせ + ecount = (ecount >> 8) + (eperiod >> (8-oversampling)) * nsamples; + if (ecount >= (1 << (envshift+6+oversampling-8))) + { + if ((reg[0x0d] & 0x0b) != 0x0a) + ecount |= (1 << (envshift+5+oversampling-8)); + ecount &= (1 << (envshift+6+oversampling-8)) - 1; + } + ecount <<= 8; + } + else + { + // エンベロープあり + for (int i=0; i> (envshift+oversampling)]; + ecount += eperiod; + if (ecount >= (1 << (envshift+6+oversampling))) + { + if ((reg[0x0d] & 0x0b) != 0x0a) + ecount |= (1 << (envshift+5+oversampling)); + ecount &= (1 << (envshift+6+oversampling)) - 1; + } +#ifdef _M_IX86 + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1)); +#else + noise = noisetable[(ncount >> (noiseshift+oversampling+6)) & (noisetablesize-1)] + >> (ncount >> (noiseshift+oversampling+1) & 31); +#endif + ncount += nperiod; + + int x, y, z; + x = ((SCOUNT(0) & chenable[0]) | (nenable[0] & noise)) - 1; // 0 or -1 + sample += (*p1 + x) ^ x; + scount[0] += speriod[0]; + y = ((SCOUNT(1) & chenable[1]) | (nenable[1] & noise)) - 1; + sample += (*p2 + y) ^ y; + scount[1] += speriod[1]; + z = ((SCOUNT(2) & chenable[2]) | (nenable[2] & noise)) - 1; + sample += (*p3 + z) ^ z; + scount[2] += speriod[2]; + } + sample /= (1 << oversampling); + StoreSample(dest[0], sample); + StoreSample(dest[1], sample); + dest += 2; + } + } + } +} + +// --------------------------------------------------------------------------- +// テーブル +// +uint PSG::noisetable[noisetablesize] = { 0, }; +int PSG::EmitTable[0x20] = { -1, }; +uint PSG::enveloptable[16][64] = { {0, } }; diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.h index fe085c585..39d72f074 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_psg.h @@ -1,115 +1,115 @@ -// --------------------------------------------------------------------------- -// PSG-like sound generator -// Copyright (C) cisc 1997, 1999. -// --------------------------------------------------------------------------- -// $Id: psg.h,v 1.8 2003/04/22 13:12:53 cisc Exp $ - -#ifndef PSG_H -#define PSG_H - -#include "fmgen_types.h" - -// libOPNMIDI: change int32 to int16 -#define PSG_SAMPLETYPE int16 // int32 or int16 - -// --------------------------------------------------------------------------- -// class PSG -// PSG に良く似た音を生成する音源ユニット -// -// interface: -// bool SetClock(uint clock, uint rate) -// 初期化.このクラスを使用する前にかならず呼んでおくこと. -// PSG のクロックや PCM レートを設定する -// -// clock: PSG の動作クロック -// rate: 生成する PCM のレート -// retval 初期化に成功すれば true -// -// void Mix(Sample* dest, int nsamples) -// PCM を nsamples 分合成し, dest で始まる配列に加える(加算する) -// あくまで加算なので,最初に配列をゼロクリアする必要がある -// -// void Reset() -// リセットする -// -// void SetReg(uint reg, uint8 data) -// レジスタ reg に data を書き込む -// -// uint GetReg(uint reg) -// レジスタ reg の内容を読み出す -// -// void SetVolume(int db) -// 各音源の音量を調節する -// 単位は約 1/2 dB -// -struct PSGData { - uint8 reg[16]; - uint olevel[3]; - uint32 scount[3]; - uint32 speriod[3]; - uint32 ecount; - uint32 eperiod; - uint32 ncount; - uint32 nperiod; - uint32 tperiodbase; - uint32 eperiodbase; - uint32 nperiodbase; - int volume; - int mask; -}; - -class PSG -{ -public: - typedef PSG_SAMPLETYPE Sample; - - enum - { - noisetablesize = 1 << 11, // ←メモリ使用量を減らしたいなら減らして - toneshift = 24, - envshift = 22, - noiseshift = 14, - oversampling = 2 // ← 音質より速度が優先なら減らすといいかも - }; - -public: - PSG(); - ~PSG(); - - void Mix(Sample* dest, int nsamples); - void SetClock(int clock, int rate); - - void SetVolume(int vol); - void SetChannelMask(int c); - - void Reset(); - void SetReg(uint regnum, uint8 data); - uint GetReg(uint regnum) { return reg[regnum & 0x0f]; } - - void DataSave(struct PSGData* data); - void DataLoad(struct PSGData* data); - -protected: - void MakeNoiseTable(); - void MakeEnvelopTable(); - static void StoreSample(Sample& dest, int32 data); - - uint8 reg[16]; - - const uint* envelop; - uint olevel[3]; - uint32 scount[3], speriod[3]; - uint32 ecount, eperiod; - uint32 ncount, nperiod; - uint32 tperiodbase; - uint32 eperiodbase; - uint32 nperiodbase; - int volume; - int mask; - - static uint enveloptable[16][64]; - static uint noisetable[noisetablesize]; - static int EmitTable[32]; -}; - -#endif // PSG_H +// --------------------------------------------------------------------------- +// PSG-like sound generator +// Copyright (C) cisc 1997, 1999. +// --------------------------------------------------------------------------- +// $Id: psg.h,v 1.8 2003/04/22 13:12:53 cisc Exp $ + +#ifndef PSG_H +#define PSG_H + +#include "fmgen_types.h" + +// libOPNMIDI: change int32 to int16 +#define PSG_SAMPLETYPE int16 // int32 or int16 + +// --------------------------------------------------------------------------- +// class PSG +// PSG に良く似た音を生成する音源ユニット +// +// interface: +// bool SetClock(uint clock, uint rate) +// 初期化.このクラスを使用する前にかならず呼んでおくこと. +// PSG のクロックや PCM レートを設定する +// +// clock: PSG の動作クロック +// rate: 生成する PCM のレート +// retval 初期化に成功すれば true +// +// void Mix(Sample* dest, int nsamples) +// PCM を nsamples 分合成し, dest で始まる配列に加える(加算する) +// あくまで加算なので,最初に配列をゼロクリアする必要がある +// +// void Reset() +// リセットする +// +// void SetReg(uint reg, uint8 data) +// レジスタ reg に data を書き込む +// +// uint GetReg(uint reg) +// レジスタ reg の内容を読み出す +// +// void SetVolume(int db) +// 各音源の音量を調節する +// 単位は約 1/2 dB +// +struct PSGData { + uint8 reg[16]; + uint olevel[3]; + uint32 scount[3]; + uint32 speriod[3]; + uint32 ecount; + uint32 eperiod; + uint32 ncount; + uint32 nperiod; + uint32 tperiodbase; + uint32 eperiodbase; + uint32 nperiodbase; + int volume; + int mask; +}; + +class PSG +{ +public: + typedef PSG_SAMPLETYPE Sample; + + enum + { + noisetablesize = 1 << 11, // ←メモリ使用量を減らしたいなら減らして + toneshift = 24, + envshift = 22, + noiseshift = 14, + oversampling = 2 // ← 音質より速度が優先なら減らすといいかも + }; + +public: + PSG(); + ~PSG(); + + void Mix(Sample* dest, int nsamples); + void SetClock(int clock, int rate); + + void SetVolume(int vol); + void SetChannelMask(int c); + + void Reset(); + void SetReg(uint regnum, uint8 data); + uint GetReg(uint regnum) { return reg[regnum & 0x0f]; } + + void DataSave(struct PSGData* data); + void DataLoad(struct PSGData* data); + +protected: + void MakeNoiseTable(); + void MakeEnvelopTable(); + static void StoreSample(Sample& dest, int32 data); + + uint8 reg[16]; + + const uint* envelop; + uint olevel[3]; + uint32 scount[3], speriod[3]; + uint32 ecount, eperiod; + uint32 ncount, nperiod; + uint32 tperiodbase; + uint32 eperiodbase; + uint32 nperiodbase; + int volume; + int mask; + + static uint enveloptable[16][64]; + static uint noisetable[noisetablesize]; + static int EmitTable[32]; +}; + +#endif // PSG_H diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_readme.txt b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_readme.txt index 05625be57..1d42c245f 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_readme.txt +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_readme.txt @@ -1,93 +1,93 @@ ------------------------------------------------------------------------------- - FM Sound Generator with OPN/OPM interface - Copyright (C) by cisc 1998, 2003. ------------------------------------------------------------------------------- - -【概要】 - C++ による FM/PSG 音源の実装です。 - - AY8910, YM2203, YM2151, YM2608, YM2610 相当のインターフェースも - 実装してあります。 - - -【使い方】 - (TODO:中身を書く) - -【注意】 - 以前のバージョンから幾分手を加えました。インターフェースは - いじっていないつもりですが、何かしらのバグが隠れているかもしれません。 - - YM2610 は動作チェックすらしていません。 - - 線形補完モード(interpolation)は廃止されました。 - Init() の引数仕様は変わっていませんが、interpolation = true にしても - 動作は変わりません。 - - OPNA::Init/SetRate で与えるチップクロックの値の仕様が - 以前の M88 に搭載されていたバージョンと異なっています. - - -【著作権、免責規定】 - - ・本ソースコードは作者(cisc@retropc.net) が著作権を所有しています。 - - ・本ソースコードはあるがままに提供されるものであり, - 暗黙及び明示的な保証を一切含みません. - - ・本ソースコードを利用したこと,利用しなかったこと, - 利用できなかったことに関して生じたあるいは生じると予測される - 損害について,作者は一切責任を負いません. - - ・本ソースコードは,以下の制限を満たす限り自由に改変・組み込み・ - 配布・利用することができます. - - 1. 本ソフトの由来(作者, 著作権)を明記すること. - 2. 配布する際にはフリーソフトとすること. - 3. 改変したソースコードを配布する際は改変内容を明示すること. - 4. ソースコードを配布する際にはこのテキストを一切改変せずに - そのまま添付すること. - - ・公開の際に作者への連絡を頂ければ幸いです. - - ・商用ソフト(シェアウェア含む) に本ソースコードの一部,または - 全部を組み込む際には,事前に作者の合意を得る必要があります. - - -【変更点】 -008 030902 ・出力サンプリングレートの設定を、チップ元来の合成サンプ - リングレートと異なる設定にするとエンベロープがおかしく - なる問題を修正。 - -007a 030608 ・領域外アクセスがあったのを修正 (Thanks to PI.様) - -007 030607 ・再現性の向上 - ・OPN: SSG-EG のサポート - ・線形補完の廃止 - ・asm 版の廃止 - ・マルチスレッドフリーになった? - -006 010330 ・再現性に関していくらか手直し(正弦波,出力タイミング等) - -005 010317 ・OPN: FM 音源の合成周波数が出力周波数よりも低いときに, - 補完を使うと音痴になる/音がまともに出なくなる問題を修正. - ・FM: 補完を使わない時の精度を上げてみる. - 問題が起きたら fmgeninl.h の FM_RATIOBITS を 8 に戻すと吉. - -004a 010311 ・OPM: ノイズをそこそこ聞けるレベル(?)まで修正. - ・OPNA/OPM: FM_USE_CALC2 廃止. - ・デグレ修正, なんのために cvs で管理しているんだか…(T-T - OPNB: ADPCMB ROM マスク作成ミス. - OPNB: ADPCMA ステータスフラグ関係の挙動修復. - OPM: LFO パラメータの初期化を忘れていたのを修正. - -003 010124 ・OPNA/OPM: 実際には補完前の値を出力していたバグを修正. - -002 010123 ・合成周波数が出力周波数より低いときでも補間が効くようにする. - ・OPN: 補間時にプリスケーラの設定を変更したときに音化けする - バグを修正. - ・OPNA/B: LFO が効かないバグを修正. - -001 000928 ・初期バージョン - - -$Id: readme.txt,v 1.1 2003/06/07 08:25:20 cisc Exp $ +------------------------------------------------------------------------------ + FM Sound Generator with OPN/OPM interface + Copyright (C) by cisc 1998, 2003. +------------------------------------------------------------------------------ + +【概要】 + C++ による FM/PSG 音源の実装です。 + + AY8910, YM2203, YM2151, YM2608, YM2610 相当のインターフェースも + 実装してあります。 + + +【使い方】 + (TODO:中身を書く) + +【注意】 + 以前のバージョンから幾分手を加えました。インターフェースは + いじっていないつもりですが、何かしらのバグが隠れているかもしれません。 + + YM2610 は動作チェックすらしていません。 + + 線形補完モード(interpolation)は廃止されました。 + Init() の引数仕様は変わっていませんが、interpolation = true にしても + 動作は変わりません。 + + OPNA::Init/SetRate で与えるチップクロックの値の仕様が + 以前の M88 に搭載されていたバージョンと異なっています. + + +【著作権、免責規定】 + + ・本ソースコードは作者(cisc@retropc.net) が著作権を所有しています。 + + ・本ソースコードはあるがままに提供されるものであり, + 暗黙及び明示的な保証を一切含みません. + + ・本ソースコードを利用したこと,利用しなかったこと, + 利用できなかったことに関して生じたあるいは生じると予測される + 損害について,作者は一切責任を負いません. + + ・本ソースコードは,以下の制限を満たす限り自由に改変・組み込み・ + 配布・利用することができます. + + 1. 本ソフトの由来(作者, 著作権)を明記すること. + 2. 配布する際にはフリーソフトとすること. + 3. 改変したソースコードを配布する際は改変内容を明示すること. + 4. ソースコードを配布する際にはこのテキストを一切改変せずに + そのまま添付すること. + + ・公開の際に作者への連絡を頂ければ幸いです. + + ・商用ソフト(シェアウェア含む) に本ソースコードの一部,または + 全部を組み込む際には,事前に作者の合意を得る必要があります. + + +【変更点】 +008 030902 ・出力サンプリングレートの設定を、チップ元来の合成サンプ + リングレートと異なる設定にするとエンベロープがおかしく + なる問題を修正。 + +007a 030608 ・領域外アクセスがあったのを修正 (Thanks to PI.様) + +007 030607 ・再現性の向上 + ・OPN: SSG-EG のサポート + ・線形補完の廃止 + ・asm 版の廃止 + ・マルチスレッドフリーになった? + +006 010330 ・再現性に関していくらか手直し(正弦波,出力タイミング等) + +005 010317 ・OPN: FM 音源の合成周波数が出力周波数よりも低いときに, + 補完を使うと音痴になる/音がまともに出なくなる問題を修正. + ・FM: 補完を使わない時の精度を上げてみる. + 問題が起きたら fmgeninl.h の FM_RATIOBITS を 8 に戻すと吉. + +004a 010311 ・OPM: ノイズをそこそこ聞けるレベル(?)まで修正. + ・OPNA/OPM: FM_USE_CALC2 廃止. + ・デグレ修正, なんのために cvs で管理しているんだか…(T-T + OPNB: ADPCMB ROM マスク作成ミス. + OPNB: ADPCMA ステータスフラグ関係の挙動修復. + OPM: LFO パラメータの初期化を忘れていたのを修正. + +003 010124 ・OPNA/OPM: 実際には補完前の値を出力していたバグを修正. + +002 010123 ・合成周波数が出力周波数より低いときでも補間が効くようにする. + ・OPN: 補間時にプリスケーラの設定を変更したときに音化けする + バグを修正. + ・OPNA/B: LFO が効かないバグを修正. + +001 000928 ・初期バージョン + + +$Id: readme.txt,v 1.1 2003/06/07 08:25:20 cisc Exp $ diff --git a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_types.h b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_types.h index c06dd4bb5..db1ebfbb9 100644 --- a/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_types.h +++ b/libraries/ZMusic/thirdparty/opnmidi/chips/np2/fmgen_types.h @@ -1,23 +1,23 @@ -#if !defined(win32_types_h) -#define win32_types_h - -#include "compiler.h" - -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; - -typedef signed char sint8; -typedef signed short sint16; -typedef signed int sint32; - -typedef signed char int8; -typedef signed short int16; -typedef signed int int32; - -#endif // win32_types_h +#if !defined(win32_types_h) +#define win32_types_h + +#include "compiler.h" + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +typedef signed char sint8; +typedef signed short sint16; +typedef signed int sint32; + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; + +#endif // win32_types_h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e860b8232..bc4a53a17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -288,8 +288,8 @@ endif() # Check for functions that may or may not exist. -require_stricmp() -require_strnicmp() +gz_require_stricmp() +gz_require_strnicmp() if( NOT MSVC ) add_definitions( -D__forceinline=inline ) @@ -327,32 +327,8 @@ endif() list( APPEND PROJECT_LIBRARIES "zwidget" ) list( APPEND PROJECT_LIBRARIES "webp" ) -# ZMUSIC - -if( MSVC ) - find_package( ZMusic ) -else() - find_package( ZMusic REQUIRED ) -endif() - message("Building for target architecture: ${TARGET_ARCHITECTURE}") -if( MSVC AND NOT ZMUSIC_FOUND ) - # Use prebuilt library - set( ZMUSIC_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../bin/windows/zmusic" ) - set( ZMUSIC_INCLUDE_DIR ${ZMUSIC_ROOT_PATH}/include ) - set( ZMUSIC_LIBRARIES zmusic ) - if( X64 ) - link_directories( ${ZMUSIC_ROOT_PATH}/64bit ) - elseif( ARM64 ) - link_directories( ${ZMUSIC_ROOT_PATH}/arm64 ) - else() - link_directories( ${ZMUSIC_ROOT_PATH}/32bit ) - endif() - set( ZMUSIC_FOUND TRUE ) -endif() - - # VPX if( MSVC AND NOT VPX_FOUND )