Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

This commit is contained in:
cam900 2023-01-19 16:51:27 +09:00
commit 7340d029ae
65 changed files with 836 additions and 466 deletions

View File

@ -11,7 +11,7 @@ defaults:
shell: bash
env:
BUILD_TYPE: Release
BUILD_TYPE: Debug
jobs:
build:
@ -278,6 +278,9 @@ jobs:
cp -v ../LICENSE LICENSE.txt
cp -v ../README.md README.txt
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then
cp -v ../${binPath}/furnace.pdb ./
fi
sha256sum ../${binPath}/furnace.exe > checksum.txt
popd

BIN
demos/snes/amalgam.fur Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Nuke.YKT
* Copyright (C) 2019-2023 Nuke.YKT
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,7 +17,7 @@
* siliconpr0n.org(digshadow, John McMaster):
* VRC VII decap and die shot.
*
* version: 1.0.1
* version: 1.0.2
*/
#include <string.h>
@ -132,7 +132,7 @@ static const opll_patch_t patch_ds1001[opll_patch_max] = {
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } },
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } },
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
};
@ -1018,6 +1018,9 @@ static void OPLL_Operator(opll_t *chip) {
}
}
if (!(chip->rm_enable & 0x80))
routput = 0;
chip->ch_out = ismod1 ? routput : (output>>3);
if (!ismod1) {

View File

@ -30,6 +30,9 @@
// hack I know
#include "../../../src/utfutils.h"
// hack 2...
#include "../../../src/ta-log.h"
class NFDWinEvents: public IFileDialogEvents {
nfdselcallback_t selCallback;
size_t refCount;
@ -38,21 +41,21 @@ class NFDWinEvents: public IFileDialogEvents {
}
public:
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
printf("QueryInterface called DAMN IT\n");
logV("%p: QueryInterface called DAMN IT",(const void*)this);
*ppv=NULL;
return E_NOTIMPL;
}
IFACEMETHODIMP_(ULONG) AddRef() {
printf("AddRef() called\n");
logV("%p: AddRef() called",(const void*)this);
return InterlockedIncrement(&refCount);
}
IFACEMETHODIMP_(ULONG) Release() {
printf("Release() called\n");
logV("%p: Release() called",(const void*)this);
LONG ret=InterlockedDecrement(&refCount);
if (ret==0) {
printf("Destroying the final object.\n");
logV("%p: Destroying the final object.",(const void*)this);
delete this;
}
return ret;
@ -67,30 +70,40 @@ class NFDWinEvents: public IFileDialogEvents {
IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) {
// Get the file name
logV("%p: OnSelectionChange() called",(const void*)this);
::IShellItem *shellItem(NULL);
logV("%p: GetCurrentSelection",(const void*)this);
HRESULT result = dialog->GetCurrentSelection(&shellItem);
if ( !SUCCEEDED(result) )
{
printf("failure!\n");
logV("%p: failure!",(const void*)this);
return S_OK;
}
wchar_t *filePath(NULL);
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
if ( !SUCCEEDED(result) )
{
printf("GDN failure!\n");
logV("%p: GDN failure!",(const void*)this);
shellItem->Release();
return S_OK;
}
std::string utf8FilePath=utf16To8(filePath);
if (selCallback!=NULL) selCallback(utf8FilePath.c_str());
printf("I got you for a value of %s\n",utf8FilePath.c_str());
if (selCallback!=NULL) {
logV("%p: calling back.",(const void*)this);
selCallback(utf8FilePath.c_str());
logV("%p: end of callback",(const void*)this);
} else {
logV("%p: no callback.",(const void*)this);
}
logV("%p: I got you for a value of %s",(const void*)this,utf8FilePath.c_str());
shellItem->Release();
logV("%p: shellItem->Release()",(const void*)this);
return S_OK;
}
NFDWinEvents(nfdselcallback_t callback):
selCallback(callback),
refCount(1) {
logV("%p: CONSTRUCT!",(const void*)this);
}
};

View File

@ -135,7 +135,7 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx
W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | Waveform | Waveform | Waveform | Waveform | Waveform | | | | Waveform | |
1 | | AMD | AMD | | | Duty | | FilterMode | Envelope | EnvMode | WaveLen | Mod Depth | Cutoff | Filter K1 | ClockDiv | EchoFeedback | Special | GroupAtk |
2 | | PMD | PMD | | Envelope | Envelope | | Resonance | | Envelope | WaveUpdate | Mod Speed | Resonance | Filter K2 | | Echo Length | Gain | GroupDec |
3 | | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise |
3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise |
A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | |
B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | |
C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | |
@ -144,4 +144,4 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx
5 | | | AMD2 | | | | | | | | | | | EnvRampR | | | | |
6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | |
7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | |
8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | |
8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | |

View File

@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
- 138: Furnace dev138
- 137: Furnace dev137
- 136: Furnace dev136
- 135: Furnace dev135
- 134: Furnace dev134
@ -398,6 +400,9 @@ size | description
4?? | patchbay
| - see next section for more details.
1 | automatic patchbay (>=136)
--- | **a couple more compat flags** (>=138)
1 | broken portamento during legato
7 | reserved
```
# patchbay

View File

@ -58,7 +58,7 @@ bool TAMidiInRtMidi::gather() {
if (m.type!=TA_MIDI_SYSEX && msg.size()>1) {
memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7));
} else if (m.type==TA_MIDI_SYSEX) {
m.sysExData.reset(new unsigned char[msg.size()]);
m.sysExData=std::shared_ptr<unsigned char>(new unsigned char[msg.size()],std::default_delete<unsigned char[]>());
m.sysExLen=msg.size();
logD("got a SysEx of length %ld!",msg.size());
memcpy(m.sysExData.get(),msg.data(),msg.size());

View File

@ -47,8 +47,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev136"
#define DIV_ENGINE_VERSION 136
#define DIV_VERSION "dev138"
#define DIV_ENGINE_VERSION 138
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02

View File

@ -1716,6 +1716,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<130) {
ds.oldArpStrategy=true;
}
if (ds.version<138) {
ds.brokenPortaLegato=true;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -2221,6 +2224,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version>=136) song.patchbayAuto=reader.readC();
if (ds.version>=138) {
ds.brokenPortaLegato=reader.readC();
for (int i=0; i<7; i++) {
reader.readC();
}
}
// read system flags
if (ds.version>=119) {
logD("reading chip flags...");
@ -2574,6 +2584,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// new YM2612/SN/X1-010 volumes
if (ds.version<137) {
for (int i=0; i<ds.systemLen; i++) {
switch (ds.system[i]) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2612_DUALPCM:
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
case DIV_SYSTEM_YM2612_CSM:
ds.systemVol[i]/=2.0;
break;
case DIV_SYSTEM_SMS:
case DIV_SYSTEM_T6W28:
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
ds.systemVol[i]/=1.5;
break;
case DIV_SYSTEM_X1_010:
ds.systemVol[i]/=4.0;
break;
default:
break;
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -4489,6 +4525,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
}
w->writeC(song.patchbayAuto);
// even more compat flags
w->writeC(song.brokenPortaLegato);
for (int i=0; i<7; i++) {
w->writeC(0);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);

View File

@ -18,6 +18,7 @@
*/
#include "../dispatch.h"
#include "../../ta-log.h"
void DivDispatch::acquire(short** buf, size_t len) {
}
@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) {
}
void DivDispatch::notifyInsDeletion(void* ins) {
logE("notifyInsDeletion NOT implemented!");
abort();
}
void DivDispatch::notifyPlaybackStop() {

View File

@ -382,53 +382,57 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
}
}
void DivPlatformArcade::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator op=chan[ch].state.op[i];
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
if (chan[ch].insChanged) {
if (isMuted[ch]) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
} else {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}
rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3));
}
}
int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
if (chan[c.chan].insChanged) {
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -521,6 +525,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_LINEAR(c.value);
chan[c.chan].freqChanged=true;
break;
@ -800,6 +809,12 @@ void DivPlatformArcade::notifyInsChange(int ins) {
}
}
void DivPlatformArcade::notifyInsDeletion(void* ins) {
for (int i=0; i<8; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void* DivPlatformArcade::getChanState(int ch) {
return &chan[ch];
}

View File

@ -57,6 +57,7 @@ class DivPlatformArcade: public DivPlatformOPM {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
void acquire_nuked(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -76,6 +77,7 @@ class DivPlatformArcade: public DivPlatformOPM {
void muteChannel(int ch, bool mute);
DivMacroInt* getChanMacroInt(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setFlags(const DivConfig& flags);
int getOutputCount();
void setYMFM(bool use);

View File

@ -131,6 +131,10 @@ int DivPlatformDummy::dispatch(DivCommand c) {
return 1;
}
void DivPlatformDummy::notifyInsDeletion(void* ins) {
// nothing
}
void DivPlatformDummy::reset() {
for (int i=0; i<chans; i++) {
chan[i]=DivPlatformDummy::Channel();

View File

@ -40,6 +40,7 @@ class DivPlatformDummy: public DivDispatch {
void acquire(short** buf, size_t len);
void muteChannel(int ch, bool mute);
int dispatch(DivCommand c);
void notifyInsDeletion(void* ins);
void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();

View File

@ -83,6 +83,7 @@ class DivPlatformFMBase: public DivDispatch {
unsigned char lastBusy;
int delay;
bool flushFirst;
unsigned char regPool[512];
short oldWrites[512];
@ -102,7 +103,7 @@ class DivPlatformFMBase: public DivDispatch {
}
}
inline void urgentWrite(unsigned short a, unsigned char v) {
if (!skipRegisterWrites) {
if (!skipRegisterWrites && !flushFirst) {
if (writes.empty()) {
writes.push_back(QueuedWrite(a,v));
} else if (writes.size()>16 || writes.front().addrOrVal) {
@ -118,9 +119,11 @@ class DivPlatformFMBase: public DivDispatch {
friend void putDispatchChan(void*,int,int);
DivPlatformFMBase():DivDispatch(),
lastBusy(0),
delay(0) {}
DivPlatformFMBase():
DivDispatch(),
lastBusy(0),
delay(0),
flushFirst(false) {}
};
#endif

View File

@ -34,7 +34,7 @@ void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clock
} else if (tnum==0) {
countA=duration_in_clocks;
}
logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks);
//logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks);
}
void DivYM2612Interface::clock() {
@ -141,23 +141,26 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
os[0]=0; os[1]=0;
for (int i=0; i<6; i++) {
if (!writes.empty() && --delay<0) {
delay=0;
QueuedWrite& w=writes.front();
if (w.addrOrVal) {
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
//printf("write: %x = %.2x\n",w.addr,w.val);
lastBusy=0;
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
} else {
lastBusy++;
if (fm.write_busy==0) {
//printf("busycounter: %d\n",lastBusy);
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
w.addrOrVal=true;
if (!writes.empty()) {
if (--delay<0) {
delay=0;
QueuedWrite& w=writes.front();
if (w.addrOrVal) {
//logV("%.3x = %.2x",w.addr,w.val);
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
lastBusy=0;
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
} else {
lastBusy++;
if (fm.write_busy==0) {
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
w.addrOrVal=true;
}
}
}
} else {
flushFirst=false;
}
OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1];
@ -207,6 +210,8 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
regPool[w.addr&0x1ff]=w.val;
writes.pop_front();
lastBusy=1;
} else {
flushFirst=false;
}
if (ladder) {
@ -389,6 +394,10 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ex3.had) {
lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
if (chan[i].std.ex4.had && chan[i].active) {
chan[i].opMask=chan[i].std.ex4.val&15;
chan[i].opMaskChanged=true;
@ -454,6 +463,10 @@ void DivPlatformGenesis::tick(bool sysTick) {
for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
if (i==0x2b && pendingWrites[i]!=0 && !parent->song.brokenDACMode) {
if (chan[5].keyOn) chan[5].keyOn=false;
chan[5].keyOff=true;
}
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
@ -556,6 +569,47 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
void DivPlatformGenesis::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
}
int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
@ -641,49 +695,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
if (c.chan>=6) break;
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -866,6 +883,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
}
chan[c.chan].note=c.value;
@ -1120,16 +1142,20 @@ void DivPlatformGenesis::forceIns() {
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (chan[i].active) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
if (i<5 || !chan[i].dacMode) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
}
}
}
immWrite(0x2b,0x00);
//rWrite(0x2a,0x00);
if (chan[5].dacMode) {
chan[5].dacSample=-1;
chan[6].dacSample=-1;
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);
flushFirst=true;
}
void DivPlatformGenesis::toggleRegisterDump(bool enable) {
@ -1156,6 +1182,10 @@ int DivPlatformGenesis::getRegisterPoolSize() {
return 512;
}
float DivPlatformGenesis::getPostAmp() {
return 2.0f;
}
void DivPlatformGenesis::reset() {
while (!writes.empty()) writes.pop_front();
memset(regPool,0,512);
@ -1183,6 +1213,7 @@ void DivPlatformGenesis::reset() {
lfoValue=8;
softPCMTimer=0;
extMode=false;
flushFirst=false;
if (softPCM) {
chan[5].dacMode=true;
@ -1219,6 +1250,9 @@ void DivPlatformGenesis::notifyInsChange(int ins) {
}
void DivPlatformGenesis::notifyInsDeletion(void* ins) {
for (int i=0; i<10; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformGenesis::poke(unsigned int addr, unsigned short val) {
@ -1285,6 +1319,7 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, const DivC
dumpWrites=false;
ladder=false;
skipRegisterWrites=false;
flushFirst=false;
for (int i=0; i<10; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;

View File

@ -92,6 +92,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
friend void putDispatchChan(void*,int,int);
inline void processDAC(int iRate);
inline void commitState(int ch, DivInstrument* ins);
void acquire_nuked(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -114,10 +115,11 @@ class DivPlatformGenesis: public DivPlatformOPN {
void setYMFM(bool use);
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);
float getPostAmp();
void toggleRegisterDump(bool enable);
void setFlags(const DivConfig& flags);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
virtual void notifyInsDeletion(void* ins);
void setSoftPCM(bool value);
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);

View File

@ -746,6 +746,13 @@ void DivPlatformGenesisExt::notifyInsChange(int ins) {
}
}
void DivPlatformGenesisExt::notifyInsDeletion(void* ins) {
DivPlatformGenesis::notifyInsDeletion(ins);
for (int i=0; i<4; i++) {
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
int DivPlatformGenesisExt::getPortaFloor(int ch) {
return (ch>8)?12:0;
}

View File

@ -41,6 +41,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
int getPortaFloor(int ch);
void setCSMChannel(unsigned char ch);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);

View File

@ -353,6 +353,9 @@ void DivPlatformMSM6258::notifyInsChange(int ins) {
}
void DivPlatformMSM6258::notifyInsDeletion(void* ins) {
for (int i=0; i<1; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformMSM6258::setFlags(const DivConfig& flags) {

View File

@ -326,6 +326,9 @@ void DivPlatformMSM6295::notifyInsChange(int ins) {
}
void DivPlatformMSM6295::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
const void* DivPlatformMSM6295::getSampleMem(int index) {

View File

@ -689,6 +689,112 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
}
}
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
chan[melodicChans+i+1].state.alg=ins->fm.alg;
chan[melodicChans+i+1].state.fb=ins->fm.fb;
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
}
} else {
chan[ch].state=ins->fm;
}
}
if (chan[ch].insChanged) {
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
int ch=melodicChans+1+i;
unsigned char slot=slots[0][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
chan[ch].fourOp=false;
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
} else {
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
chan[ch].fourOp=(ops==4);
if (chan[ch].fourOp) {
/*
if (chan[ch+1].active) {
chan[ch+1].keyOff=true;
chan[ch+1].keyOn=false;
chan[ch+1].active=false;
}*/
chan[ch+1].insChanged=true;
chan[ch+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[(ops==4)?orderedOpsL[i]:i];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (KVSL(ch,i) || ch>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1));
}
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
if (ops==4) {
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
}
}
}
int DivPlatformOPL::dispatch(DivCommand c) {
if (c.chan>=totalChans && c.chan!=adpcmChan) return 0;
// ineffective in 4-op mode
@ -771,114 +877,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL);
if (chan[c.chan].insChanged) {
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
chan[melodicChans+i+1].state.alg=ins->fm.alg;
chan[melodicChans+i+1].state.fb=ins->fm.fb;
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
}
} else {
chan[c.chan].state=ins->fm;
}
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].insChanged) {
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
int ch=melodicChans+1+i;
unsigned char slot=slots[0][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
chan[ch].fourOp=false;
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
} else {
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) {
/*
if (chan[c.chan+1].active) {
chan[c.chan+1].keyOff=true;
chan[c.chan+1].keyOn=false;
chan[c.chan+1].active=false;
}*/
chan[c.chan+1].insChanged=true;
chan[c.chan+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][c.chan];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (KVSL(c.chan,i) || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
if (isMuted[c.chan]) {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
}
} else {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
}
}
}
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -1075,6 +1079,11 @@ int DivPlatformOPL::dispatch(DivCommand c) {
iface.sampleBank=sampleBank;
break;
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
@ -1640,6 +1649,9 @@ void DivPlatformOPL::notifyInsChange(int ins) {
}
void DivPlatformOPL::notifyInsDeletion(void* ins) {
for (int i=0; i<totalChans; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformOPL::poke(unsigned int addr, unsigned short val) {

View File

@ -100,6 +100,7 @@ class DivPlatformOPL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
double NOTE_ADPCMB(int note);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);

View File

@ -19,6 +19,7 @@
#include "opll.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <string.h>
#include <math.h>
@ -331,6 +332,55 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
}
if (chan[ch].insChanged) {
// update custom preset
if (chan[ch].state.opllPreset==0) {
DivInstrumentFM::Operator& mod=chan[ch].state.op[0];
DivInstrumentFM::Operator& car=chan[ch].state.op[1];
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
rWrite(0x02,(mod.ksl<<6)|(mod.tl&63));
rWrite(0x03,(car.ksl<<6)|((chan[ch].state.fms&1)<<4)|((chan[ch].state.ams&1)<<3)|chan[ch].state.fb);
rWrite(0x04,(mod.ar<<4)|(mod.dr));
rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(0x06,(mod.sl<<4)|(mod.rr));
rWrite(0x07,(car.sl<<4)|(car.rr));
lastCustomMemory=ch;
}
if (chan[ch].state.opllPreset==16) { // compatible drums mode
if (ch>=6) {
drums=true;
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x18,0xC0);
immWrite(0x28,0x01);
}
} else {
if (ch>=6) {
if (drums) {
drums=false;
immWrite(0x0e,0);
drumState=0;
}
}
if (ch<9) {
rWrite(0x30+ch,((15-VOL_SCALE_LOG_BROKEN(chan[ch].outVol,15-chan[ch].state.op[1].tl,15))&15)|(chan[ch].state.opllPreset<<4));
}
}
}
}
int DivPlatformOPLL::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
@ -375,49 +425,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break;
}
if (chan[c.chan].insChanged) {
// update custom preset
if (chan[c.chan].state.opllPreset==0) {
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
rWrite(0x02,(mod.ksl<<6)|(mod.tl&63));
rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb);
rWrite(0x04,(mod.ar<<4)|(mod.dr));
rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(0x06,(mod.sl<<4)|(mod.rr));
rWrite(0x07,(car.sl<<4)|(car.rr));
lastCustomMemory=c.chan;
}
if (chan[c.chan].state.opllPreset==16) { // compatible drums mode
if (c.chan>=6) {
drums=true;
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x18,0xC0);
immWrite(0x28,0x01);
}
} else {
if (c.chan>=6) {
if (drums) {
drums=false;
immWrite(0x0e,0);
drumState=0;
}
}
if (c.chan<9) {
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -541,6 +549,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
}
case DIV_CMD_LEGATO: {
if (c.chan>=9 && !properDrums) return 0;
if (c.chan<6 || (!drums && !properDrums)) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
}
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
@ -940,6 +955,9 @@ void DivPlatformOPLL::notifyInsChange(int ins) {
}
void DivPlatformOPLL::notifyInsDeletion(void* ins) {
for (int i=0; i<11; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformOPLL::poke(unsigned int addr, unsigned short val) {
@ -958,6 +976,10 @@ void DivPlatformOPLL::setYMFM(bool use) {
useYMFM=use;
}
float DivPlatformOPLL::getPostAmp() {
return 1.5f;
}
void DivPlatformOPLL::setFlags(const DivConfig& flags) {
int clockSel=flags.getInt("clockSel",0);
if (clockSel==3) {

View File

@ -73,6 +73,7 @@ class DivPlatformOPLL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
@ -95,6 +96,7 @@ class DivPlatformOPLL: public DivDispatch {
void setYMFM(bool use);
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);
float getPostAmp();
void toggleRegisterDump(bool enable);
void setVRC7(bool vrc);
void setProperDrums(bool pd);

View File

@ -606,7 +606,7 @@ void DivPlatformQSound::forceIns() {
for (int i=0; i<19; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
//chan[i].sample=-1;
}
}
@ -645,7 +645,7 @@ bool DivPlatformQSound::keyOffAffectsArp(int ch) {
}
void DivPlatformQSound::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
for (int i=0; i<19; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -657,7 +657,7 @@ void DivPlatformQSound::notifyWaveChange(int wave) {
}
void DivPlatformQSound::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
for (int i=0; i<19; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}

View File

@ -406,6 +406,12 @@ void DivPlatformSegaPCM::notifyInsChange(int ins) {
}
}
void DivPlatformSegaPCM::notifyInsDeletion(void* ins) {
for (int i=0; i<16; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void* DivPlatformSegaPCM::getChanState(int ch) {
return &chan[ch];
}

View File

@ -89,6 +89,7 @@ class DivPlatformSegaPCM: public DivDispatch {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void renderSamples(int chipID);
void setFlags(const DivConfig& flags);
int getOutputCount();

View File

@ -39,6 +39,10 @@ const char** DivPlatformSMS::getRegisterSheet() {
return stereo?regCheatSheetGG:regCheatSheetSN;
}
float DivPlatformSMS::getPostAmp() {
return 1.5f;
}
void DivPlatformSMS::acquire_nuked(short** buf, size_t len) {
int oL=0;
int oR=0;

View File

@ -81,6 +81,7 @@ class DivPlatformSMS: public DivDispatch {
int getOutputCount();
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);
float getPostAmp();
int getPortaFloor(int ch);
void setFlags(const DivConfig& flags);
void notifyInsDeletion(void* ins);

View File

@ -135,7 +135,7 @@ void DivPlatformTIA::tick(bool sysTick) {
int bf=chan[i].baseFreq;
if (!parent->song.oldArpStrategy) {
if (!chan[i].fixedArp) {
bf+=chan[i].baseFreq+chan[i].arpOff;
bf+=chan[i].arpOff;
}
}
chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch)+chan[i].pitch2;

View File

@ -350,56 +350,60 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) {
}
}
void DivPlatformTX81Z::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator op=chan[ch].state.op[i];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
}
}
if (chan[ch].insChanged) {
/*
if (isMuted[ch]) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
} else {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}*/
rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3));
//rWrite(chanOffs[ch]+ADDR_FMS_AMS,0x84|((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3));
}
}
int DivPlatformTX81Z::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4));
rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6));
}
}
if (chan[c.chan].insChanged) {
/*
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}*/
rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3));
//rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,0x84|((chan[c.chan].state.fms2&7)<<4)|(chan[c.chan].state.ams2&3));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -498,6 +502,11 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_LINEAR(c.value);
chan[c.chan].freqChanged=true;
break;
@ -873,6 +882,12 @@ void DivPlatformTX81Z::notifyInsChange(int ins) {
}
}
void DivPlatformTX81Z::notifyInsDeletion(void* ins) {
for (int i=0; i<8; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void* DivPlatformTX81Z::getChanState(int ch) {
return &chan[ch];
}

View File

@ -57,6 +57,7 @@ class DivPlatformTX81Z: public DivPlatformOPM {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
public:
@ -72,6 +73,7 @@ class DivPlatformTX81Z: public DivPlatformOPM {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setFlags(const DivConfig& flags);
int getOutputCount();
void poke(unsigned int addr, unsigned short val);

View File

@ -411,7 +411,7 @@ int DivPlatformVERA::getOutputCount() {
}
void DivPlatformVERA::notifyInsDeletion(void* ins) {
for (int i=0; i<2; i++) {
for (int i=0; i<17; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}

View File

@ -903,6 +903,10 @@ bool DivPlatformX1_010::keyOffAffectsArp(int ch) {
return true;
}
float DivPlatformX1_010::getPostAmp() {
return 4.0f;
}
void DivPlatformX1_010::notifyWaveChange(int wave) {
for (int i=0; i<16; i++) {
if (chan[i].wave==wave) {

View File

@ -140,6 +140,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf {
void muteChannel(int ch, bool mute);
int getOutputCount();
bool keyOffAffectsArp(int ch);
float getPostAmp();
void setFlags(const DivConfig& flags);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View File

@ -474,6 +474,46 @@ void DivPlatformYM2203::tick(bool sysTick) {
}
}
void DivPlatformYM2203::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
}
}
int DivPlatformYM2203::dispatch(DivCommand c) {
if (c.chan>2) {
c.chan-=3;
@ -489,43 +529,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
}
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -616,6 +620,11 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
chan[c.chan].freqChanged=true;
break;
@ -986,6 +995,9 @@ void DivPlatformYM2203::notifyInsChange(int ins) {
void DivPlatformYM2203::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<3; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformYM2203::setSkipRegisterWrites(bool value) {

View File

@ -55,6 +55,8 @@ class DivPlatformYM2203: public DivPlatformOPN {
friend void putDispatchChip(void*,int);
inline void commitState(int ch, DivInstrument* ins);
void acquire_combo(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -73,7 +75,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
int getOutputCount();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
virtual void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -641,6 +641,13 @@ void DivPlatformYM2203Ext::notifyInsChange(int ins) {
}
}
void DivPlatformYM2203Ext::notifyInsDeletion(void* ins) {
DivPlatformYM2203::notifyInsDeletion(ins);
for (int i=0; i<4; i++) {
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2203::init(parent,channels,sugRate,flags);
for (int i=0; i<4; i++) {

View File

@ -39,6 +39,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformYM2203Ext();

View File

@ -545,6 +545,10 @@ void DivPlatformYM2608::tick(bool sysTick) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ex3.had) {
lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
if (chan[i].std.ex4.had && chan[i].active) {
chan[i].opMask=chan[i].std.ex4.val&15;
chan[i].opMaskChanged=true;
@ -785,6 +789,43 @@ void DivPlatformYM2608::tick(bool sysTick) {
ay->getRegisterWrites().clear();
}
void DivPlatformYM2608::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
}
int DivPlatformYM2608::dispatch(DivCommand c) {
if (c.chan>5 && c.chan<9) {
c.chan-=6;
@ -898,40 +939,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
}
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -1059,6 +1067,13 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
break;
case DIV_CMD_LEGATO: {
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
if (c.chan<=psgChanOffs) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
}
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
chan[c.chan].freqChanged=true;
break;
@ -1454,6 +1469,12 @@ void DivPlatformYM2608::notifyInsChange(int ins) {
void DivPlatformYM2608::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<psgChanOffs; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
for (int i=adpcmAChanOffs; i<chanNum; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformYM2608::setSkipRegisterWrites(bool value) {

View File

@ -70,6 +70,8 @@ class DivPlatformYM2608: public DivPlatformOPN {
friend void putDispatchChip(void*,int);
inline void commitState(int ch, DivInstrument* ins);
void acquire_combo(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -88,7 +90,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
int getOutputCount();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
virtual void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -679,6 +679,13 @@ void DivPlatformYM2608Ext::notifyInsChange(int ins) {
}
}
void DivPlatformYM2608Ext::notifyInsDeletion(void* ins) {
DivPlatformYM2608::notifyInsDeletion(ins);
for (int i=0; i<4; i++) {
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
int DivPlatformYM2608Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2608::init(parent,channels,sugRate,flags);
for (int i=0; i<4; i++) {

View File

@ -39,6 +39,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformYM2608Ext();

View File

@ -478,6 +478,10 @@ void DivPlatformYM2610::tick(bool sysTick) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ex3.had) {
lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
if (chan[i].std.ex4.had && chan[i].active) {
chan[i].opMask=chan[i].std.ex4.val&15;
chan[i].opMaskChanged=true;
@ -720,6 +724,43 @@ void DivPlatformYM2610::tick(bool sysTick) {
ay->getRegisterWrites().clear();
}
void DivPlatformYM2610::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
}
int DivPlatformYM2610::dispatch(DivCommand c) {
if (c.chan>=psgChanOffs && c.chan<adpcmAChanOffs) {
c.chan-=psgChanOffs;
@ -874,40 +915,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
}
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -1035,6 +1043,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
case DIV_CMD_LEGATO: {
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
if (c.chan<=psgChanOffs) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
}
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
chan[c.chan].freqChanged=true;
break;
@ -1402,6 +1417,12 @@ void DivPlatformYM2610::notifyInsChange(int ins) {
void DivPlatformYM2610::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<psgChanOffs; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
for (int i=adpcmAChanOffs; i<chanNum; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformYM2610::setSkipRegisterWrites(bool value) {

View File

@ -38,6 +38,8 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
friend void putDispatchChip(void*,int);
void commitState(int ch, DivInstrument* ins);
void acquire_combo(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -56,7 +58,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
int getOutputCount();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
virtual void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -545,6 +545,10 @@ void DivPlatformYM2610B::tick(bool sysTick) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ex3.had) {
lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7));
rWrite(0x22,lfoValue);
}
if (chan[i].std.ex4.had && chan[i].active) {
chan[i].opMask=chan[i].std.ex4.val&15;
chan[i].opMaskChanged=true;
@ -787,6 +791,43 @@ void DivPlatformYM2610B::tick(bool sysTick) {
ay->getRegisterWrites().clear();
}
void DivPlatformYM2610B::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
}
int DivPlatformYM2610B::dispatch(DivCommand c) {
if (c.chan>=psgChanOffs && c.chan<adpcmAChanOffs) {
c.chan-=psgChanOffs;
@ -941,40 +982,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
}
}
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -1102,6 +1110,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
break;
case DIV_CMD_LEGATO: {
if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break;
if (c.chan<=psgChanOffs) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
}
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
chan[c.chan].freqChanged=true;
break;
@ -1473,6 +1488,12 @@ void DivPlatformYM2610B::notifyInsChange(int ins) {
void DivPlatformYM2610B::notifyInsDeletion(void* ins) {
ay->notifyInsDeletion(ins);
for (int i=0; i<psgChanOffs; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
for (int i=adpcmAChanOffs; i<chanNum; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformYM2610B::setSkipRegisterWrites(bool value) {

View File

@ -34,6 +34,8 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
friend void putDispatchChip(void*,int);
void commitState(int ch, DivInstrument* ins);
void acquire_combo(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);
@ -52,7 +54,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
int getOutputCount();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
virtual void notifyInsDeletion(void* ins);
void setSkipRegisterWrites(bool val);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -670,6 +670,13 @@ void DivPlatformYM2610BExt::notifyInsChange(int ins) {
}
}
void DivPlatformYM2610BExt::notifyInsDeletion(void* ins) {
DivPlatformYM2610B::notifyInsDeletion(ins);
for (int i=0; i<4; i++) {
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2610B::init(parent,channels,sugRate,flags);
for (int i=0; i<4; i++) {

View File

@ -39,6 +39,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformYM2610BExt();

View File

@ -670,6 +670,13 @@ void DivPlatformYM2610Ext::notifyInsChange(int ins) {
}
}
void DivPlatformYM2610Ext::notifyInsDeletion(void* ins) {
DivPlatformYM2610::notifyInsDeletion(ins);
for (int i=0; i<4; i++) {
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) {
DivPlatformYM2610::init(parent,channels,sugRate,flags);
for (int i=0; i<4; i++) {

View File

@ -39,6 +39,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformYM2610Ext();

View File

@ -908,7 +908,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].vibratoPos=0;
}
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
if (chan[i].legato) {
if (chan[i].legato && (!chan[i].inPorta || song.brokenPortaLegato)) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
} else {

View File

@ -330,6 +330,7 @@ struct DivSong {
bool autoSystem;
bool oldArpStrategy;
bool patchbayAuto;
bool brokenPortaLegato;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -439,7 +440,8 @@ struct DivSong {
disableSampleMacro(false),
autoSystem(true),
oldArpStrategy(false),
patchbayAuto(true) {
patchbayAuto(true),
brokenPortaLegato(false) {
for (int i=0; i<DIV_MAX_CHIPS; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=1.0;

View File

@ -1086,7 +1086,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_SMS:
if (!hasSN) {
hasSN=disCont[i].dispatch->chipClock;
CHIP_VOL(0,2.0);
CHIP_VOL(0,4.0);
willExport[i]=true;
switch (song.systemFlags[i].getInt("chipType",0)) {
case 1: // real SN
@ -1105,7 +1105,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
} else if (!(hasSN&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
CHIP_VOL_SECOND(0,2.0);
CHIP_VOL_SECOND(0,4.0);
hasSN|=0x40000000;
howManyChips++;
}
@ -1170,12 +1170,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_X1_010:
if (!hasX1) {
hasX1=disCont[i].dispatch->chipClock;
CHIP_VOL(38,0.5);
CHIP_VOL(38,2.0);
willExport[i]=true;
writeX1010[0]=disCont[i].dispatch;
} else if (!(hasX1&0x40000000)) {
isSecond[i]=true;
CHIP_VOL_SECOND(38,0.5);
CHIP_VOL_SECOND(38,2.0);
willExport[i]=true;
writeX1010[1]=disCont[i].dispatch;
hasX1|=0x40000000;
@ -1272,12 +1272,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
if (!hasOPN2) {
hasOPN2=disCont[i].dispatch->chipClock;
CHIP_VOL(2,0.8);
CHIP_VOL(2,1.6);
willExport[i]=true;
writeDACSamples=true;
} else if (!(hasOPN2&0x40000000)) {
isSecond[i]=true;
CHIP_VOL_SECOND(2,0.8);
CHIP_VOL_SECOND(2,1.6);
willExport[i]=true;
hasOPN2|=0x40000000;
howManyChips++;
@ -1336,11 +1336,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_VRC7:
if (!hasOPLL) {
hasOPLL=disCont[i].dispatch->chipClock;
CHIP_VOL(1,1.6);
CHIP_VOL(1,3.2);
willExport[i]=true;
} else if (!(hasOPLL&0x40000000)) {
isSecond[i]=true;
CHIP_VOL_SECOND(1,1.6);
CHIP_VOL_SECOND(1,3.2);
willExport[i]=true;
hasOPLL|=0x40000000;
howManyChips++;

View File

@ -69,6 +69,7 @@ const char* aboutLine[]={
"brickblock369",
"Burnt Fishy",
"CaptainMalware",
"Clingojam",
"DeMOSic",
"DevEd",
"Dippy",

View File

@ -214,7 +214,7 @@ void FurnaceGUI::drawCompatFlags() {
e->song.delayBehavior=2;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("no checks (like FamiTracker)");
ImGui::SetTooltip("no checks");
}
ImGui::Text("Simultaneous jump (0B+0D) treatment:");
@ -293,6 +293,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6pre2");
}
ImGui::Checkbox("Broken portamento during legato",&e->song.brokenPortaLegato);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6pre4");
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
ImGui::End();

View File

@ -163,7 +163,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_TX81Z_REQUEST: {
TAMidiMessage msg;
msg.type=TA_MIDI_SYSEX;
msg.sysExData.reset(new unsigned char[15]);
msg.sysExData.reset(new unsigned char[15],std::default_delete<unsigned char[]>());
msg.sysExLen=15;
memcpy(msg.sysExData.get(),avRequest,15);
if (!e->sendMidiMessage(msg)) {

View File

@ -506,7 +506,9 @@ void FurnaceGUI::drawMobileControls() {
ImGui::SameLine();
ImGui::Button("Legacy .dmf");
ImGui::SameLine();
ImGui::Button("Export Audio");
if (ImGui::Button("Export Audio")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
}
ImGui::SameLine();
if (ImGui::Button("Export VGM")) {
openFileDialog(GUI_FILE_EXPORT_VGM);

View File

@ -1000,6 +1000,9 @@ float FurnaceGUI::calcBPM(int s1, int s2, float hz, int vN, int vD) {
}
void FurnaceGUI::play(int row) {
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
e->walkSong(loopOrder,loopRow,loopEnd);
memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS);
if (!followPattern) e->setOrder(curOrder);
@ -1026,6 +1029,9 @@ void FurnaceGUI::stop() {
curNibble=false;
orderNibble=false;
activeNotes.clear();
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
}
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
@ -3132,6 +3138,14 @@ bool FurnaceGUI::loop() {
break;
case SDL_DISPLAYEVENT: {
switch (ev.display.event) {
case SDL_DISPLAYEVENT_CONNECTED:
logD("display %d connected!",ev.display.display);
updateWindow=true;
break;
case SDL_DISPLAYEVENT_DISCONNECTED:
logD("display %d disconnected!",ev.display.display);
updateWindow=true;
break;
case SDL_DISPLAYEVENT_ORIENTATION:
logD("display oriented to %d",ev.display.data1);
updateWindow=true;

View File

@ -1295,6 +1295,7 @@ class FurnaceGUI {
double exportFadeOut;
int macroLayout;
float doubleClickTime;
int oneDigitEffects;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
@ -1430,6 +1431,7 @@ class FurnaceGUI {
exportFadeOut(0.0),
macroLayout(0),
doubleClickTime(0.3f),
oneDigitEffects(0),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),

View File

@ -3796,6 +3796,9 @@ void FurnaceGUI::drawInsEdit() {
}
}
if (ins->type==DIV_INS_FM) {
macroList.push_back(FurnaceGUIMacroDesc("LFO Speed",&ins->std.ex3Macro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER]));
}
if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
macroList.push_back(FurnaceGUIMacroDesc("AM Depth",&ins->std.ex1Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("PM Depth",&ins->std.ex2Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER]));

View File

@ -287,10 +287,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (pat->data[i][index]>0xff) {
snprintf(id,63,"??##PE%d_%d_%d",k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else {
} else if (pat->data[i][index]>0x10 || settings.oneDigitEffects==0) {
const unsigned char data=pat->data[i][index];
snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
} else {
const unsigned char data=pat->data[i][index];
snprintf(id,63," %.1X##PE%d_%d_%d",data,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
}
ImGui::SameLine(0.0f,0.0f);

View File

@ -1602,6 +1602,11 @@ void FurnaceGUI::drawSettings() {
settings.germanNotation=germanNotationB;
}
bool oneDigitEffectsB=settings.oneDigitEffects;
if (ImGui::Checkbox("Single-digit effects for 00-0F",&oneDigitEffectsB)) {
settings.oneDigitEffects=oneDigitEffectsB;
}
bool centerPatternB=settings.centerPattern;
if (ImGui::Checkbox("Center pattern view",&centerPatternB)) {
settings.centerPattern=centerPatternB;
@ -2565,6 +2570,7 @@ void FurnaceGUI::syncSettings() {
settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
settings.macroLayout=e->getConfInt("macroLayout",0);
settings.doubleClickTime=e->getConfFloat("doubleClickTime",0.3f);
settings.oneDigitEffects=e->getConfInt("oneDigitEffects",0);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
@ -2676,6 +2682,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.persistFadeOut,0,1);
clampSetting(settings.macroLayout,0,4);
clampSetting(settings.doubleClickTime,0.02,1.0);
clampSetting(settings.oneDigitEffects,0,1);
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
@ -2880,6 +2887,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("exportFadeOut",settings.exportFadeOut);
e->setConf("macroLayout",settings.macroLayout);
e->setConf("doubleClickTime",settings.doubleClickTime);
e->setConf("oneDigitEffects",settings.oneDigitEffects);
// colors
for (int i=0; i<GUI_COLOR_MAX; i++) {