diff --git a/CMakeLists.txt b/CMakeLists.txt index 03ad112ef..ca10cf91b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,7 @@ src/engine/platform/sound/ymz280b.cpp src/engine/platform/sound/rf5c68.cpp +src/engine/platform/sound/oki/msm5232.cpp src/engine/platform/sound/oki/okim6258.cpp src/engine/platform/sound/snes/SPC_DSP.cpp @@ -493,6 +494,7 @@ src/engine/platform/fds.cpp src/engine/platform/tia.cpp src/engine/platform/saa.cpp src/engine/platform/amiga.cpp +src/engine/platform/msm5232.cpp src/engine/platform/msm6258.cpp src/engine/platform/msm6295.cpp src/engine/platform/pcspkr.cpp diff --git a/papers/format.md b/papers/format.md index dd58170e2..03ef41f23 100644 --- a/papers/format.md +++ b/papers/format.md @@ -452,8 +452,8 @@ size | description 4 | size of this block 2 | format version (see header) 1 | instrument type - | - 0: standard - | - 1: FM (OPM/OPN) + | - 0: SN76489/standard + | - 1: FM (OPN) | - 2: Game Boy | - 3: C64 | - 4: Amiga/sample @@ -485,6 +485,17 @@ size | description | - 30: Sound Unit | - 31: Namco WSG | - 32: OPL (drums) + | - 33: FM (OPM) + | - 34: NES + | - 35: MSM6258 + | - 36: MSM6295 + | - 37: ADPCM-A + | - 38: ADPCM-B + | - 39: SegaPCM + | - 40: QSound + | - 41: YMZ280B + | - 42: RF5C68 + | - 43: MSM5232 1 | reserved STR | instrument name --- | **FM instrument data** diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 7a246da8a..28b4114aa 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -21,6 +21,7 @@ #include "engine.h" #include "platform/genesis.h" #include "platform/genesisext.h" +#include "platform/msm5232.h" #include "platform/msm6258.h" #include "platform/msm6295.h" #include "platform/namcowsg.h" @@ -386,6 +387,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_MSM6295: dispatch=new DivPlatformMSM6295; break; + case DIV_SYSTEM_MSM5232: + dispatch=new DivPlatformMSM5232; + break; case DIV_SYSTEM_NAMCO: dispatch=new DivPlatformNamcoWSG; // Pac-Man (TODO: support Pole Position?) diff --git a/src/engine/instrument.h b/src/engine/instrument.h index a8283430e..6be0efd23 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -70,6 +70,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_QSOUND=40, DIV_INS_YMZ280B=41, DIV_INS_RF5C68=42, + DIV_INS_MSM5232=43, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp new file mode 100644 index 000000000..2bc3d8b6b --- /dev/null +++ b/src/engine/platform/msm5232.cpp @@ -0,0 +1,346 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "msm5232.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } + +#define NOTE_LINEAR(x) ((x)<<7) + +const char* regCheatSheetMSM5232[]={ + "Select", "0", + "MasterVol", "1", + "FreqL", "2", + "FreqH", "3", + "DataCtl", "4", + "ChanVol", "5", + "WaveCtl", "6", + "NoiseCtl", "7", + "LFOFreq", "8", + "LFOCtl", "9", + NULL +}; + +const char** DivPlatformMSM5232::getRegisterSheet() { + return regCheatSheetMSM5232; +} + +void DivPlatformMSM5232::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hwrite(w.addr,w.val); + regPool[w.addr&0x0f]=w.val; + writes.pop(); + } + memset(temp,0,16*sizeof(short)); + + /*for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1,-32768,32767); + }*/ + + msm->sound_stream_update(temp); + + //printf("tempL: %d tempR: %d\n",tempL,tempR); + bufL[h]=0; + for (int i=0; i<8; i++) { + bufL[h]+=temp[i]; + } + } +} + +const int attackMap[8]={ + 0, 1, 2, 3, 4, 5, 5, 5 +}; + +const int decayMap[16]={ + 0, 1, 2, 3, 8, 9, 4, 10, 5, 11, 12, 13, 13, 13, 13, 13 +}; + +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_LOG(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); + // TODO: volume write? + } + 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)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.duty.had) { + rWrite(12+(i>>2),chan[i].std.duty.val); + } + if (chan[i].std.ex1.had) { // attack + rWrite(8+(i>>2),attackMap[chan[i].std.ex1.val&7]); + } + if (chan[i].std.ex2.had) { // decay + rWrite(10+(i>>2),decayMap[chan[i].std.ex2.val&15]); + } + 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); + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>0x2aff) chan[i].freq=0x2aff; + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].active) { + rWrite(i,0x80|(chan[i].freq>>7)); + } + if (chan[i].keyOff) { + rWrite(i,0); + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformMSM5232::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_LINEAR(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + 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; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_LINEAR(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*parent->song.pitchSlideSpeed; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*parent->song.pitchSlideSpeed; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].noise=c.value; + 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))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 31; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformMSM5232::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + // TODO: this +} + +void DivPlatformMSM5232::forceIns() { + for (int i=0; i<8; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + } +} + +void* DivPlatformMSM5232::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformMSM5232::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformMSM5232::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformMSM5232::getRegisterPool() { + return regPool; +} + +int DivPlatformMSM5232::getRegisterPoolSize() { + return 14; +} + +void DivPlatformMSM5232::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,128); + for (int i=0; i<8; i++) { + chan[i]=DivPlatformMSM5232::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + msm->device_start(); + msm->device_reset(); + memset(temp,0,16*sizeof(short)); + 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<8; i++) { + rWrite(i,0); + } +} + +bool DivPlatformMSM5232::isStereo() { + return false; +} + +bool DivPlatformMSM5232::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformMSM5232::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformMSM5232::setFlags(const DivConfig& flags) { + chipClock=2119040; + msm->set_clock(chipClock); + rate=msm->get_rate(); + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate; + } +} + +void DivPlatformMSM5232::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformMSM5232::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformMSM5232::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<8; i++) { + isMuted[i]=false; + 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(); + return 8; +} + +void DivPlatformMSM5232::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } + if (msm!=NULL) { + msm->device_stop(); + delete msm; + msm=NULL; + } +} + +DivPlatformMSM5232::~DivPlatformMSM5232() { +} diff --git a/src/engine/platform/msm5232.h b/src/engine/platform/msm5232.h new file mode 100644 index 000000000..189649834 --- /dev/null +++ b/src/engine/platform/msm5232.h @@ -0,0 +1,96 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MSM5232_H +#define _MSM5232_H + +#include "../dispatch.h" +#include +#include "../macroInt.h" +#include "sound/oki/msm5232.h" + +class DivPlatformMSM5232: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, pitch2, note; + int ins; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise; + signed char vol, outVol; + DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } + Channel(): + freq(0), + baseFreq(0), + pitch(0), + pitch2(0), + note(0), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + noise(false), + vol(31), + outVol(31) {} + }; + Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; + bool isMuted[8]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + + int cycles, curChan, delay; + short temp[16]; + msm5232_device* msm; + unsigned char regPool[128]; + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformMSM5232(); +}; + +#endif diff --git a/src/engine/platform/sound/oki/msm5232.cpp b/src/engine/platform/sound/oki/msm5232.cpp new file mode 100644 index 000000000..c92d92043 --- /dev/null +++ b/src/engine/platform/sound/oki/msm5232.cpp @@ -0,0 +1,709 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski, Hiromitsu Shioya +// additional modifications for Furnace by tildearrow +#include "msm5232.h" +#include +#include + +#define CLOCK_RATE_DIVIDER 16 + +/* + OKI MSM5232RS + 8 channel tone generator +*/ + +msm5232_device::msm5232_device(uint32_t clock) + : m_noise_cnt(0), m_noise_step(0), m_noise_rng(0), m_noise_clocks(0), m_UpdateStep(0), m_control1(0), m_control2(0), m_gate(0), m_chip_clock(0), m_rate(0), m_clock(clock) + , m_gate_handler_cb(NULL) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void msm5232_device::device_start() +{ + int rate = m_clock/CLOCK_RATE_DIVIDER; + + init(m_clock, rate); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void msm5232_device::device_reset() +{ + for (int i=0; i<8; i++) + { + write(i, 0x80); + write(i, 0x00); + } + m_noise_cnt = 0; + m_noise_rng = 1; + m_noise_clocks = 0; + + m_control1 = 0; + m_EN_out16[0] = 0; + m_EN_out8[0] = 0; + m_EN_out4[0] = 0; + m_EN_out2[0] = 0; + + m_control2 = 0; + m_EN_out16[1] = 0; + m_EN_out8[1] = 0; + m_EN_out4[1] = 0; + m_EN_out2[1] = 0; + + gate_update(); +} + +//------------------------------------------------- +// device_stop - device-specific stop +//------------------------------------------------- + +void msm5232_device::device_stop() +{ +} + +void msm5232_device::set_capacitors(double cap1, double cap2, double cap3, double cap4, double cap5, double cap6, double cap7, double cap8) +{ + m_external_capacity[0] = cap1; + m_external_capacity[1] = cap2; + m_external_capacity[2] = cap3; + m_external_capacity[3] = cap4; + m_external_capacity[4] = cap5; + m_external_capacity[5] = cap6; + m_external_capacity[6] = cap7; + m_external_capacity[7] = cap8; +} + +/* Default chip clock is 2119040 Hz */ +/* At this clock chip generates exactly 440.0 Hz signal on 8' output when pitch data=0x21 */ + + +/* ROM table to convert from pitch data into data for programmable counter and binary counter */ +/* Chip has 88x12bits ROM (addressing (in hex) from 0x00 to 0x57) */ +#define ROM(counter,bindiv) (counter|(bindiv<<9)) + +static const uint16_t MSM5232_ROM[88]={ +/* higher values are Programmable Counter data (9 bits) */ +/* lesser values are Binary Counter shift data (3 bits) */ + +/* 0 */ ROM (506, 7), + +/* 1 */ ROM (478, 7),/* 2 */ ROM (451, 7),/* 3 */ ROM (426, 7),/* 4 */ ROM (402, 7), +/* 5 */ ROM (379, 7),/* 6 */ ROM (358, 7),/* 7 */ ROM (338, 7),/* 8 */ ROM (319, 7), +/* 9 */ ROM (301, 7),/* A */ ROM (284, 7),/* B */ ROM (268, 7),/* C */ ROM (253, 7), + +/* D */ ROM (478, 6),/* E */ ROM (451, 6),/* F */ ROM (426, 6),/*10 */ ROM (402, 6), +/*11 */ ROM (379, 6),/*12 */ ROM (358, 6),/*13 */ ROM (338, 6),/*14 */ ROM (319, 6), +/*15 */ ROM (301, 6),/*16 */ ROM (284, 6),/*17 */ ROM (268, 6),/*18 */ ROM (253, 6), + +/*19 */ ROM (478, 5),/*1A */ ROM (451, 5),/*1B */ ROM (426, 5),/*1C */ ROM (402, 5), +/*1D */ ROM (379, 5),/*1E */ ROM (358, 5),/*1F */ ROM (338, 5),/*20 */ ROM (319, 5), +/*21 */ ROM (301, 5),/*22 */ ROM (284, 5),/*23 */ ROM (268, 5),/*24 */ ROM (253, 5), + +/*25 */ ROM (478, 4),/*26 */ ROM (451, 4),/*27 */ ROM (426, 4),/*28 */ ROM (402, 4), +/*29 */ ROM (379, 4),/*2A */ ROM (358, 4),/*2B */ ROM (338, 4),/*2C */ ROM (319, 4), +/*2D */ ROM (301, 4),/*2E */ ROM (284, 4),/*2F */ ROM (268, 4),/*30 */ ROM (253, 4), + +/*31 */ ROM (478, 3),/*32 */ ROM (451, 3),/*33 */ ROM (426, 3),/*34 */ ROM (402, 3), +/*35 */ ROM (379, 3),/*36 */ ROM (358, 3),/*37 */ ROM (338, 3),/*38 */ ROM (319, 3), +/*39 */ ROM (301, 3),/*3A */ ROM (284, 3),/*3B */ ROM (268, 3),/*3C */ ROM (253, 3), + +/*3D */ ROM (478, 2),/*3E */ ROM (451, 2),/*3F */ ROM (426, 2),/*40 */ ROM (402, 2), +/*41 */ ROM (379, 2),/*42 */ ROM (358, 2),/*43 */ ROM (338, 2),/*44 */ ROM (319, 2), +/*45 */ ROM (301, 2),/*46 */ ROM (284, 2),/*47 */ ROM (268, 2),/*48 */ ROM (253, 2), + +/*49 */ ROM (478, 1),/*4A */ ROM (451, 1),/*4B */ ROM (426, 1),/*4C */ ROM (402, 1), +/*4D */ ROM (379, 1),/*4E */ ROM (358, 1),/*4F */ ROM (338, 1),/*50 */ ROM (319, 1), +/*51 */ ROM (301, 1),/*52 */ ROM (284, 1),/*53 */ ROM (268, 1),/*54 */ ROM (253, 1), + +/*55 */ ROM (253, 1),/*56 */ ROM (253, 1), + +/*57 */ ROM (13, 7) +}; +#undef ROM + + +#define STEP_SH (16) /* step calculations accuracy */ + +/* + * resistance values are guesswork, default capacity is mentioned in the datasheets + * + * charges external capacitor (default is 0.39uF) via R51 + * in approx. 5*1400 * 0.39e-6 + * + * external capacitor is discharged through R52 + * in approx. 5*28750 * 0.39e-6 + */ + + +#define R51 1400 /* charge resistance */ +#define R52 28750 /* discharge resistance */ + +#if 0 +/* + C24 = external capacity + + osd_printf_debug("Time constant T=R*C =%f sec.\n",R51*C24); + osd_printf_debug("Cap fully charged after 5T=%f sec (sample=%f). Level=%f\n",(R51*C24)*5,(R51*C24)*5*sample_rate , VMAX*0.99326 ); + osd_printf_debug("Cap charged after 5T=%f sec (sample=%f). Level=%20.16f\n",(R51*C24)*5,(R51*C24)*5*sample_rate , + VMAX*(1.0-pow(2.718,-0.0748/(R51*C24))) ); +*/ +#endif + + + + +void msm5232_device::init_tables() +{ + int i; + double scale; + + /* sample rate = chip clock !!! But : */ + /* highest possible frequency is chipclock/13/16 (pitch data=0x57) */ + /* at 2MHz : 2000000/13/16 = 9615 Hz */ + + i = ((double)(1< 0x0d) + return; + + if (offset < 0x08) /* pitch */ + { + int ch = offset&7; + + m_voi[ch].GF = ((data&0x80)>>7); + if (ch == 7) + gate_update(); + + if(data&0x80) + { + if(data >= 0xd8) + { + /*if ((data&0x7f) != 0x5f) logerror("MSM5232: WRONG PITCH CODE = %2x\n",data&0x7f);*/ + m_voi[ch].mode = 1; /* noise mode */ + m_voi[ch].eg_sect = 0; /* Key On */ + } + else + { + if ( m_voi[ch].pitch != (data&0x7f) ) + { + int n; + uint16_t pg; + + m_voi[ch].pitch = data&0x7f; + + pg = MSM5232_ROM[ data&0x7f ]; + + m_voi[ch].TG_count_period = (pg & 0x1ff) * m_UpdateStep / 2; + + n = (pg>>9) & 7; /* n = bit number for 16' output */ + m_voi[ch].TG_out16 = 1<0)? n-1: 0; + m_voi[ch].TG_out8 = 1<0)? n-1: 0; + m_voi[ch].TG_out4 = 1<0)? n-1: 0; + m_voi[ch].TG_out2 = 1< go to release */ + else /* arm = 1 */ + m_voi[ch].eg_sect = 1; /* Key Off -> go to decay */ + } + } + else + { + int i; + switch(offset) + { + case 0x08: /* group1 attack */ + for (i=0; i<4; i++) + m_voi[i].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i]; + break; + + case 0x09: /* group2 attack */ + for (i=0; i<4; i++) + m_voi[i+4].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i+4]; + break; + + case 0x0a: /* group1 decay */ + for (i=0; i<4; i++) { + m_voi[i].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i]; + } + break; + + case 0x0b: /* group2 decay */ + for (i=0; i<4; i++) + m_voi[i+4].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i+4]; + break; + + case 0x0c: /* group1 control */ + + /*if (m_control1 != data) + logerror("msm5232: control1 ctrl=%x OE=%x\n", data&0xf0, data&0x0f);*/ + + /*if (data & 0x10) + popmessage("msm5232: control1 ctrl=%2x\n", data);*/ + + m_control1 = data; + + for (i=0; i<4; i++) + { + if ( (data&0x10) && (m_voi[i].eg_sect == 1) ) + m_voi[i].eg_sect = 0; + m_voi[i].eg_arm = data&0x10; + } + + m_EN_out16[0] = (data&1) ? ~0:0; + m_EN_out8[0] = (data&2) ? ~0:0; + m_EN_out4[0] = (data&4) ? ~0:0; + m_EN_out2[0] = (data&8) ? ~0:0; + + break; + + case 0x0d: /* group2 control */ + + /*if (m_control2 != data) + logerror("msm5232: control2 ctrl=%x OE=%x\n", data&0xf0, data&0x0f);*/ + + /*if (data & 0x10) + popmessage("msm5232: control2 ctrl=%2x\n", data);*/ + + m_control2 = data; + gate_update(); + + for (i=0; i<4; i++) + { + 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_EN_out16[1] = (data&1) ? ~0:0; + m_EN_out8[1] = (data&2) ? ~0:0; + m_EN_out4[1] = (data&4) ? ~0:0; + m_EN_out2[1] = (data&8) ? ~0:0; + + break; + } + } +} + + + +#define VMIN 0 +#define VMAX 32768 + + +void msm5232_device::EG_voices_advance() +{ + VOICE *voi = &m_voi[0]; + int samplerate = m_rate; + int i; + + i = 8; + do + { + 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; + } + } + + /* 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*/ + + break; + + 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; + } + + voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ + + break; + + 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; + } + + voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/ + + break; + + default: + break; + } + + voi++; + i--; + } while (i>0); + +} + +static int o2,o4,o8,o16,solo8,solo16; + +void msm5232_device::TG_group_advance(int groupidx) +{ + VOICE *voi = &m_voi[groupidx*4]; + int i; + + o2 = o4 = o8 = o16 = solo8 = solo16 = 0; + + i=4; + do + { + int out2, out4, out8, out16; + + out2 = out4 = out8 = out16 = 0; + + if (voi->mode==0) /* generate square tone */ + { + int left = 1<TG_cnt&voi->TG_out16) out16+=voi->TG_count; + if (voi->TG_cnt&voi->TG_out8) out8 +=voi->TG_count; + if (voi->TG_cnt&voi->TG_out4) out4 +=voi->TG_count; + if (voi->TG_cnt&voi->TG_out2) out2 +=voi->TG_count; + + voi->TG_count -= nextevent; + + while (voi->TG_count <= 0) + { + voi->TG_count += voi->TG_count_period; + voi->TG_cnt++; + if (voi->TG_cnt&voi->TG_out16) out16+=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out8 ) out8 +=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out4 ) out4 +=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out2 ) out2 +=voi->TG_count_period; + + if (voi->TG_count > 0) + break; + + voi->TG_count += voi->TG_count_period; + voi->TG_cnt++; + if (voi->TG_cnt&voi->TG_out16) out16+=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out8 ) out8 +=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out4 ) out4 +=voi->TG_count_period; + if (voi->TG_cnt&voi->TG_out2 ) out2 +=voi->TG_count_period; + } + if (voi->TG_cnt&voi->TG_out16) out16-=voi->TG_count; + if (voi->TG_cnt&voi->TG_out8 ) out8 -=voi->TG_count; + if (voi->TG_cnt&voi->TG_out4 ) out4 -=voi->TG_count; + if (voi->TG_cnt&voi->TG_out2 ) out2 -=voi->TG_count; + + left -=nextevent; + + }while (left>0); + } + else /* generate noise */ + { + if (m_noise_clocks&8) out16+=(1<egvol) >> STEP_SH; + o8 += ( (out8 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH; + o4 += ( (out4 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH; + o2 += ( (out2 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH; + + if (i == 1 && groupidx == 1) + { + solo16 += ( (out16-(1<<(STEP_SH-1))) << 11) >> STEP_SH; + solo8 += ( (out8 -(1<<(STEP_SH-1))) << 11) >> STEP_SH; + } + + voi++; + i--; + }while (i>0); + + /* cut off disabled output lines */ + o16 &= m_EN_out16[groupidx]; + o8 &= m_EN_out8 [groupidx]; + o4 &= m_EN_out4 [groupidx]; + o2 &= m_EN_out2 [groupidx]; +} + + +/* macro saves feet data to mono file */ +#ifdef SAVE_SEPARATE_CHANNELS + #define SAVE_SINGLE_CHANNEL(j,val) \ + { signed int pom= val; \ + if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ + fputc((unsigned short)pom&0xff,sample[j]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[j]); } +#else + #define SAVE_SINGLE_CHANNEL(j,val) +#endif + +/* first macro saves all 8 feet outputs to mixed (mono) file */ +/* second macro saves one group into left and the other in right channel */ +#if 1 /*MONO*/ + #ifdef SAVE_SAMPLE + #define SAVE_ALL_CHANNELS \ + { signed int pom = buf1[i] + buf2[i]; \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + } + #else + #define SAVE_ALL_CHANNELS + #endif +#else /*STEREO*/ + #ifdef SAVE_SAMPLE + #define SAVE_ALL_CHANNELS \ + { signed int pom = buf1[i]; \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + pom = buf2[i]; \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + } + #else + #define SAVE_ALL_CHANNELS + #endif +#endif + + +/* MAME Interface */ +void msm5232_device::device_post_load() +{ + init_tables(); +} + +void msm5232_device::set_clock(int clock) +{ + if (m_chip_clock != clock) + { + m_chip_clock = clock; + m_rate = clock/CLOCK_RATE_DIVIDER; + init_tables(); + } +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void msm5232_device::sound_stream_update(short* outputs) +{ + auto &buf1 = outputs[0]; + auto &buf2 = outputs[1]; + auto &buf3 = outputs[2]; + auto &buf4 = outputs[3]; + auto &buf5 = outputs[4]; + auto &buf6 = outputs[5]; + auto &buf7 = outputs[6]; + auto &buf8 = outputs[7]; + auto &bufsolo1 = outputs[8]; + auto &bufsolo2 = outputs[9]; + auto &bufnoise = outputs[10]; + + /* calculate all voices' envelopes */ + EG_voices_advance(); + + TG_group_advance(0); /* calculate tones group 1 */ + buf1=o2; + buf2=o4; + buf3=o8; + buf4=o16; + + TG_group_advance(1); /* calculate tones group 2 */ + buf5=o2; + buf6=o4; + buf7=o8; + buf8=o16; + + bufsolo1=solo8; + bufsolo2=solo16; + + /* update noise generator */ + { + int cnt = (m_noise_cnt+=m_noise_step) >> STEP_SH; + m_noise_cnt &= ((1< 0) + { + int tmp = m_noise_rng & (1<<16); /* store current level */ + + if (m_noise_rng&1) + m_noise_rng ^= 0x24000; + m_noise_rng>>=1; + + if ( (m_noise_rng & (1<<16)) != tmp ) /* level change detect */ + m_noise_clocks++; + + cnt--; + } + } + + bufnoise=(m_noise_rng & (1<<16)) ? 32767 : 0; +} diff --git a/src/engine/platform/sound/oki/msm5232.h b/src/engine/platform/sound/oki/msm5232.h new file mode 100644 index 000000000..bddc97c65 --- /dev/null +++ b/src/engine/platform/sound/oki/msm5232.h @@ -0,0 +1,101 @@ +// license:GPL-2.0+ +// copyright-holders:Jarek Burczynski, Hiromitsu Shioya +// additional modifications for Furnace by tildearrow +#ifndef MAME_SOUND_MSM5232_H +#define MAME_SOUND_MSM5232_H + +#pragma once + +#include +#include + +class msm5232_device +{ +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); + //auto gate() { return m_gate_handler_cb.bind(); } + + void write(unsigned int offset, uint8_t data); + void set_clock(int clock); + + // device-level overrides + void device_start(); + void device_stop(); + void device_reset(); + void device_post_load(); + + // sound stream update overrides + void sound_stream_update(short* outputs); + + int get_rate(); + +private: + struct VOICE { + uint8_t mode; + + int TG_count_period; + int TG_count; + + uint8_t TG_cnt; /* 7 bits binary counter (frequency output) */ + uint8_t TG_out16; /* bit number (of TG_cnt) for 16' output */ + uint8_t TG_out8; /* bit number (of TG_cnt) for 8' output */ + uint8_t TG_out4; /* bit number (of TG_cnt) for 4' output */ + uint8_t TG_out2; /* bit number (of TG_cnt) for 2' output */ + + int egvol; + int eg_sect; + int counter; + int eg; + + uint8_t eg_arm; /* attack/release mode */ + + double ar_rate; + double dr_rate; + double rr_rate; + + int pitch; /* current pitch data */ + + int GF; + }; + + VOICE m_voi[8]; + + uint32_t m_EN_out16[2]; /* enable 16' output masks for both groups (0-disabled ; ~0 -enabled) */ + uint32_t m_EN_out8[2]; /* enable 8' output masks */ + uint32_t m_EN_out4[2]; /* enable 4' output masks */ + uint32_t m_EN_out2[2]; /* enable 2' output masks */ + + int m_noise_cnt; + int m_noise_step; + int m_noise_rng; + int m_noise_clocks; /* number of the noise_rng (output) level changes */ + + unsigned int m_UpdateStep; + + /* rate tables */ + double m_ar_tbl[8]; + double m_dr_tbl[16]; + + uint8_t m_control1; + uint8_t m_control2; + + int m_gate; /* current state of the GATE output */ + + int m_chip_clock; /* chip clock in Hz */ + int m_rate; /* sample rate in Hz */ + uint32_t m_clock; + + double m_external_capacity[8]; /* in Farads, eg 0.39e-6 = 0.36 uF (microFarads) */ + std::function m_gate_handler_cb;/* callback called when the GATE output pin changes state */ + + void init_tables(); + void init_voice(int i); + void gate_update(); + void init(int clock, int rate); + void EG_voices_advance(); + void TG_group_advance(int groupidx); +}; + +#endif // MAME_SOUND_MSM5232_H diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 29b2f3d61..faf757579 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1610,7 +1610,7 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, - {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD} + {DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232, DIV_INS_MSM5232} ); sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef( diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 2a1c7a66a..defb68d0b 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -378,6 +378,10 @@ void FurnaceGUI::drawInsList(bool asChild) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_RF5C68]); name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); break; + case DIV_INS_MSM5232: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM5232]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); diff --git a/src/gui/gui.h b/src/gui/gui.h index 3337d7dd0..fa00d76cf 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -170,6 +170,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_QSOUND, GUI_COLOR_INSTR_YMZ280B, GUI_COLOR_INSTR_RF5C68, + GUI_COLOR_INSTR_MSM5232, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index b429a79fd..6b314963c 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -113,8 +113,8 @@ const char* insTypes[DIV_INS_MAX+1]={ "Sound Unit", "Namco WSG", "OPL (drums)", - "FM (OPM)", // 33 - "NES", // 34 + "FM (OPM)", + "NES", "MSM6258", "MSM6295", "ADPCM-A", @@ -123,6 +123,7 @@ const char* insTypes[DIV_INS_MAX+1]={ "QSound", "YMZ280B", "RF5C68", + "MSM5232", NULL }; @@ -799,6 +800,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_QSOUND,"",ImVec4(1.0f,0.8f,0.3f,1.0f)), D(GUI_COLOR_INSTR_YMZ280B,"",ImVec4(0.4f,0.5f,1.0f,1.0f)), D(GUI_COLOR_INSTR_RF5C68,"",ImVec4(1.0f,0.3f,0.3f,1.0f)), + D(GUI_COLOR_INSTR_MSM5232,"",ImVec4(0.5f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -952,6 +954,7 @@ const int availableSystems[]={ DIV_SYSTEM_MSM6295, DIV_SYSTEM_RF5C68, DIV_SYSTEM_SNES, + DIV_SYSTEM_MSM5232, DIV_SYSTEM_PCM_DAC, 0 // don't remove this last one! }; @@ -996,6 +999,7 @@ const int chipsSquare[]={ DIV_SYSTEM_PCSPKR, DIV_SYSTEM_SAA1099, DIV_SYSTEM_VIC20, + DIV_SYSTEM_MSM5232, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 8cf784bb7..dd16fa1a9 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -243,6 +243,10 @@ const char* mikeyFeedbackBits[11] = { "0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL }; +const char* msm5232ControlBits[6]={ + "2'", "4'", "8'", "16'", "sustain", NULL +}; + const char* x1_010EnvBits[8]={ "enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL }; @@ -4326,7 +4330,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { volMax=65535; } - if (ins->type==DIV_INS_MSM6258) { + if (ins->type==DIV_INS_MSM6258 || ins->type==DIV_INS_MSM5232) { volMax=0; } if (ins->type==DIV_INS_MSM6295) { @@ -4370,6 +4374,10 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Duty/Int"; dutyMax=ins->amiga.useSample?0:10; } + if (ins->type==DIV_INS_MSM5232) { + dutyLabel="Group Ctrl"; + dutyMax=5; + } if (ins->type==DIV_INS_BEEPER) { dutyLabel="Pulse Width"; dutyMax=255; @@ -4454,6 +4462,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ADPCMB) waveMax=0; if (ins->type==DIV_INS_QSOUND) waveMax=0; if (ins->type==DIV_INS_YMZ280B) waveMax=0; + if (ins->type==DIV_INS_MSM5232) waveMax=0; if (ins->type==DIV_INS_MSM6258) waveMax=0; if (ins->type==DIV_INS_MSM6295) waveMax=0; if (ins->type==DIV_INS_SEGAPCM) waveMax=0; @@ -4521,6 +4530,10 @@ void FurnaceGUI::drawInsEdit() { ex1Max=5; ex2Max=5; } + if (ins->type==DIV_INS_MSM5232) { + ex1Max=5; + ex2Max=11; + } int panMin=0; int panMax=0; @@ -4585,6 +4598,8 @@ void FurnaceGUI::drawInsEdit() { if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits)); + } else if (ins->type==DIV_INS_MSM5232) { + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits)); } else if (ins->type==DIV_INS_ES5506) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,¯oHoverES5506FilterMode)); } else { @@ -4615,7 +4630,9 @@ void FurnaceGUI::drawInsEdit() { } } } - macroList.push_back(FurnaceGUIMacroDesc("Pitch",&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); + if (ins->type!=DIV_INS_MSM5232) { + macroList.push_back(FurnaceGUIMacroDesc("Pitch",&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); + } if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM || ins->type==DIV_INS_STD || @@ -4667,6 +4684,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Echo Feedback",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SNES) { macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,snesModeBits)); + } else if (ins->type==DIV_INS_MSM5232) { + macroList.push_back(FurnaceGUIMacroDesc("Group Attack",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER])); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } @@ -4686,6 +4705,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Echo Length",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SNES) { macroList.push_back(FurnaceGUIMacroDesc("Gain Mode",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,snesGainModes)); + } else if (ins->type==DIV_INS_MSM5232) { + macroList.push_back(FurnaceGUIMacroDesc("Group Decay",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else { macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); }