early MSM5232 work

This commit is contained in:
tildearrow 2022-10-01 03:15:40 -05:00
parent f0b3f614e1
commit 5e18edb229
13 changed files with 1307 additions and 7 deletions

View file

@ -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

View file

@ -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**

View file

@ -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?)

View file

@ -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
};

View file

@ -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 <math.h>
//#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; h<start+len; h++) {
while (!writes.empty()) {
QueuedWrite w=writes.front();
msm->write(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<DivRegWrite>& 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() {
}

View file

@ -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 <queue>
#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<QueuedWrite> 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<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformMSM5232();
};
#endif

View file

@ -0,0 +1,709 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski, Hiromitsu Shioya
// additional modifications for Furnace by tildearrow
#include "msm5232.h"
#include <stdlib.h>
#include <string.h>
#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<<STEP_SH) * (double)m_rate) / (double)m_chip_clock;
m_UpdateStep = i;
/* printf("clock=%d Hz rate=%d Hz, UpdateStep=%d\n",
m_chip_clock, m_rate, m_UpdateStep); */
scale = ((double)m_chip_clock) / (double)m_rate;
m_noise_step = ((1<<STEP_SH)/128.0) * scale; /* step of the rng reg in 16.16 format */
/* logerror("noise step=%8x\n", m_noise_step); */
#if 0
{
/* rate tables (in milliseconds) */
static const int ATBL[8] = { 2,4,8,16, 32,64, 32,64};
static const int DTBL[16]= { 40,80,160,320, 640,1280, 640,1280,
333,500,1000,2000, 4000,8000, 4000,8000};
for (i=0; i<8; i++)
{
double clockscale = (double)m_chip_clock / 2119040.0;
double time = (ATBL[i] / 1000.0) / clockscale; /* attack time in seconds */
m_ar_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
/* printf("ATBL[%d] = %20.16f time = %f s\n",i, m_ar_tbl[i], time); */
}
for (i=0; i<16; i++)
{
double clockscale = (double)m_chip_clock / 2119040.0;
double time = (DTBL[i] / 1000.0) / clockscale; /* decay time in seconds */
m_dr_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
/* printf("DTBL[%d] = %20.16f time = %f s\n",i, m_dr_tbl[i], time); */
}
}
#endif
for (i=0; i<8; i++)
{
double clockscale = (double)m_chip_clock / 2119040.0;
m_ar_tbl[i] = ((1<<i) / clockscale) * (double)R51;
}
for (i=0; i<8; i++)
{
double clockscale = (double)m_chip_clock / 2119040.0;
m_dr_tbl[i] = ( (1<<i) / clockscale) * (double)R52;
m_dr_tbl[i+8] = (6.25*(1<<i) / clockscale) * (double)R52;
}
}
void msm5232_device::init_voice(int i)
{
m_voi[i].ar_rate= m_ar_tbl[0] * m_external_capacity[i];
m_voi[i].dr_rate= m_dr_tbl[0] * m_external_capacity[i];
m_voi[i].rr_rate= m_dr_tbl[0] * m_external_capacity[i]; /* this is constant value */
m_voi[i].eg_sect= -1;
m_voi[i].eg = 0.0;
m_voi[i].eg_arm = 0;
m_voi[i].pitch = -1.0;
}
void msm5232_device::gate_update()
{
int new_state = (m_control2 & 0x20) ? m_voi[7].GF : 0;
if (m_gate != new_state && m_gate_handler_cb!=NULL)
{
m_gate = new_state;
m_gate_handler_cb(new_state);
}
}
int msm5232_device::get_rate() {
return m_rate;
}
void msm5232_device::init(int clock, int rate)
{
int j;
m_chip_clock = clock;
m_rate = rate ? rate : 44100; /* avoid division by 0 */
init_tables();
for (j=0; j<8; j++)
{
memset(&m_voi[j],0,sizeof(VOICE));
init_voice(j);
}
}
void msm5232_device::write(unsigned int offset, uint8_t data)
{
if (offset > 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<<n;
/* for 8' it is bit n-1 (bit 0 if n-1<0) */
/* for 4' it is bit n-2 (bit 0 if n-2<0) */
/* for 2' it is bit n-3 (bit 0 if n-3<0) */
n = (n>0)? n-1: 0;
m_voi[ch].TG_out8 = 1<<n;
n = (n>0)? n-1: 0;
m_voi[ch].TG_out4 = 1<<n;
n = (n>0)? n-1: 0;
m_voi[ch].TG_out2 = 1<<n;
}
m_voi[ch].mode = 0; /* tone mode */
m_voi[ch].eg_sect = 0; /* Key On */
}
}
else
{
if ( !m_voi[ch].eg_arm ) /* arm = 0 */
m_voi[ch].eg_sect = 2; /* Key Off -> 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<<STEP_SH;
do
{
int nextevent = left;
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;
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<<STEP_SH);
if (m_noise_clocks&4) out8 +=(1<<STEP_SH);
if (m_noise_clocks&2) out4 +=(1<<STEP_SH);
if (m_noise_clocks&1) out2 +=(1<<STEP_SH);
}
/* calculate signed output */
o16 += ( (out16-(1<<(STEP_SH-1))) * voi->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<<STEP_SH)-1);
while (cnt > 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;
}

View file

@ -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 <stdint.h>
#include <functional>
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<void(int)> 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

View file

@ -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(

View file

@ -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);

View file

@ -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,

View file

@ -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!
};

View file

@ -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,&macroHoverES5506FilterMode));
} else {
@ -4615,7 +4630,9 @@ void FurnaceGUI::drawInsEdit() {
}
}
}
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));
}