diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index dd69c85c..c7812740 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -46,10 +46,10 @@ no plans have been made for TX81Z MIDI passthrough, because: - `28xy`: set reverb of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. -- `29xy`: set EG shift of operator. +- `2Axy`: set waveform of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. -- `2Axy`: set waveform of operator. +- `2Bxy`: set EG shift of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. - `2Fxx`: enable envelope hard reset. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 9b906220..d212b090 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -78,6 +78,8 @@ enum DivDispatchCmds { DIV_CMD_FM_SUS, // (op, value) DIV_CMD_FM_WS, // (op, value) DIV_CMD_FM_SSG, // (op, value) + DIV_CMD_FM_REV, // (op, value) + DIV_CMD_FM_EG_SHIFT, // (op, value) DIV_CMD_FM_FB, // (value) DIV_CMD_FM_MULT, // (op, value) DIV_CMD_FM_FINE, // (op, value) diff --git a/src/engine/engine.h b/src/engine/engine.h index 2669dddb..0c1e07a7 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev90" -#define DIV_ENGINE_VERSION 90 +#define DIV_VERSION "dev91" +#define DIV_ENGINE_VERSION 91 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 5b823606..3fec732b 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -27,7 +27,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 8 +#define CHIP_DIVIDER 4 const char* regCheatSheetAY8930[]={ "FreqL_A", "00", @@ -645,7 +645,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/2.0; break; } - rate=chipClock/8; + rate=chipClock/4; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index dfe83d36..dc121a06 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -940,6 +940,7 @@ void* DivPlatformOPL::getChanState(int ch) { } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { + if (ch>=18) return NULL; return oscBuf[ch]; } diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index 8bb38788..4d19e7de 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -1064,7 +1064,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) { tone = &m_tone[chan]; const int period = std::max(1,tone->period); - tone->count += is_expanded_mode() ? 16 : 1; + tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; while (tone->count >= period) { tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; @@ -1080,7 +1080,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) * channels. */ m_count_noise = 0; - m_prescale_noise ^= 1; + m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1); if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode { @@ -1469,7 +1469,7 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_noise_latch(0), m_mode(0), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), - m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1), + m_step( (feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY) ? 2 : 1), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), @@ -1502,7 +1502,7 @@ void ay8910_device::set_type(psg_type_t psg_type) else { m_env_step_mask = 0x1f; - m_step = 1; + m_step = (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; m_zero_is_off = 0; m_par = &ym2149_param; m_par_env = &ym2149_param_env; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 4cb224ed..496a0434 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -80,6 +80,8 @@ const char* cmdName[]={ "FM_SUS", "FM_WS", "FM_SSG", + "FM_REV", + "FM_EG_SHIFT", "FM_FB", "FM_MULT", "FM_FINE", diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index fcbc370c..007a4e17 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -75,7 +75,7 @@ bool DivSample::save(const char* path) { } sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst)); - sf_write_short(f,data16,length16); + sf_writef_short(f,data16,samples); sf_close(f); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 36331f70..405f39d9 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -319,6 +319,18 @@ int DivEngine::minVGMVersion(DivSystem which) { #define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) #define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ) +#define OP_EFFECT_MULTI(x,c,op,mask) \ + case x: \ + dispatchCmd(DivCommand(c,ch,op,effectVal&mask)); \ + break; + +#define OP_EFFECT_SINGLE(x,c,maxOp,mask) \ + case x: \ + if ((effectVal>>4)>=0 && (effectVal>>4)<=maxOp) { \ + dispatchCmd(DivCommand(c,ch,(effectVal>>4)-1,effectVal&mask)); \ + } \ + break; + // define systems like: // sysDefs[DIV_SYSTEM_ID]=new DivSysDef( // "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, @@ -436,6 +448,54 @@ void DivEngine::registerSystems() { dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); } break; + // fixed frequency effects on OPZ + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,0,((effect&7)<<8)|effectVal)); + } + break; + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,1,((effect&7)<<8)|effectVal)); + } + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,2,((effect&7)<<8)|effectVal)); + } + break; + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,3,((effect&7)<<8)|effectVal)); + } + break; + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,4,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,4,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,4,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_DT,4,7); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,4,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SSG,4,(IS_OPM_LIKE?3:15)); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,31); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,31); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,31); + OP_EFFECT_MULTI(0x59,DIV_CMD_FM_DR,2,31); + OP_EFFECT_MULTI(0x5a,DIV_CMD_FM_DR,3,31); + + OP_EFFECT_MULTI(0x5b,DIV_CMD_FM_D2R,-1,31); + OP_EFFECT_MULTI(0x5c,DIV_CMD_FM_D2R,0,31); + OP_EFFECT_MULTI(0x5d,DIV_CMD_FM_D2R,1,31); + OP_EFFECT_MULTI(0x5e,DIV_CMD_FM_D2R,2,31); + OP_EFFECT_MULTI(0x5f,DIV_CMD_FM_D2R,3,31); + + OP_EFFECT_SINGLE(0x28,DIV_CMD_FM_REV,4,7); + OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); + OP_EFFECT_SINGLE(0x2b,DIV_CMD_FM_EG_SHIFT,4,3); default: return false; } @@ -467,6 +527,21 @@ void DivEngine::registerSystems() { case 0x1b: // AR op2 dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); break; + + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,2,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,2,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,2,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_VIB,2,1); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,2,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SUS,2,1); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,15); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,15); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,15); + + OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,2,1); + OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); default: return false; } @@ -516,6 +591,23 @@ void DivEngine::registerSystems() { case 0x1d: // AR op4 dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&15)); break; + + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,4,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,4,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,4,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_VIB,4,1); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,4,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SUS,4,1); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,15); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,15); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,15); + OP_EFFECT_MULTI(0x59,DIV_CMD_FM_DR,2,15); + OP_EFFECT_MULTI(0x5a,DIV_CMD_FM_DR,3,15); + + OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,4,1); + default: return false; }