From 7b55ba4e77b215bdc0bb22f0efaf93ea83eaafc0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 1 Oct 2022 19:47:40 -0500 Subject: [PATCH] MSM5232: more options --- src/engine/platform/msm5232.cpp | 103 ++++++++++++-- src/engine/platform/msm5232.h | 14 +- src/engine/platform/sound/oki/msm5232.cpp | 161 ++++++++++++---------- src/engine/platform/sound/oki/msm5232.h | 3 + src/gui/insEdit.cpp | 6 +- src/gui/sysConf.cpp | 94 +++++++++++++ 6 files changed, 290 insertions(+), 91 deletions(-) diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp index 9cfabc8c7..f311b1136 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -63,7 +63,7 @@ void DivPlatformMSM5232::acquire(short* bufL, short* bufR, size_t start, size_t //printf("tempL: %d tempR: %d\n",tempL,tempR); bufL[h]=0; for (int i=0; i<8; i++) { - bufL[h]+=temp[i]; + bufL[h]+=(temp[i]*partVolume[i])>>8; } } } @@ -79,6 +79,9 @@ const int decayMap[16]={ void DivPlatformMSM5232::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&127,MIN(127,chan[i].std.vol.val),127); + } if (chan[i].std.arp.had) { if (!chan[i].inPorta) { chan[i].baseFreq=NOTE_LINEAR(parent->calcArp(chan[i].note,chan[i].std.arp.val)); @@ -86,18 +89,39 @@ void DivPlatformMSM5232::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.duty.had) { - rWrite(12+(i>>2),chan[i].std.duty.val); + groupControl[i>>2]=(chan[i].std.duty.val&0x1f)|(groupEnv[i>>2]?0x20:0); + updateGroup[i>>2]=true; } if (chan[i].std.ex1.had) { // attack - rWrite(8+(i>>2),attackMap[chan[i].std.ex1.val&7]); + groupAR[i>>2]=attackMap[chan[i].std.ex1.val&7]; + updateGroupAR[i>>2]=true; } if (chan[i].std.ex2.had) { // decay - rWrite(10+(i>>2),decayMap[chan[i].std.ex2.val&15]); + groupDR[i>>2]=decayMap[chan[i].std.ex2.val&15]; + updateGroupDR[i>>2]=true; } if (chan[i].std.ex3.had) { // noise chan[i].noise=chan[i].std.ex3.val; chan[i].freqChanged=true; } + } + + for (int i=0; i<2; i++) { + if (updateGroup[i]) { + rWrite(12+i,groupControl[i]); + updateGroup[i]=false; + } + if (updateGroupAR[i]) { + rWrite(8+i,groupAR[i]); + updateGroupAR[i]=false; + } + if (updateGroupDR[i]) { + rWrite(10+i,groupDR[i]); + updateGroupDR[i]=false; + } + } + + for (int i=0; i<8; i++) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); chan[i].freq=chan[i].baseFreq+chan[i].pitch+chan[i].pitch2-(12<<7); @@ -118,6 +142,17 @@ void DivPlatformMSM5232::tick(bool sysTick) { chan[i].freqChanged=false; } } + + msm->set_vol_input( + chan[0].active?((double)chan[0].outVol/127.0):0.0, + chan[1].active?((double)chan[1].outVol/127.0):0.0, + chan[2].active?((double)chan[2].outVol/127.0):0.0, + chan[3].active?((double)chan[3].outVol/127.0):0.0, + chan[4].active?((double)chan[4].outVol/127.0):0.0, + chan[5].active?((double)chan[5].outVol/127.0):0.0, + chan[6].active?((double)chan[6].outVol/127.0):0.0, + chan[7].active?((double)chan[7].outVol/127.0):0.0 + ); } int DivPlatformMSM5232::dispatch(DivCommand c) { @@ -158,9 +193,6 @@ int DivPlatformMSM5232::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; - if (chan[c.chan].active) { - //chWrite(c.chan,0x04,0x80|chan[c.chan].outVol); - } } } break; @@ -199,6 +231,7 @@ int DivPlatformMSM5232::dispatch(DivCommand c) { } case DIV_CMD_STD_NOISE_MODE: chan[c.chan].noise=c.value; + chan[c.chan].freqChanged=true; break; case DIV_CMD_LEGATO: chan[c.chan].baseFreq=NOTE_LINEAR(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); @@ -213,7 +246,7 @@ int DivPlatformMSM5232::dispatch(DivCommand c) { chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: - return 1; + return 127; break; case DIV_ALWAYS_SET_VOLUME: return 1; @@ -234,6 +267,11 @@ void DivPlatformMSM5232::forceIns() { chan[i].insChanged=true; chan[i].freqChanged=true; } + for (int i=0; i<2; i++) { + updateGroup[i]=true; + updateGroupAR[i]=true; + updateGroupDR[i]=true; + } } void* DivPlatformMSM5232::getChanState(int ch) { @@ -272,15 +310,20 @@ void DivPlatformMSM5232::reset() { cycles=0; curChan=-1; delay=500; - rWrite(8,0); - rWrite(9,0); - rWrite(10,5); - rWrite(11,5); - rWrite(12,0x2f); - rWrite(13,0x2f); + + for (int i=0; i<2; i++) { + groupControl[i]=15|(groupEnv[i]?0x20:0); + groupAR[i]=0; + groupDR[i]=5; + + updateGroup[i]=true; + updateGroupAR[i]=true; + updateGroupDR[i]=true; + } for (int i=0; i<8; i++) { rWrite(i,0); + partVolume[i]=initPartVolume[i]; msm->mute(i,isMuted[i]); } } @@ -307,6 +350,37 @@ void DivPlatformMSM5232::setFlags(const DivConfig& flags) { for (int i=0; i<8; i++) { oscBuf[i]->rate=rate; } + initPartVolume[0]=flags.getInt("partVolume0",255); + initPartVolume[1]=flags.getInt("partVolume1",255); + initPartVolume[2]=flags.getInt("partVolume2",255); + initPartVolume[3]=flags.getInt("partVolume3",255); + initPartVolume[4]=flags.getInt("partVolume4",255); + initPartVolume[5]=flags.getInt("partVolume5",255); + initPartVolume[6]=flags.getInt("partVolume6",255); + initPartVolume[7]=flags.getInt("partVolume7",255); + + capacitance[0]=flags.getFloat("capValue0",390.0f); + capacitance[1]=flags.getFloat("capValue1",390.0f); + capacitance[2]=flags.getFloat("capValue2",390.0f); + capacitance[3]=flags.getFloat("capValue3",390.0f); + capacitance[4]=flags.getFloat("capValue4",390.0f); + capacitance[5]=flags.getFloat("capValue5",390.0f); + capacitance[6]=flags.getFloat("capValue6",390.0f); + capacitance[7]=flags.getFloat("capValue7",390.0f); + + groupEnv[0]=flags.getBool("groupEnv0",true); + groupEnv[1]=flags.getBool("groupEnv1",true); + + msm->set_capacitors( + capacitance[0]*0.000000001, + capacitance[1]*0.000000001, + capacitance[2]*0.000000001, + capacitance[3]*0.000000001, + capacitance[4]*0.000000001, + capacitance[5]*0.000000001, + capacitance[6]*0.000000001, + capacitance[7]*0.000000001 + ); } void DivPlatformMSM5232::poke(unsigned int addr, unsigned short val) { @@ -326,7 +400,6 @@ int DivPlatformMSM5232::init(DivEngine* p, int channels, int sugRate, const DivC oscBuf[i]=new DivDispatchOscBuffer; } msm=new msm5232_device(2119040); - msm->set_capacitors(0.39e-6,0.39e-6,0.39e-6,0.39e-6,0.39e-6,0.39e-6,0.39e-6,0.39e-6); msm->device_start(); setFlags(flags); reset(); diff --git a/src/engine/platform/msm5232.h b/src/engine/platform/msm5232.h index 20e7a7e47..34412b9a3 100644 --- a/src/engine/platform/msm5232.h +++ b/src/engine/platform/msm5232.h @@ -50,12 +50,22 @@ class DivPlatformMSM5232: public DivDispatch { keyOff(false), inPorta(false), noise(false), - vol(1), - outVol(1) {} + vol(127), + outVol(127) {} }; Channel chan[8]; DivDispatchOscBuffer* oscBuf[8]; + int partVolume[8]; + int initPartVolume[8]; + double capacitance[8]; bool isMuted[8]; + bool updateGroup[2]; + bool updateGroupAR[2]; + bool updateGroupDR[2]; + bool groupEnv[2]; + unsigned char groupControl[2]; + unsigned char groupAR[2]; + unsigned char groupDR[2]; struct QueuedWrite { unsigned char addr; unsigned char val; diff --git a/src/engine/platform/sound/oki/msm5232.cpp b/src/engine/platform/sound/oki/msm5232.cpp index d1dd14544..59c001aeb 100644 --- a/src/engine/platform/sound/oki/msm5232.cpp +++ b/src/engine/platform/sound/oki/msm5232.cpp @@ -79,6 +79,18 @@ void msm5232_device::set_capacitors(double cap1, double cap2, double cap3, doubl m_external_capacity[7] = cap8; } +void msm5232_device::set_vol_input(double v1, double v2, double v3, double v4, double v5, double v6, double v7, double v8) +{ + m_external_input[0] = v1; + m_external_input[1] = v2; + m_external_input[2] = v3; + m_external_input[3] = v4; + m_external_input[4] = v5; + m_external_input[5] = v6; + m_external_input[6] = v7; + m_external_input[7] = v8; +} + /* Default chip clock is 2119040 Hz */ /* At this clock chip generates exactly 440.0 Hz signal on 8' output when pitch data=0x21 */ @@ -224,6 +236,7 @@ void msm5232_device::init_voice(int i) m_voi[i].eg_sect= -1; m_voi[i].eg = 0.0; m_voi[i].eg_arm = 0; + m_voi[i].eg_ext = 0; m_voi[i].pitch = -1.0; m_voi[i].mute = false; } @@ -366,6 +379,7 @@ void msm5232_device::write(unsigned int offset, uint8_t data) if ( (data&0x10) && (m_voi[i].eg_sect == 1) ) m_voi[i].eg_sect = 0; m_voi[i].eg_arm = data&0x10; + m_voi[i].eg_ext = !(data&0x20); } m_EN_out16[0] = (data&1) ? ~0:0; @@ -391,6 +405,7 @@ void msm5232_device::write(unsigned int offset, uint8_t data) if ( (data&0x10) && (m_voi[i+4].eg_sect == 1) ) m_voi[i+4].eg_sect = 0; m_voi[i+4].eg_arm = data&0x10; + m_voi[i+4].eg_ext = !(data&0x20); } m_EN_out16[1] = (data&1) ? ~0:0; @@ -418,90 +433,94 @@ void msm5232_device::EG_voices_advance() i = 8; do { - switch(voi->eg_sect) - { - case 0: /* attack */ + if (voi->eg_ext) { + voi->egvol=m_external_input[8-i]*2048.0; + } else { + switch(voi->eg_sect) + { + case 0: /* attack */ - /* capacitor charge */ - if (voi->eg < VMAX) - { - voi->counter -= (int)((VMAX - voi->eg) / voi->ar_rate); - if ( voi->counter <= 0 ) - { - int n = -voi->counter / samplerate + 1; - voi->counter += n * samplerate; - if ( (voi->eg += n) > VMAX ) - voi->eg = VMAX; - } - } + /* capacitor charge */ + if (voi->eg < VMAX) + { + voi->counter -= (int)((VMAX - voi->eg) / voi->ar_rate); + if ( voi->counter <= 0 ) + { + int n = -voi->counter / samplerate + 1; + voi->counter += n * samplerate; + if ( (voi->eg += n) > VMAX ) + voi->eg = VMAX; + } + } - /* when ARM=0, EG switches to decay as soon as cap is charged to VT (EG inversion voltage; about 80% of MAX) */ - if (!voi->eg_arm) - { - if(voi->eg >= VMAX * 80/100 ) - { - voi->eg_sect = 1; - } - } - else - /* ARM=1 */ - { - /* when ARM=1, EG stays at maximum until key off */ - } + /* when ARM=0, EG switches to decay as soon as cap is charged to VT (EG inversion voltage; about 80% of MAX) */ + if (!voi->eg_arm) + { + if(voi->eg >= VMAX * 80/100 ) + { + voi->eg_sect = 1; + } + } + else + /* ARM=1 */ + { + /* when ARM=1, EG stays at maximum until key off */ + } - voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ + voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ - break; + break; - case 1: /* decay */ + case 1: /* decay */ - /* capacitor discharge */ - if (voi->eg > VMIN) - { - voi->counter -= (int)((voi->eg - VMIN) / voi->dr_rate); - if ( voi->counter <= 0 ) - { - int n = -voi->counter / samplerate + 1; - voi->counter += n * samplerate; - if ( (voi->eg -= n) < VMIN ) - voi->eg = VMIN; - } - } - else /* voi->eg <= VMIN */ - { - voi->eg_sect =-1; - } + /* capacitor discharge */ + if (voi->eg > VMIN) + { + voi->counter -= (int)((voi->eg - VMIN) / voi->dr_rate); + if ( voi->counter <= 0 ) + { + int n = -voi->counter / samplerate + 1; + voi->counter += n * samplerate; + if ( (voi->eg -= n) < VMIN ) + voi->eg = VMIN; + } + } + else /* voi->eg <= VMIN */ + { + voi->eg_sect =-1; + } - voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ + voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ - break; + break; - case 2: /* release */ + case 2: /* release */ - /* capacitor discharge */ - if (voi->eg > VMIN) - { - voi->counter -= (int)((voi->eg - VMIN) / voi->rr_rate); - if ( voi->counter <= 0 ) - { - int n = -voi->counter / samplerate + 1; - voi->counter += n * samplerate; - if ( (voi->eg -= n) < VMIN ) - voi->eg = VMIN; - } - } - else /* voi->eg <= VMIN */ - { - voi->eg_sect =-1; - } + /* capacitor discharge */ + if (voi->eg > VMIN) + { + voi->counter -= (int)((voi->eg - VMIN) / voi->rr_rate); + if ( voi->counter <= 0 ) + { + int n = -voi->counter / samplerate + 1; + voi->counter += n * samplerate; + if ( (voi->eg -= n) < VMIN ) + voi->eg = VMIN; + } + } + else /* voi->eg <= VMIN */ + { + voi->eg_sect =-1; + } - voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ + voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ - break; + break; - default: - break; - } + default: + break; + } + } voi++; i--; diff --git a/src/engine/platform/sound/oki/msm5232.h b/src/engine/platform/sound/oki/msm5232.h index 66cce81c4..ecf8d1e80 100644 --- a/src/engine/platform/sound/oki/msm5232.h +++ b/src/engine/platform/sound/oki/msm5232.h @@ -15,6 +15,7 @@ public: msm5232_device(uint32_t clock); void set_capacitors(double cap1, double cap2, double cap3, double cap4, double cap5, double cap6, double cap7, double cap8); + void set_vol_input(double v1, double v2, double v3, double v4, double v5, double v6, double v7, double v8); //auto gate() { return m_gate_handler_cb.bind(); } void write(unsigned int offset, uint8_t data); @@ -52,6 +53,7 @@ private: int eg; uint8_t eg_arm; /* attack/release mode */ + uint8_t eg_ext; /* inhibit envelope generator */ double ar_rate; double dr_rate; @@ -90,6 +92,7 @@ private: uint32_t m_clock; double m_external_capacity[8]; /* in Farads, eg 0.39e-6 = 0.36 uF (microFarads) */ + double m_external_input[8]; std::function m_gate_handler_cb;/* callback called when the GATE output pin changes state */ void init_tables(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index cb2fa87ed..a67856bf3 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -243,7 +243,7 @@ const char* mikeyFeedbackBits[11] = { "0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL }; -const char* msm5232ControlBits[6]={ +const char* msm5232ControlBits[7]={ "2'", "4'", "8'", "16'", "sustain", NULL }; @@ -4311,7 +4311,7 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_FM || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || ins->type==DIV_INS_OPZ || - ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES) { + ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232) { volMax=127; } if (ins->type==DIV_INS_GB) { @@ -4330,7 +4330,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { volMax=65535; } - if (ins->type==DIV_INS_MSM6258 || ins->type==DIV_INS_MSM5232) { + if (ins->type==DIV_INS_MSM6258) { volMax=0; } if (ins->type==DIV_INS_MSM6295) { diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 960d0f9d7..ad9d4fece 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -18,6 +18,7 @@ */ #include "gui.h" +#include bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) { bool altered=false; @@ -1292,6 +1293,28 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo } case DIV_SYSTEM_MSM5232: { int detune=flags.getInt("detune",0); + bool groupEnv[2]; + int groupVol[8]; + float capValue[8]; + char temp[64]; + groupEnv[0]=flags.getBool("groupEnv0",true); + groupEnv[1]=flags.getBool("groupEnv1",true); + groupVol[0]=flags.getInt("partVolume0",255); + groupVol[1]=flags.getInt("partVolume1",255); + groupVol[2]=flags.getInt("partVolume2",255); + groupVol[3]=flags.getInt("partVolume3",255); + groupVol[4]=flags.getInt("partVolume4",255); + groupVol[5]=flags.getInt("partVolume5",255); + groupVol[6]=flags.getInt("partVolume6",255); + groupVol[7]=flags.getInt("partVolume7",255); + capValue[0]=flags.getFloat("capValue0",390.0f); + capValue[1]=flags.getFloat("capValue1",390.0f); + capValue[2]=flags.getFloat("capValue2",390.0f); + capValue[3]=flags.getFloat("capValue3",390.0f); + capValue[4]=flags.getFloat("capValue4",390.0f); + capValue[5]=flags.getFloat("capValue5",390.0f); + capValue[6]=flags.getFloat("capValue6",390.0f); + capValue[7]=flags.getFloat("capValue7",390.0f); if (CWSliderInt("Detune",&detune,-127,127)) { if (detune<-127) detune=-127; @@ -1299,8 +1322,79 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } rightClickable + ImGui::Text("Capacitor values (nF):"); + for (int i=0; i<8; i++) { + snprintf(temp,63,"%d##CAPV%d",i+1,i); + if (CWSliderFloat(temp,&capValue[i],1.0f,1000.0f)) { + if (capValue[i]<0) capValue[i]=0; + if (capValue[i]>1000) capValue[i]=1000; + altered=true; + } rightClickable + } + + ImGui::Text("Initial part volume (channel 1-4):"); + for (int i=0; i<4; i++) { + snprintf(temp,63,"%d'##GRPV%d",16>>i,i); + if (CWSliderInt(temp,&groupVol[i],0,255)) { + if (groupVol[i]<0) groupVol[i]=0; + if (groupVol[i]>255) groupVol[i]=255; + altered=true; + } rightClickable + } + + ImGui::Text("Initial part volume (channel 5-8):"); + for (int i=4; i<8; i++) { + snprintf(temp,63,"%d'##GRPV%d",16>>(i-4),i); + if (CWSliderInt(temp,&groupVol[i],0,255)) { + if (groupVol[i]<0) groupVol[i]=0; + if (groupVol[i]>255) groupVol[i]=255; + altered=true; + } rightClickable + } + + ImGui::Text("Envelope mode (channel 1-4):"); + if (ImGui::RadioButton("Capacitor (attack/decay)##EM00",groupEnv[0])) { + groupEnv[0]=true; + altered=true; + } + if (ImGui::RadioButton("External (volume macro)##EM01",!groupEnv[0])) { + groupEnv[0]=false; + altered=true; + } + + ImGui::Text("Envelope mode (channel 5-8):"); + if (ImGui::RadioButton("Capacitor (attack/decay)##EM10",groupEnv[1])) { + groupEnv[1]=true; + altered=true; + } + if (ImGui::RadioButton("External (volume macro)##EM11",!groupEnv[1])) { + groupEnv[1]=false; + altered=true; + } + if (altered) { flags.set("detune",detune); + + flags.set("capValue0",capValue[0]); + flags.set("capValue1",capValue[1]); + flags.set("capValue2",capValue[2]); + flags.set("capValue3",capValue[3]); + flags.set("capValue4",capValue[4]); + flags.set("capValue5",capValue[5]); + flags.set("capValue6",capValue[6]); + flags.set("capValue7",capValue[7]); + + flags.set("partVolume0",groupVol[0]); + flags.set("partVolume1",groupVol[1]); + flags.set("partVolume2",groupVol[2]); + flags.set("partVolume3",groupVol[3]); + flags.set("partVolume4",groupVol[4]); + flags.set("partVolume5",groupVol[5]); + flags.set("partVolume6",groupVol[6]); + flags.set("partVolume7",groupVol[7]); + + flags.set("groupEnv0",groupEnv[0]); + flags.set("groupEnv1",groupEnv[1]); } break; }