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

This commit is contained in:
cam900 2022-10-02 10:52:14 +09:00
commit 66f7ab688b
23 changed files with 1760 additions and 50 deletions

View File

@ -435,6 +435,7 @@ src/engine/platform/sound/ymz280b.cpp
src/engine/platform/sound/rf5c68.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/oki/okim6258.cpp
src/engine/platform/sound/snes/SPC_DSP.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/tia.cpp
src/engine/platform/saa.cpp src/engine/platform/saa.cpp
src/engine/platform/amiga.cpp src/engine/platform/amiga.cpp
src/engine/platform/msm5232.cpp
src/engine/platform/msm6258.cpp src/engine/platform/msm6258.cpp
src/engine/platform/msm6295.cpp src/engine/platform/msm6295.cpp
src/engine/platform/pcspkr.cpp src/engine/platform/pcspkr.cpp

View File

@ -452,8 +452,8 @@ size | description
4 | size of this block 4 | size of this block
2 | format version (see header) 2 | format version (see header)
1 | instrument type 1 | instrument type
| - 0: standard | - 0: SN76489/standard
| - 1: FM (OPM/OPN) | - 1: FM (OPN)
| - 2: Game Boy | - 2: Game Boy
| - 3: C64 | - 3: C64
| - 4: Amiga/sample | - 4: Amiga/sample
@ -485,6 +485,17 @@ size | description
| - 30: Sound Unit | - 30: Sound Unit
| - 31: Namco WSG | - 31: Namco WSG
| - 32: OPL (drums) | - 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 1 | reserved
STR | instrument name STR | instrument name
--- | **FM instrument data** --- | **FM instrument data**

View File

@ -21,6 +21,7 @@
#include "engine.h" #include "engine.h"
#include "platform/genesis.h" #include "platform/genesis.h"
#include "platform/genesisext.h" #include "platform/genesisext.h"
#include "platform/msm5232.h"
#include "platform/msm6258.h" #include "platform/msm6258.h"
#include "platform/msm6295.h" #include "platform/msm6295.h"
#include "platform/namcowsg.h" #include "platform/namcowsg.h"
@ -390,6 +391,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_MSM6295: case DIV_SYSTEM_MSM6295:
dispatch=new DivPlatformMSM6295; dispatch=new DivPlatformMSM6295;
break; break;
case DIV_SYSTEM_MSM5232:
dispatch=new DivPlatformMSM5232;
break;
case DIV_SYSTEM_NAMCO: case DIV_SYSTEM_NAMCO:
dispatch=new DivPlatformNamcoWSG; dispatch=new DivPlatformNamcoWSG;
// Pac-Man (TODO: support Pole Position?) // Pac-Man (TODO: support Pole Position?)

View File

@ -2127,12 +2127,13 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
blip_clear(samp_bb); blip_clear(samp_bb);
double rate=song.sample[sample]->rate; double rate=song.sample[sample]->rate;
if (note>=0) { if (note>=0) {
rate=(song.tuning*pow(2.0,(double)(note+3)/12.0)*((double)song.sample[sample]->centerRate/8363.0)); rate=(pow(2.0,(double)(note)/12.0)*((double)song.sample[sample]->centerRate)*0.0625);
if (rate<=0) rate=song.sample[sample]->rate; if (rate<=0) rate=song.sample[sample]->rate;
} }
if (rate<100) rate=100; if (rate<100) rate=100;
blip_set_rates(samp_bb,rate,got.rate); blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0; samp_prevSample=0;
sPreview.rate=rate;
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0; sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
sPreview.sample=sample; sPreview.sample=sample;
sPreview.wave=-1; sPreview.wave=-1;
@ -2166,6 +2167,7 @@ void DivEngine::previewWave(int wave, int note) {
if (rate<100) rate=100; if (rate<100) rate=100;
blip_set_rates(samp_bb,rate,got.rate); blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0; samp_prevSample=0;
sPreview.rate=rate;
sPreview.pos=0; sPreview.pos=0;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=wave; sPreview.wave=wave;
@ -2181,6 +2183,18 @@ void DivEngine::stopWavePreview() {
BUSY_END; BUSY_END;
} }
bool DivEngine::isPreviewingSample() {
return (sPreview.sample>=0 && sPreview.sample<(int)song.sample.size());
}
int DivEngine::getSamplePreviewPos() {
return sPreview.pos;
}
double DivEngine::getSamplePreviewRate() {
return sPreview.rate;
}
String DivEngine::getConfigPath() { String DivEngine::getConfigPath() {
return configPath; return configPath;
} }

View File

@ -387,12 +387,14 @@ class DivEngine {
DivSystem sysFileMapDMF[256]; DivSystem sysFileMapDMF[256];
struct SamplePreview { struct SamplePreview {
double rate;
int sample; int sample;
int wave; int wave;
int pos; int pos;
int pBegin, pEnd; int pBegin, pEnd;
bool dir; bool dir;
SamplePreview(): SamplePreview():
rate(0.0),
sample(-1), sample(-1),
wave(-1), wave(-1),
pos(0), pos(0),
@ -601,6 +603,11 @@ class DivEngine {
// reset playback state // reset playback state
void syncReset(); void syncReset();
// sample preview query
bool isPreviewingSample();
int getSamplePreviewPos();
double getSamplePreviewRate();
// trigger sample preview // trigger sample preview
void previewSample(int sample, int note=-1, int pStart=-1, int pEnd=-1); void previewSample(int sample, int note=-1, int pStart=-1, int pEnd=-1);
void stopSamplePreview(); void stopSamplePreview();

View File

@ -1502,11 +1502,36 @@ bool DivInstrument::saveDMP(const char* path) {
} }
w->writeC(std.arpMacro.len); w->writeC(std.arpMacro.len);
bool arpMacroMode=false;
int arpMacroHowManyFixed=0;
int realArpMacroLen=std.arpMacro.len;
for (int i=0; i<std.arpMacro.len; i++) { for (int i=0; i<std.arpMacro.len; i++) {
w->writeI(std.arpMacro.val[i]+12); if ((std.arpMacro.val[i]&0xc0000000)==0x40000000 || (std.arpMacro.val[i]&0xc0000000)==0x80000000) {
arpMacroHowManyFixed++;
}
} }
if (std.arpMacro.len>0) w->writeC(std.arpMacro.loop); if (arpMacroHowManyFixed>=std.arpMacro.len-1) {
w->writeC(std.arpMacro.mode); arpMacroMode=true;
}
if (std.arpMacro.len>0) {
if (arpMacroMode && std.arpMacro.val[std.arpMacro.len-1]==0 && std.arpMacro.loop>=std.arpMacro.len) {
realArpMacroLen--;
}
}
if (arpMacroMode) {
for (int i=0; i<realArpMacroLen; i++) {
w->writeI(std.arpMacro.val[i]);
}
} else {
for (int i=0; i<realArpMacroLen; i++) {
w->writeI(std.arpMacro.val[i]+12);
}
}
if (realArpMacroLen>0) {
w->writeC(std.arpMacro.loop);
}
w->writeC(arpMacroMode);
w->writeC(std.dutyMacro.len); w->writeC(std.dutyMacro.len);
for (int i=0; i<std.dutyMacro.len; i++) { for (int i=0; i<std.dutyMacro.len; i++) {

View File

@ -72,6 +72,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_QSOUND=40, DIV_INS_QSOUND=40,
DIV_INS_YMZ280B=41, DIV_INS_YMZ280B=41,
DIV_INS_RF5C68=42, DIV_INS_RF5C68=42,
DIV_INS_MSM5232=43,
DIV_INS_MAX, DIV_INS_MAX,
DIV_INS_NULL DIV_INS_NULL
}; };

View File

@ -0,0 +1,421 @@
/**
* 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]*partVolume[i])>>8;
}
}
}
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_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));
}
chan[i].freqChanged=true;
}
if (chan[i].std.duty.had) {
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
groupAR[i>>2]=attackMap[chan[i].std.ex1.val&7];
updateGroupAR[i>>2]=true;
}
if (chan[i].std.ex2.had) { // decay
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);
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,chan[i].noise?0xd8:(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;
}
}
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) {
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;
}
}
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;
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)));
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 127;
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformMSM5232::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
msm->mute(ch,mute);
}
void DivPlatformMSM5232::forceIns() {
for (int i=0; i<8; i++) {
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) {
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;
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]);
}
}
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;
detune=flags.getInt("detune",0);
msm->set_clock(chipClock+detune*1024);
rate=msm->get_rate();
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) {
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->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,106 @@
/**
* 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(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;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
int cycles, curChan, delay, detune;
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,735 @@
// 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;
}
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 */
/* 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].eg_ext = 0;
m_voi[i].pitch = -1.0;
m_voi[i].mute = false;
}
void msm5232_device::mute(int voice, bool mute)
{
m_voi[voice].mute = mute;
}
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_voi[i].eg_ext = !(data&0x20);
}
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_voi[i+4].eg_ext = !(data&0x20);
}
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
{
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;
}
}
/* 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 */
if (!voi->mute) {
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,106 @@
// 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);
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);
void set_clock(int clock);
void mute(int voice, bool mute);
// 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;
bool mute;
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 */
uint8_t eg_ext; /* inhibit envelope generator */
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) */
double m_external_input[8];
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

@ -114,7 +114,7 @@ enum DivSystem {
DIV_SYSTEM_NAMCO_CUS30, DIV_SYSTEM_NAMCO_CUS30,
DIV_SYSTEM_YM2612_FRAC, DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT, DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_RESERVED_8, DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28, DIV_SYSTEM_T6W28,
DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_DUMMY, DIV_SYSTEM_DUMMY,

View File

@ -1646,14 +1646,13 @@ void DivEngine::registerSystems() {
namcoEffectHandlerMap namcoEffectHandlerMap
); );
// replace with an 8-channel chip in a future sysDefs[DIV_SYSTEM_MSM5232]=new DivSysDef(
sysDefs[DIV_SYSTEM_RESERVED_8]=new DivSysDef( "OKI MSM5232", NULL, 0xbc, 0, 8, false, true, 0, false, 0,
"Reserved", NULL, 0xbc, 0, 8, false, true, 0, false, 0, "a square wave additive synthesis chip made by OKI. used in some arcade machines and instruments.",
"this was YM2612_FRAC, but due to changes this ID is reserved.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {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( 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]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_RF5C68]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break; 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: default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i);

View File

@ -822,6 +822,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_CUT: { case GUI_ACTION_SAMPLE_CUT: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
if (end-start<1) break; if (end-start<1) break;
@ -866,6 +867,7 @@ void FurnaceGUI::doAction(int what) {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
if (sampleClipboard==NULL || sampleClipboardLen<1) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1; if (pos>=(int)sample->samples) pos=sample->samples-1;
@ -896,6 +898,7 @@ void FurnaceGUI::doAction(int what) {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
if (sampleClipboard==NULL || sampleClipboardLen<1) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1; if (pos>=(int)sample->samples) pos=sample->samples-1;
@ -926,6 +929,7 @@ void FurnaceGUI::doAction(int what) {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
if (sampleClipboard==NULL || sampleClipboardLen<1) break; if (sampleClipboard==NULL || sampleClipboardLen<1) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart;
if (pos>=(int)sample->samples) pos=sample->samples-1; if (pos>=(int)sample->samples) pos=sample->samples-1;
@ -981,6 +985,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_NORMALIZE: { case GUI_ACTION_SAMPLE_NORMALIZE: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1028,6 +1033,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_FADE_IN: { case GUI_ACTION_SAMPLE_FADE_IN: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1058,6 +1064,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_FADE_OUT: { case GUI_ACTION_SAMPLE_FADE_OUT: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1092,6 +1099,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_SILENCE: { case GUI_ACTION_SAMPLE_SILENCE: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1116,6 +1124,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_DELETE: { case GUI_ACTION_SAMPLE_DELETE: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1133,6 +1142,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_TRIM: { case GUI_ACTION_SAMPLE_TRIM: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1150,6 +1160,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_REVERSE: { case GUI_ACTION_SAMPLE_REVERSE: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1182,6 +1193,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_INVERT: { case GUI_ACTION_SAMPLE_INVERT: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;
@ -1208,6 +1220,7 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_SIGN: { case GUI_ACTION_SAMPLE_SIGN: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) break;
sample->prepareUndo(true); sample->prepareUndo(true);
e->lockEngine([this,sample]() { e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN; SAMPLE_OP_BEGIN;

View File

@ -2134,18 +2134,20 @@ void FurnaceGUI::processDrags(int dragX, int dragY) {
if (x1>=(int)sampleDragLen) x1=sampleDragLen-1; if (x1>=(int)sampleDragLen) x1=sampleDragLen-1;
double y=0.5-double(dragY-sampleDragStart.y)/sampleDragAreaSize.y; double y=0.5-double(dragY-sampleDragStart.y)/sampleDragAreaSize.y;
if (sampleDragMode) { // draw if (sampleDragMode) { // draw
if (sampleDrag16) { if (sampleDragTarget) {
int val=y*65536; if (sampleDrag16) {
if (val<-32768) val=-32768; int val=y*65536;
if (val>32767) val=32767; if (val<-32768) val=-32768;
for (int i=x; i<=x1; i++) ((short*)sampleDragTarget)[i]=val; if (val>32767) val=32767;
} else { for (int i=x; i<=x1; i++) ((short*)sampleDragTarget)[i]=val;
int val=y*256; } else {
if (val<-128) val=-128; int val=y*256;
if (val>127) val=127; if (val<-128) val=-128;
for (int i=x; i<=x1; i++) ((signed char*)sampleDragTarget)[i]=val; if (val>127) val=127;
for (int i=x; i<=x1; i++) ((signed char*)sampleDragTarget)[i]=val;
}
updateSampleTex=true;
} }
updateSampleTex=true;
} else { // select } else { // select
if (sampleSelStart<0) { if (sampleSelStart<0) {
sampleSelStart=x; sampleSelStart=x;
@ -2836,7 +2838,7 @@ void FurnaceGUI::pointDown(int x, int y, int button) {
} }
void FurnaceGUI::pointUp(int x, int y, int button) { void FurnaceGUI::pointUp(int x, int y, int button) {
if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode)) { if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode && sampleDragTarget)) {
MARK_MODIFIED; MARK_MODIFIED;
} }
if (macroDragActive && macroDragLineMode && !macroDragMouseMoved) { if (macroDragActive && macroDragLineMode && !macroDragMouseMoved) {
@ -5395,6 +5397,7 @@ FurnaceGUI::FurnaceGUI():
editOptsVisible(false), editOptsVisible(false),
latchNibble(false), latchNibble(false),
nonLatchNibble(false), nonLatchNibble(false),
keepLoopAlive(false),
curWindow(GUI_WINDOW_NOTHING), curWindow(GUI_WINDOW_NOTHING),
nextWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING),
curWindowLast(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING),

View File

@ -170,6 +170,7 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_QSOUND, GUI_COLOR_INSTR_QSOUND,
GUI_COLOR_INSTR_YMZ280B, GUI_COLOR_INSTR_YMZ280B,
GUI_COLOR_INSTR_RF5C68, GUI_COLOR_INSTR_RF5C68,
GUI_COLOR_INSTR_MSM5232,
GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG, GUI_COLOR_CHANNEL_BG,
@ -217,6 +218,15 @@ enum FurnaceGUIColors {
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_MISC, GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_SAMPLE_BG,
GUI_COLOR_SAMPLE_FG,
GUI_COLOR_SAMPLE_LOOP,
GUI_COLOR_SAMPLE_CENTER,
GUI_COLOR_SAMPLE_GRID,
GUI_COLOR_SAMPLE_SEL,
GUI_COLOR_SAMPLE_SEL_POINT,
GUI_COLOR_SAMPLE_NEEDLE,
GUI_COLOR_PAT_MANAGER_NULL, GUI_COLOR_PAT_MANAGER_NULL,
GUI_COLOR_PAT_MANAGER_USED, GUI_COLOR_PAT_MANAGER_USED,
GUI_COLOR_PAT_MANAGER_OVERUSED, GUI_COLOR_PAT_MANAGER_OVERUSED,
@ -1357,6 +1367,7 @@ class FurnaceGUI {
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
bool keepLoopAlive;
FurnaceGUIWindows curWindow, nextWindow, curWindowLast; FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
float peak[2]; float peak[2];
float patChanX[DIV_MAX_CHANS+1]; float patChanX[DIV_MAX_CHANS+1];

View File

@ -113,8 +113,8 @@ const char* insTypes[DIV_INS_MAX+1]={
"Sound Unit", "Sound Unit",
"Namco WSG", "Namco WSG",
"OPL (drums)", "OPL (drums)",
"FM (OPM)", // 33 "FM (OPM)",
"NES", // 34 "NES",
"MSM6258", "MSM6258",
"MSM6295", "MSM6295",
"ADPCM-A", "ADPCM-A",
@ -123,6 +123,7 @@ const char* insTypes[DIV_INS_MAX+1]={
"QSound", "QSound",
"YMZ280B", "YMZ280B",
"RF5C68", "RF5C68",
"MSM5232",
NULL 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_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_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_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_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)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -846,6 +848,15 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"",ImVec4(0.0f,1.0f,0.5f,1.0f)), D(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
D(GUI_COLOR_PATTERN_EFFECT_MISC,"",ImVec4(0.3f,0.3f,1.0f,1.0f)), D(GUI_COLOR_PATTERN_EFFECT_MISC,"",ImVec4(0.3f,0.3f,1.0f,1.0f)),
D(GUI_COLOR_SAMPLE_BG,"",ImVec4(0.04f,0.13f,0.2f,1.0f)),
D(GUI_COLOR_SAMPLE_FG,"",ImVec4(0.7f,0.7f,0.7f,1.0f)),
D(GUI_COLOR_SAMPLE_LOOP,"",ImVec4(0.1f,0.22f,0.35f,1.0f)),
D(GUI_COLOR_SAMPLE_CENTER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_SAMPLE_GRID,"",ImVec4(0.1f,0.1f,0.15f,1.0f)),
D(GUI_COLOR_SAMPLE_SEL,"",ImVec4(0.26f,0.59f,0.98f,0.25f)),
D(GUI_COLOR_SAMPLE_SEL_POINT,"",ImVec4(0.06f,0.53f,0.98f,0.5f)),
D(GUI_COLOR_SAMPLE_NEEDLE,"",ImVec4(1.0f,0.8f,0.0f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_NULL,"",ImVec4(0.15f,0.15f,0.15f,1.0f)), D(GUI_COLOR_PAT_MANAGER_NULL,"",ImVec4(0.15f,0.15f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_USED,"",ImVec4(0.15f,1.0f,0.15f,1.0f)), D(GUI_COLOR_PAT_MANAGER_USED,"",ImVec4(0.15f,1.0f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_OVERUSED,"",ImVec4(1.0f,1.0f,0.15f,1.0f)), D(GUI_COLOR_PAT_MANAGER_OVERUSED,"",ImVec4(1.0f,1.0f,0.15f,1.0f)),
@ -944,6 +955,7 @@ const int availableSystems[]={
DIV_SYSTEM_MSM6295, DIV_SYSTEM_MSM6295,
DIV_SYSTEM_RF5C68, DIV_SYSTEM_RF5C68,
DIV_SYSTEM_SNES, DIV_SYSTEM_SNES,
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PCM_DAC,
0 // don't remove this last one! 0 // don't remove this last one!
}; };
@ -988,6 +1000,7 @@ const int chipsSquare[]={
DIV_SYSTEM_PCSPKR, DIV_SYSTEM_PCSPKR,
DIV_SYSTEM_SAA1099, DIV_SYSTEM_SAA1099,
DIV_SYSTEM_VIC20, DIV_SYSTEM_VIC20,
DIV_SYSTEM_MSM5232,
0 // don't remove this last one! 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 "0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL
}; };
const char* msm5232ControlBits[7]={
"2'", "4'", "8'", "16'", "sustain", NULL
};
const char* x1_010EnvBits[8]={ const char* x1_010EnvBits[8]={
"enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL "enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL
}; };
@ -4513,7 +4517,7 @@ void FurnaceGUI::drawInsEdit() {
} }
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_MIKEY || 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_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; volMax=127;
} }
if (ins->type==DIV_INS_GB) { if (ins->type==DIV_INS_GB) {
@ -4576,6 +4580,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="Duty/Int"; dutyLabel="Duty/Int";
dutyMax=ins->amiga.useSample?0:10; dutyMax=ins->amiga.useSample?0:10;
} }
if (ins->type==DIV_INS_MSM5232) {
dutyLabel="Group Ctrl";
dutyMax=5;
}
if (ins->type==DIV_INS_BEEPER) { if (ins->type==DIV_INS_BEEPER) {
dutyLabel="Pulse Width"; dutyLabel="Pulse Width";
dutyMax=255; dutyMax=255;
@ -4684,6 +4692,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_ADPCMB) waveMax=0; if (ins->type==DIV_INS_ADPCMB) waveMax=0;
if (ins->type==DIV_INS_QSOUND) waveMax=0; if (ins->type==DIV_INS_QSOUND) waveMax=0;
if (ins->type==DIV_INS_YMZ280B) 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_MSM6258) waveMax=0;
if (ins->type==DIV_INS_MSM6295) waveMax=0; if (ins->type==DIV_INS_MSM6295) waveMax=0;
if (ins->type==DIV_INS_SEGAPCM) waveMax=0; if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
@ -4751,6 +4760,10 @@ void FurnaceGUI::drawInsEdit() {
ex1Max=5; ex1Max=5;
ex2Max=5; ex2Max=5;
} }
if (ins->type==DIV_INS_MSM5232) {
ex1Max=5;
ex2Max=11;
}
int panMin=0; int panMin=0;
int panMax=0; int panMax=0;
@ -4815,6 +4828,8 @@ void FurnaceGUI::drawInsEdit() {
if (dutyMax>0) { if (dutyMax>0) {
if (ins->type==DIV_INS_MIKEY) { 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)); 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) { } 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)); macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,&macroHoverES5506FilterMode));
} else { } else {
@ -4845,7 +4860,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 || if (ins->type==DIV_INS_FM ||
ins->type==DIV_INS_OPM || ins->type==DIV_INS_OPM ||
ins->type==DIV_INS_STD || ins->type==DIV_INS_STD ||
@ -4897,6 +4914,8 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Echo Feedback",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Echo Feedback",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SNES) { } 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)); 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 { } else {
macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} }
@ -4916,6 +4935,8 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Echo Length",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Echo Length",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SNES) { } 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)); 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 { } else {
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits));
} }
@ -4958,6 +4979,9 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_SNES) { if (ins->type==DIV_INS_SNES) {
macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
} }
if (ins->type==DIV_INS_MSM5232) {
macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
drawMacros(macroList); drawMacros(macroList);
ImGui::EndTabItem(); ImGui::EndTabItem();

View File

@ -306,6 +306,12 @@ void FurnaceGUI::initSystemPresets() {
0 0
} }
)); ));
cat.systems.push_back(FurnaceGUISysDef(
"OKI MSM5232", {
DIV_SYSTEM_MSM5232, 64, 0, 0,
0
}
));
sysCategories.push_back(cat); sysCategories.push_back(cat);
cat=FurnaceGUISysCategory("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis."); cat=FurnaceGUISysCategory("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis.");

View File

@ -121,10 +121,14 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Length: %d",sample->samples); ImGui::Text("Length: %d",sample->samples);
if (doLoop) { if (doLoop || keepLoopAlive) {
keepLoopAlive=false;
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Loop Mode"); ImGui::Text("Loop Mode");
@ -156,6 +160,12 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Loop End"); ImGui::Text("Loop End");
ImGui::SameLine(); ImGui::SameLine();
@ -169,6 +179,12 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
} }
ImGui::EndTable(); ImGui::EndTable();
} }
@ -181,8 +197,6 @@ void FurnaceGUI::drawSampleEdit() {
*/ */
ImGui::Separator(); ImGui::Separator();
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
pushToggleColors(!sampleDragMode); pushToggleColors(!sampleDragMode);
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
sampleDragMode=false; sampleDragMode=false;
@ -200,6 +214,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Draw"); ImGui::SetTooltip("Edit mode: Draw");
} }
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::SameLine(); ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
ImGui::SameLine(); ImGui::SameLine();
@ -567,6 +582,7 @@ void FurnaceGUI::drawSampleEdit() {
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
ImGui::SameLine(); ImGui::SameLine();
@ -681,10 +697,14 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Length: %d",sample->samples); ImGui::Text("Length: %d",sample->samples);
if (doLoop) { if (doLoop || keepLoopAlive) {
keepLoopAlive=false;
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Loop Mode"); ImGui::Text("Loop Mode");
@ -717,6 +737,12 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Loop End"); ImGui::Text("Loop End");
ImGui::SameLine(); ImGui::SameLine();
@ -730,6 +756,12 @@ void FurnaceGUI::drawSampleEdit() {
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
}
} }
ImGui::EndTable(); ImGui::EndTable();
} }
@ -742,8 +774,6 @@ void FurnaceGUI::drawSampleEdit() {
*/ */
ImGui::Separator(); ImGui::Separator();
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
pushToggleColors(!sampleDragMode); pushToggleColors(!sampleDragMode);
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
sampleDragMode=false; sampleDragMode=false;
@ -761,6 +791,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Edit mode: Draw"); ImGui::SetTooltip("Edit mode: Draw");
} }
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::SameLine(); ImGui::SameLine();
ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale));
ImGui::SameLine(); ImGui::SameLine();
@ -1135,6 +1166,8 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::SetTooltip("Trim"); ImGui::SetTooltip("Trim");
} }
ImGui::EndDisabled();
if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) {
e->previewSample(curSample); e->previewSample(curSample);
} }
@ -1236,10 +1269,10 @@ void FurnaceGUI::drawSampleEdit() {
if (SDL_LockTexture(sampleTex,NULL,(void**)&data,&pitch)!=0) { if (SDL_LockTexture(sampleTex,NULL,(void**)&data,&pitch)!=0) {
logE("error while locking sample texture! %s",SDL_GetError()); logE("error while locking sample texture! %s",SDL_GetError());
} else { } else {
ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg); ImU32 bgColor=ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_BG]);
ImU32 bgColorLoop=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_FrameBgHovered,0.5)); ImU32 bgColorLoop=ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_LOOP]);
ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines); ImU32 lineColor=ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_FG]);
ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); ImU32 centerLineColor=ImGui::GetColorU32(uiColors[GUI_COLOR_SAMPLE_CENTER]);
for (int i=0; i<availY; i++) { for (int i=0; i<availY; i++) {
for (int j=0; j<availX; j++) { for (int j=0; j<availX; j++) {
int scaledPos=samplePos+(j*sampleZoom); int scaledPos=samplePos+(j*sampleZoom);
@ -1314,11 +1347,23 @@ void FurnaceGUI::drawSampleEdit() {
sampleSelStart=0; sampleSelStart=0;
sampleSelEnd=sample->samples; sampleSelEnd=sample->samples;
} else { } else {
if (sample->samples>0 && (sample->depth==DIV_SAMPLE_DEPTH_16BIT || sample->depth==DIV_SAMPLE_DEPTH_8BIT)) { if (sample->samples>0) {
sampleDragStart=rectMin; sampleDragStart=rectMin;
sampleDragAreaSize=rectSize; sampleDragAreaSize=rectSize;
sampleDrag16=(sample->depth==DIV_SAMPLE_DEPTH_16BIT); switch (sample->depth) {
sampleDragTarget=(sample->depth==DIV_SAMPLE_DEPTH_16BIT)?((void*)sample->data16):((void*)sample->data8); case DIV_SAMPLE_DEPTH_8BIT:
sampleDrag16=false;
sampleDragTarget=(void*)sample->data8;
break;
case DIV_SAMPLE_DEPTH_16BIT:
sampleDrag16=true;
sampleDragTarget=(void*)sample->data16;
break;
default:
sampleDrag16=true;
sampleDragTarget=NULL;
break;
}
sampleDragLen=sample->samples; sampleDragLen=sample->samples;
sampleDragActive=true; sampleDragActive=true;
sampleSelStart=-1; sampleSelStart=-1;
@ -1334,12 +1379,15 @@ void FurnaceGUI::drawSampleEdit() {
} }
if (ImGui::BeginPopup("SRightClick",ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopup("SRightClick",ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_SAMPLE_CUT))) { if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_SAMPLE_CUT))) {
doAction(GUI_ACTION_SAMPLE_CUT); doAction(GUI_ACTION_SAMPLE_CUT);
} }
ImGui::EndDisabled();
if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_SAMPLE_COPY))) { if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_SAMPLE_COPY))) {
doAction(GUI_ACTION_SAMPLE_COPY); doAction(GUI_ACTION_SAMPLE_COPY);
} }
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_SAMPLE_PASTE))) { if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_SAMPLE_PASTE))) {
doAction(GUI_ACTION_SAMPLE_PASTE); doAction(GUI_ACTION_SAMPLE_PASTE);
} }
@ -1349,6 +1397,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::MenuItem("paste (mix)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_MIX))) { if (ImGui::MenuItem("paste (mix)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_MIX))) {
doAction(GUI_ACTION_SAMPLE_PASTE_MIX); doAction(GUI_ACTION_SAMPLE_PASTE_MIX);
} }
ImGui::EndDisabled();
if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) { if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) {
doAction(GUI_ACTION_SAMPLE_SELECT_ALL); doAction(GUI_ACTION_SAMPLE_SELECT_ALL);
} }
@ -1374,7 +1423,11 @@ void FurnaceGUI::drawSampleEdit() {
end^=start; end^=start;
start^=end; start^=end;
} }
statusBar+=fmt::sprintf(" (%d-%d)",start,end); if (start==end) {
statusBar+=fmt::sprintf(" (%d)",start);
} else {
statusBar+=fmt::sprintf(" (%d-%d: %d samples)",start,end,end-start);
}
drawSelection=true; drawSelection=true;
} }
} }
@ -1426,6 +1479,43 @@ void FurnaceGUI::drawSampleEdit() {
} }
} }
if (e->isPreviewingSample()) {
statusBar+=fmt::sprintf(" | %.2fHz",e->getSamplePreviewRate());
int start=sampleSelStart;
int end=sampleSelEnd;
if (start>end) {
start^=end;
end^=start;
start^=end;
}
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 p1=rectMin;
p1.x+=(e->getSamplePreviewPos()-samplePos)/sampleZoom;
ImVec4 posColor=uiColors[GUI_COLOR_SAMPLE_NEEDLE];
ImVec4 posTrail1=posColor;
ImVec4 posTrail2=posColor;
posTrail1.w*=0.5f;
posTrail2.w=0.0f;
float trailDistance=(e->getSamplePreviewRate()/100.0f)/sampleZoom;
if (p1.x<rectMin.x) p1.x=rectMin.x;
if (p1.x>rectMax.x) p1.x=rectMax.x;
ImVec2 p2=p1;
p2.y=rectMax.y;
dl->AddRectFilledMultiColor(
ImVec2(p1.x-trailDistance,p1.y),
p2,
ImGui::GetColorU32(posTrail2),
ImGui::GetColorU32(posTrail1),
ImGui::GetColorU32(posTrail1),
ImGui::GetColorU32(posTrail2)
);
dl->AddLine(p1,p2,ImGui::GetColorU32(posColor));
}
if (drawSelection) { if (drawSelection) {
int start=sampleSelStart; int start=sampleSelStart;
int end=sampleSelEnd; int end=sampleSelEnd;
@ -1439,10 +1529,8 @@ void FurnaceGUI::drawSampleEdit() {
p1.x+=(start-samplePos)/sampleZoom; p1.x+=(start-samplePos)/sampleZoom;
ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y); ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y);
ImVec4 boundColor=uiColors[GUI_COLOR_ACCENT_PRIMARY]; ImVec4 boundColor=uiColors[GUI_COLOR_SAMPLE_SEL_POINT];
ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY]; ImVec4 selColor=uiColors[GUI_COLOR_SAMPLE_SEL];
boundColor.w*=0.5;
selColor.w*=0.25;
if (p1.x<rectMin.x) p1.x=rectMin.x; if (p1.x<rectMin.x) p1.x=rectMin.x;
if (p1.x>rectMax.x) p1.x=rectMax.x; if (p1.x>rectMax.x) p1.x=rectMax.x;
@ -1470,11 +1558,9 @@ void FurnaceGUI::drawSampleEdit() {
} }
} }
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) { if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT && sampleDragMode) {
statusBar="Non-8/16-bit samples cannot be edited without prior conversion."; statusBar="Non-8/16-bit samples cannot be edited without prior conversion.";
} }
ImGui::EndDisabled();
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize); ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
ImGui::Text("%s",statusBar.c_str()); ImGui::Text("%s",statusBar.c_str());

View File

@ -1773,6 +1773,17 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output"); UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output");
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Sample Editor")) {
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_BG,"Background");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_FG,"Waveform");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP,"Loop region");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CENTER,"Center guide");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_GRID,"Grid");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL,"Selection");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL_POINT,"Selection points");
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE,"Preview needle");
ImGui::TreePop();
}
if (ImGui::TreeNode("Pattern Manager")) { if (ImGui::TreeNode("Pattern Manager")) {
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_NULL,"Unallocated"); UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_NULL,"Unallocated");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_UNUSED,"Unused"); UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_UNUSED,"Unused");

View File

@ -18,6 +18,7 @@
*/ */
#include "gui.h" #include "gui.h"
#include <imgui.h>
bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) { bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange) {
bool altered=false; bool altered=false;
@ -1306,6 +1307,113 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
break; break;
} }
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;
if (detune>127) detune=127;
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;
}
case DIV_SYSTEM_SWAN: case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_VERA: case DIV_SYSTEM_VERA:
case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_BUBSYS_WSG: