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

* 'master' of https://github.com/tildearrow/furnace: (58 commits)
  SMS: early Nuked-PSG modding
  SMS: add modified Nuked-PSG core
  build release and don't strip
  strip MinGW builds
  FDS: a bit more
  FDS: set a post-amp value
  FDS: fix NSFplay core low pass filter precision
  fix .dmf saving
  fix compilation on GCC 12
  Fix multithreading on CI
  Lynx: why did I not commit this
  Lynx: more sample improvements
  Lynx: sample improvements
  Lynx: add sample support!
  GUI: fix wavetable list oversight
  WaveSynth: fix phase modulation - again
  GUI: fix possible wave editor crash
  WaveSynth: fix phase modulation
  Lynx: add phase reset macro
  fix another fucking IGFD crash bug
  ...

# Conflicts:
#	src/gui/insEdit.cpp
#	src/gui/presets.cpp
This commit is contained in:
cam900 2022-05-27 13:01:06 +09:00
commit cbf20c6320
116 changed files with 8748 additions and 857 deletions

View file

@ -21,6 +21,8 @@
#include "engine.h"
#include "platform/genesis.h"
#include "platform/genesisext.h"
#include "platform/msm6258.h"
#include "platform/msm6295.h"
#include "platform/namcowsg.h"
#include "platform/sms.h"
#include "platform/opll.h"
@ -190,6 +192,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
break;
case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS;
((DivPlatformSMS*)dispatch)->setNuked(eng->getConfInt("snCore",0));
break;
case DIV_SYSTEM_GB:
dispatch=new DivPlatformGB;
@ -368,6 +371,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_SOUND_UNIT:
dispatch=new DivPlatformSoundUnit;
break;
case DIV_SYSTEM_MSM6258:
dispatch=new DivPlatformMSM6258;
break;
case DIV_SYSTEM_MSM6295:
dispatch=new DivPlatformMSM6295;
break;
case DIV_SYSTEM_NAMCO:
dispatch=new DivPlatformNamcoWSG;
// Pac-Man (TODO: support Pole Position?)

View file

@ -23,7 +23,9 @@
#include "safeReader.h"
#include "../ta-log.h"
#include "../fileutils.h"
#ifdef HAVE_SDL2
#include "../audio/sdl.h"
#endif
#include <stdexcept>
#ifndef _WIN32
#include <unistd.h>
@ -34,7 +36,9 @@
#include "../audio/jack.h"
#endif
#include <math.h>
#ifdef HAVE_SNDFILE
#include <sndfile.h>
#endif
#include <fmt/printf.h>
void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) {
@ -187,6 +191,7 @@ bool DivEngine::isExporting() {
#define EXPORT_BUFSIZE 2048
#ifdef HAVE_SNDFILE
void DivEngine::runExportThread() {
switch (exportMode) {
case DIV_EXPORT_MODE_ONE: {
@ -438,8 +443,16 @@ void DivEngine::runExportThread() {
}
stopExport=false;
}
#else
void DivEngine::runExportThread() {
}
#endif
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) {
#ifndef HAVE_SNDFILE
logE("Furnace was not compiled with libsndfile. cannot export!");
return false;
#else
exportPath=path;
exportMode=mode;
if (exportMode!=DIV_EXPORT_MODE_ONE) {
@ -461,6 +474,7 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode)
remainingLoops=loops;
exportThread=new std::thread(_runExportThread,this);
return true;
#endif
}
void DivEngine::waitAudioFile() {
@ -2027,6 +2041,10 @@ int DivEngine::addSampleFromFile(const char* path) {
}
}
#ifndef HAVE_SNDFILE
lastError="Furnace was not compiled with libsndfile!";
return -1;
#else
SF_INFO si;
SNDFILE* f=sf_open(path,SFM_READ,&si);
if (f==NULL) {
@ -2107,6 +2125,7 @@ int DivEngine::addSampleFromFile(const char* path) {
renderSamples();
BUSY_END;
return sampleCount;
#endif
}
void DivEngine::delSample(int index) {
@ -2757,13 +2776,23 @@ bool DivEngine::initAudioBackend() {
logE("Furnace was not compiled with JACK support!");
setConf("audioEngine","SDL");
saveConf();
#ifdef HAVE_SDL2
output=new TAAudioSDL;
#else
logE("Furnace was not compiled with SDL support either!");
output=new TAAudio;
#endif
#else
output=new TAAudioJACK;
#endif
break;
case DIV_AUDIO_SDL:
#ifdef HAVE_SDL2
output=new TAAudioSDL;
#else
logE("Furnace was not compiled with SDL support!");
output=new TAAudio;
#endif
break;
case DIV_AUDIO_DUMMY:
output=new TAAudio;
@ -2858,7 +2887,7 @@ bool DivEngine::init() {
// init config
#ifdef _WIN32
configPath=getWinConfigPath();
#elif defined(ANDROID)
#elif defined(IS_MOBILE)
configPath=SDL_GetPrefPath("tildearrow","furnace");
#else
struct stat st;

View file

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev96"
#define DIV_ENGINE_VERSION 96
#define DIV_VERSION "dev97"
#define DIV_ENGINE_VERSION 97
// for imports
#define DIV_VERSION_MOD 0xff01

View file

@ -166,6 +166,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.e1e2AlsoTakePriority=true;
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
// 1.1 compat flags
if (ds.version>24) {
@ -307,19 +308,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
logI("reading instruments (%d)...",ds.insLen);
for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument;
unsigned char mode=0;
if (ds.version>0x05) {
ins->name=reader.readString((unsigned char)reader.readC());
}
logD("%d name: %s",i,ins->name.c_str());
if (ds.version<0x0b) {
// instruments in ancient versions were all FM or STD.
ins->mode=1;
mode=1;
} else {
unsigned char mode=reader.readC();
mode=reader.readC();
if (mode>1) logW("%d: invalid instrument mode %d!",i,mode);
ins->mode=mode;
}
ins->type=ins->mode?DIV_INS_FM:DIV_INS_STD;
ins->type=mode?DIV_INS_FM:DIV_INS_STD;
if (ds.system[0]==DIV_SYSTEM_GB) {
ins->type=DIV_INS_GB;
}
@ -329,7 +330,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
if (!ins->mode) {
if (!mode) {
ins->type=DIV_INS_AY;
}
}
@ -343,7 +344,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ins->type=DIV_INS_OPL;
}
if (ins->mode) { // FM
if (mode) { // FM
if (ds.version>0x05) {
ins->fm.alg=reader.readC();
if (ds.version<0x13) {
@ -1027,6 +1028,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<90) {
ds.pitchMacroIsLinear=false;
}
if (ds.version<97) {
ds.oldOctaveBoundary=true;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1404,7 +1408,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<14; i++) {
if (ds.version>=97) {
ds.oldOctaveBoundary=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<13; i++) {
reader.readC();
}
}
@ -2405,8 +2414,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
const int dpcmNotes=(blockVersion>=2)?96:72;
for (int j=0; j<dpcmNotes; j++) {
ins->amiga.noteMap[j]=(short)((unsigned char)reader.readC())-1;
ins->amiga.noteFreq[j]=(unsigned char)reader.readC();
ins->amiga.noteMap[j].ind=(short)((unsigned char)reader.readC())-1;
ins->amiga.noteMap[j].freq=(unsigned char)reader.readC();
if (blockVersion>=6) {
reader.readC(); // DMC value
}
@ -2884,7 +2893,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.snDutyReset);
w->writeC(song.pitchMacroIsLinear);
w->writeC(song.pitchSlideSpeed);
for (int i=0; i<14; i++) {
w->writeC(song.oldOctaveBoundary);
for (int i=0; i<13; i++) {
w->writeC(0);
}
@ -3234,15 +3244,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeString(i->name,true);
// safety check
if (!isFMSystem(sys) && i->mode) {
i->mode=0;
if (!isFMSystem(sys) && i->type!=DIV_INS_STD && i->type!=DIV_INS_FDS) {
i->type=DIV_INS_STD;
}
if (!isSTDSystem(sys) && i->mode==0) {
i->mode=1;
if (!isSTDSystem(sys) && i->type!=DIV_INS_FM) {
i->type=DIV_INS_FM;
}
w->writeC(i->mode);
if (i->mode) { // FM
w->writeC((i->type==DIV_INS_FM || i->type==DIV_INS_OPLL)?1:0);
if (i->type==DIV_INS_FM || i->type==DIV_INS_OPLL) { // FM
w->writeC(i->fm.alg);
w->writeC(i->fm.fb);
w->writeC(i->fm.fms);

View file

@ -326,7 +326,7 @@ struct DivInstrumentAmiga {
double sliceEnd;
// inlines
inline double updateSize(double length, double loopStart, double loopEnd) {
inline void updateSize(double length, double loopStart, double loopEnd) {
sliceSize=loopEnd-loopStart;
sliceBound=(length-sliceSize);
}
@ -589,7 +589,6 @@ struct DivInstrument {
bool save(const char* path);
DivInstrument():
name(""),
mode(false),
type(DIV_INS_FM) {
}
};

View file

@ -207,7 +207,7 @@ void DivPlatformAmiga::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -337,7 +337,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -345,7 +345,7 @@ void DivPlatformArcade::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}

View file

@ -224,7 +224,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -242,7 +242,7 @@ void DivPlatformAY8930::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -124,7 +124,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -191,7 +191,7 @@ void DivPlatformC64::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -284,7 +284,7 @@ void DivPlatformES5506::e_pin(bool state)
}
if (chan[ch].transwaveIRQ) {
if ((chan[ch].cr&0x37)==0x34) { // IRQE = 1, BLE = 1, LPE = 0, LEI = 1
DivInstrument* ins=parent->getIns(chan[i].ins);
DivInstrument* ins=parent->getIns(chan[ch].ins);
if (!ins->amiga.useNoteMap && ins->amiga.transWave.enable) {
const int next=chan[ch].pcm.next;
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
@ -316,8 +316,7 @@ void DivPlatformES5506::e_pin(bool state)
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[ch].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[ch].transWave.slice=transWaveInd.slice;
chan[ch].transWave.slicePos(chan[ch].transWave.slice);
chan[ch].transWave.slicePos(double(chan[ch].transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
@ -371,10 +370,10 @@ void DivPlatformES5506::e_pin(bool state)
}
chan[ch].transwaveIRQ=false;
}
if (chan[ch].isTransWave) {
if (chan[ch].isTranswave) {
pageReadMask(0x00|ch,0x5f,0x00,&chan[ch].cr);
chan[ch].transwaveIRQ=true;
chan[ch].isTransWave=false;
chan[ch].isTranswave=false;
}
}
}
@ -640,7 +639,7 @@ void DivPlatformES5506::tick(bool sysTick) {
chan[i].volChanged.changed=0;
}
if (chan[i].pcmChanged.changed) {
if (!chan[i].isTransWave) {
if (!chan[i].isTranswave) {
if (chan[i].pcmChanged.transwaveInd && (!ins->amiga.useNoteMap && ins->amiga.transWave.enable)) {
const int next=chan[i].pcm.next;
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
@ -667,8 +666,7 @@ void DivPlatformES5506::tick(bool sysTick) {
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[i].transWave.slice=transWaveInd.slice;
chan[i].transWave.slicePos(transWaveInd.slice);
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
@ -688,7 +686,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
chan[i].pcmChanged.transwaveInd=0;
}
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTransWave)) {
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTranswave)) {
if (chan[i].pcmChanged.index) {
const int next=chan[i].pcm.next;
bool sampleVaild=false;
@ -737,8 +735,7 @@ void DivPlatformES5506::tick(bool sysTick) {
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[i].transWave.slice=transWaveInd.slice;
chan[i].transWave.slicePos(transWaveInd.slice);
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
@ -790,7 +787,7 @@ void DivPlatformES5506::tick(bool sysTick) {
double loopEnd=(double)s->loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[i].transWave.slicePos(chan[i].transWave.slice);
chan[i].transWave.slicePos(double(chan[i].transWave.slice)/4095.0);
loopStart=chan[i].transWave.sliceStart;
loopEnd=chan[i].transWave.sliceEnd;
}
@ -798,8 +795,8 @@ void DivPlatformES5506::tick(bool sysTick) {
const unsigned int nextLoopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
const unsigned int nextLoopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
if ((chan[i].pcm.loopStart!=nextLoopStart) || (chan[i].pcm.loopEnd!=nextLoopEnd)) {
chan[i].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
chan[i].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
chan[i].pcm.loopStart=nextLoopStart;
chan[i].pcm.loopEnd=nextLoopEnd;
chan[i].pcmChanged.position=1;
}
}
@ -1002,11 +999,11 @@ void DivPlatformES5506::tick(bool sysTick) {
int DivPlatformES5506::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
bool sampleVaild=false;
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (ins->amiga.transWave.ind>=0 && ins->amiga.transWave.ind<ins->amiga.transWaveMap.size())) ||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value];
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind];
int sample=ins->amiga.initSample;
@ -1043,7 +1040,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (ins->amiga.transWave.enable) {
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
loopMode=transWaveInd.loopMode;
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
} else if ((chan[c.chan].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
}
// get loop position
@ -1051,8 +1048,8 @@ int DivPlatformES5506::dispatch(DivCommand c) {
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[c.chan].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[c.chan].transWave.slice=transWaveInd.slice;
chan[c.chan].transWave.slicePos(transWaveInd.slice);
chan[c.chan].transWave.slice=ins->amiga.transWave.slice;
chan[c.chan].transWave.slicePos(double(ins->amiga.transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
@ -1173,6 +1170,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
case DIV_CMD_WAVE:
if (!chan[c.chan].useWave) {
if (chan[c.chan].active) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (c.value>=0 && c.value<ins->amiga.transWaveMap.size())) ||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<parent->song.sampleLen))) {
@ -1246,7 +1244,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (chan[c.chan].active) {
if (chan[c.chan].pcm.pause!=(bool)(c.value&1)) {
chan[c.chan].pcm.pause=c.value&1;
pageWriteMask(0x00|i,0x5f,0x00,chan[c.chan].pcm.pause?0x0002:0x0000,0x0002);
pageWriteMask(0x00|c.chan,0x5f,0x00,chan[c.chan].pcm.pause?0x0002:0x0000,0x0002);
}
}
break;
@ -1325,7 +1323,7 @@ void DivPlatformES5506::forceIns() {
chan[i].volChanged.changed=0xff;
chan[i].filterChanged.changed=0xff;
chan[i].envChanged.changed=0xff;
chan[i].sample=-1;
chan[i].pcmChanged.changed=0xff;
}
}

View file

@ -64,7 +64,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
} pcm;
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
unsigned int volMacroMax, panMacroMax;
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop, isTranswave, transwaveIRQ;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop, isTranswave, transwaveIRQ;
unsigned int cr;
struct NoteChanged { // Note changed flags
@ -181,7 +181,6 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
active(false),
insChanged(true),
freqChanged(false),
pcmChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),

View file

@ -185,7 +185,7 @@ void DivPlatformFDS::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -498,6 +498,10 @@ void DivPlatformFDS::notifyInsDeletion(void* ins) {
}
}
float DivPlatformFDS::getPostAmp() {
return useNP?2.0f:1.0f;
}
void DivPlatformFDS::poke(unsigned int addr, unsigned short val) {
rWrite(addr,val);
}

View file

@ -99,6 +99,7 @@ class DivPlatformFDS: public DivDispatch {
void setNSFPlay(bool use);
void setFlags(unsigned int flags);
void notifyInsDeletion(void* ins);
float getPostAmp();
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();

View file

@ -32,4 +32,65 @@
#define ADDR_FB_ALG 0xb0
#define ADDR_LRAF 0xb4
#define PLEASE_HELP_ME(_targetChan) \
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); \
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false); \
int destFreq=NOTE_FNUM_BLOCK(c.value2,11); \
int newFreq; \
bool return2=false; \
if (_targetChan.portaPause) { \
if (parent->song.oldOctaveBoundary) { \
if ((_targetChan.portaPauseFreq&0xf800)>(_targetChan.baseFreq&0xf800)) { \
_targetChan.baseFreq=((_targetChan.baseFreq&0x7ff)>>1)|(_targetChan.portaPauseFreq&0xf800); \
} else { \
_targetChan.baseFreq=((_targetChan.baseFreq&0x7ff)<<1)|(_targetChan.portaPauseFreq&0xf800); \
} \
c.value*=2; \
} else { \
_targetChan.baseFreq=_targetChan.portaPauseFreq; \
} \
} \
if (destFreq>_targetChan.baseFreq) { \
newFreq=_targetChan.baseFreq+c.value; \
if (newFreq>=destFreq) { \
newFreq=destFreq; \
return2=true; \
} \
} else { \
newFreq=_targetChan.baseFreq-c.value; \
if (newFreq<=destFreq) { \
newFreq=destFreq; \
return2=true; \
} \
} \
/* check for octave boundary */ \
/* what the heck! */ \
if (!_targetChan.portaPause) { \
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { \
if (parent->song.fbPortaPause) { \
_targetChan.portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); \
_targetChan.portaPause=true; \
break; \
} else { \
newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); \
} \
} \
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) { \
if (parent->song.fbPortaPause) { \
_targetChan.portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); \
_targetChan.portaPause=true; \
break; \
} else { \
newFreq=((newFreq&0x7ff)<<1)|((newFreq-0x800)&0xf800); \
} \
} \
} \
_targetChan.portaPause=false; \
_targetChan.freqChanged=true; \
_targetChan.baseFreq=newFreq; \
if (return2) { \
_targetChan.inPorta=false; \
return 2; \
}
#endif

View file

@ -204,7 +204,7 @@ void DivPlatformGB::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -339,7 +339,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -347,7 +347,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -758,48 +758,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
}
chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true;
chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
PLEASE_HELP_ME(chan[c.chan]);
break;
}
case DIV_CMD_SAMPLE_MODE: {

View file

@ -150,52 +150,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
PLEASE_HELP_ME(opChan[ch]);
break;
}
case DIV_CMD_SAMPLE_MODE: {

View file

@ -27,7 +27,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
int vol;
unsigned char pan;
OpChannel():
@ -45,6 +45,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
keyOn(false),
keyOff(false),
portaPause(false),
inPorta(false),
vol(0),
pan(3) {}
};

View file

@ -142,23 +142,52 @@ const char* DivPlatformLynx::getEffectName(unsigned char effect) {
}
void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) {
mikey->sampleAudio( bufL + start, bufR + start, len, oscBuf );
for (size_t h=start; h<start+len; h++) {
for (int i=0; i<4; i++) {
if (chan[i].pcm && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
chan[i].sampleAccum-=chan[i].sampleFreq;
if (chan[i].sampleAccum<0) {
chan[i].sampleAccum+=rate;
DivSample* s=parent->getSample(chan[i].sample);
if (s!=NULL) {
if (isMuted[i]) {
WRITE_VOLUME(i,0);
chan[i].samplePos++;
} else {
WRITE_VOLUME(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
}
if (chan[i].samplePos>=(int)s->samples) {
chan[i].sample=-1;
}
}
}
}
}
mikey->sampleAudio( bufL + h, bufR + h, 1, oscBuf );
}
}
void DivPlatformLynx::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7;
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
if (chan[i].pcm) {
chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6;
} else {
chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7;
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].std.arp.val,false);
chan[i].actualNote=chan[i].std.arp.val;
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].note+chan[i].std.arp.val,false);
chan[i].actualNote=chan[i].note+chan[i].std.arp.val;
}
chan[i].freqChanged=true;
@ -166,6 +195,7 @@ void DivPlatformLynx::tick(bool sysTick) {
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
if (chan[i].pcm) chan[i].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,chan[i].note,false);
chan[i].actualNote=chan[i].note;
chan[i].freqChanged=true;
}
@ -188,40 +218,75 @@ void DivPlatformLynx::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
WRITE_LFSR(i, 0);
WRITE_OTHER(i, 0);
}
}
if (chan[i].freqChanged) {
if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff));
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
chan[i].lfsr=-1;
if (chan[i].pcm) {
double off=1.0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=(double)s->centerRate/8363.0;
}
}
chan[i].sampleFreq=off*parent->calcFreq(chan[i].sampleBaseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
WRITE_FEEDBACK(i,0);
WRITE_LFSR(i,0);
WRITE_OTHER(i,0);
WRITE_CONTROL(i,0x18);
WRITE_BACKUP(i,2);
} else {
if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff));
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
chan[i].lfsr=-1;
}
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
WRITE_FEEDBACK(i, chan[i].duty.feedback);
}
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
WRITE_BACKUP( i, chan[i].fd.backup );
}
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
WRITE_FEEDBACK(i, chan[i].duty.feedback);
}
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
WRITE_BACKUP( i, chan[i].fd.backup );
chan[i].freqChanged=false;
} else if (chan[i].std.duty.had) {
chan[i].duty = chan[i].std.duty.val;
WRITE_FEEDBACK(i, chan[i].duty.feedback);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
if (!chan[i].pcm) {
WRITE_FEEDBACK(i, chan[i].duty.feedback);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
}
}
}
}
int DivPlatformLynx::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY);
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
if (chan[c.chan].pcm) {
chan[c.chan].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,c.value,false);
chan[c.chan].sample=ins->amiga.getSample(c.value);
chan[c.chan].sampleAccum=0;
chan[c.chan].samplePos=0;
}
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
chan[c.chan].actualNote=c.value;
@ -232,10 +297,14 @@ int DivPlatformLynx::dispatch(DivCommand c) {
WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
WRITE_VOLUME(c.chan, 0);
chan[c.chan].macroInit(NULL);
if (chan[c.chan].pcm) {
chan[c.chan].pcm=false;
}
break;
case DIV_CMD_LYNX_LFSR_LOAD:
chan[c.chan].freqChanged=true;
@ -255,7 +324,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (chan[c.chan].active) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
if (chan[c.chan].active && !chan[c.chan].pcm) WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
}
break;
case DIV_CMD_PANNING:
@ -289,18 +358,26 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
}
chan[c.chan].freqChanged=true;
if (chan[c.chan].pcm && parent->song.linearPitch==2) {
chan[c.chan].sampleBaseFreq=chan[c.chan].baseFreq;
}
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
case DIV_CMD_LEGATO: {
int whatAMess=c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0));
chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess);
if (chan[c.chan].pcm) {
chan[c.chan].sampleBaseFreq=parent->calcBaseFreq(1.0,1.0,whatAMess,false);
}
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
chan[c.chan].actualNote=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_MIKEY));

View file

@ -44,9 +44,9 @@ class DivPlatformLynx: public DivDispatch {
DivMacroInt std;
MikeyFreqDiv fd;
MikeyDuty duty;
int baseFreq, pitch, pitch2, note, actualNote, lfsr, ins;
int baseFreq, pitch, pitch2, note, actualNote, lfsr, ins, sample, samplePos, sampleAccum, sampleBaseFreq, sampleFreq;
unsigned char pan;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm;
signed char vol, outVol;
void macroInit(DivInstrument* which) {
std.init(which);
@ -63,6 +63,11 @@ class DivPlatformLynx: public DivDispatch {
actualNote(0),
lfsr(-1),
ins(-1),
sample(-1),
samplePos(0),
sampleAccum(0),
sampleBaseFreq(0),
sampleFreq(0),
pan(0xff),
active(false),
insChanged(true),
@ -70,6 +75,7 @@ class DivPlatformLynx: public DivDispatch {
keyOn(false),
keyOff(false),
inPorta(false),
pcm(false),
vol(127),
outVol(127) {}
};

View file

@ -134,7 +134,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -0,0 +1,349 @@
/**
* 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 "msm6258.h"
#include "../engine.h"
#include "../../ta-log.h"
#include "sound/oki/okim6258.h"
#include <string.h>
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6258::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6258::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
short* outs[2]={
&bufL[h],
NULL
};
if (!writes.empty()) {
QueuedWrite& w=writes.front();
switch (w.addr) {
case 0:
msm->ctrl_w(w.val);
break;
}
writes.pop();
}
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
if (msm->data_w(nextData)) {
samplePos++;
if (samplePos>=(int)s->lengthVOX) {
sample=-1;
samplePos=0;
msm->ctrl_w(1);
}
}
}
msm->sound_stream_update(outs,1);
if (isMuted[0]) {
bufL[h]=0;
}
/*if (++updateOsc>=22) {
updateOsc=0;
// TODO: per-channel osc
for (int i=0; i<1; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
}
}*/
}
}
void DivPlatformMSM6258::tick(bool sysTick) {
// nothing
}
int DivPlatformMSM6258::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
} else {
chan[c.chan].furnacePCM=false;
}
if (skipRegisterWrites) break;
if (chan[c.chan].furnacePCM) {
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
sample=ins->amiga.getSample(c.value);
if (sample>=0 && sample<parent->song.sampleLen) {
//DivSample* s=parent->getSample(chan[c.chan].sample);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
rWrite(0,1);
rWrite(0,2);
} else {
break;
}
} else {
chan[c.chan].sample=-1;
chan[c.chan].macroInit(NULL);
chan[c.chan].outVol=chan[c.chan].vol;
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
break;
}
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
sample=12*sampleBank+c.value%12;
samplePos=0;
msm->ctrl_w(1);
msm->ctrl_w(2);
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite(0,1); // turn off
sample=-1;
samplePos=0;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite(0,1); // turn off
sample=-1;
samplePos=0;
chan[c.chan].std.release();
break;
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_VOLUME: {
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: {
return chan[c.chan].vol;
break;
}
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].insChanged=true;
}
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PITCH: {
break;
}
case DIV_CMD_NOTE_PORTA: {
return 2;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_LEGATO: {
break;
}
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;
case DIV_CMD_GET_VOLMAX:
return 8;
break;
case DIV_CMD_PRE_PORTA:
break;
case DIV_CMD_PRE_NOTE:
break;
default:
//printf("WARNING: unimplemented command %d\n",c.cmd);
break;
}
return 1;
}
void DivPlatformMSM6258::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformMSM6258::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<1; i++) {
chan[i].insChanged=true;
}
}
void* DivPlatformMSM6258::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformMSM6258::getRegisterPool() {
return NULL;
}
int DivPlatformMSM6258::getRegisterPoolSize() {
return 0;
}
void DivPlatformMSM6258::poke(unsigned int addr, unsigned short val) {
//immWrite(addr,val);
}
void DivPlatformMSM6258::poke(std::vector<DivRegWrite>& wlist) {
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
}
void DivPlatformMSM6258::reset() {
while (!writes.empty()) writes.pop();
msm->device_reset();
if (dumpWrites) {
addWrite(0xffffffff,0);
}
for (int i=0; i<1; i++) {
chan[i]=DivPlatformMSM6258::Channel();
chan[i].std.setEngine(parent);
}
for (int i=0; i<1; i++) {
chan[i].vol=8;
chan[i].outVol=8;
}
sampleBank=0;
sample=-1;
samplePos=0;
delay=0;
}
bool DivPlatformMSM6258::keyOffAffectsArp(int ch) {
return false;
}
void DivPlatformMSM6258::notifyInsChange(int ins) {
for (int i=0; i<1; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformMSM6258::notifyInsDeletion(void* ins) {
}
const void* DivPlatformMSM6258::getSampleMem(int index) {
return index == 0 ? adpcmMem : NULL;
}
size_t DivPlatformMSM6258::getSampleMemCapacity(int index) {
return index == 0 ? 262144 : 0;
}
size_t DivPlatformMSM6258::getSampleMemUsage(int index) {
return index == 0 ? adpcmMemLen : 0;
}
void DivPlatformMSM6258::renderSamples() {
memset(adpcmMem,0,getSampleMemCapacity(0));
// sample data
size_t memPos=0;
int sampleCount=parent->song.sampleLen;
if (sampleCount>128) sampleCount=128;
for (int i=0; i<sampleCount; i++) {
DivSample* s=parent->song.sample[i];
int paddedLen=s->lengthVOX;
if (memPos>=getSampleMemCapacity(0)) {
logW("out of ADPCM memory for sample %d!",i);
break;
}
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos);
logW("out of ADPCM memory for sample %d!",i);
} else {
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
}
s->offVOX=memPos;
memPos+=paddedLen;
}
adpcmMemLen=memPos+256;
}
void DivPlatformMSM6258::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=4096000;
} else {
chipClock=4000000;
}
rate=chipClock/256;
for (int i=0; i<1; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/256;
}
}
int DivPlatformMSM6258::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
adpcmMem=new unsigned char[getSampleMemCapacity(0)];
adpcmMemLen=0;
dumpWrites=false;
skipRegisterWrites=false;
updateOsc=0;
for (int i=0; i<1; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
msm=new okim6258_device(4000000);
msm->device_start();
setFlags(flags);
reset();
return 4;
}
void DivPlatformMSM6258::quit() {
for (int i=0; i<1; i++) {
delete oscBuf[i];
}
delete msm;
delete[] adpcmMem;
}
DivPlatformMSM6258::~DivPlatformMSM6258() {
}

View file

@ -0,0 +1,129 @@
/**
* 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 _MSM6258_H
#define _MSM6258_H
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include "sound/oki/okim6258.h"
class DivPlatformMSM6258: public DivDispatch {
protected:
const unsigned short chanOffs[6]={
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
};
struct Channel {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
int vol, outVol;
int sample;
unsigned char pan;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
portaPauseFreq(0),
note(0),
ins(-1),
psgMode(1),
autoEnvNum(0),
autoEnvDen(0),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
portaPause(false),
inPorta(false),
furnacePCM(false),
hardReset(false),
vol(0),
outVol(15),
sample(-1),
pan(3) {}
};
Channel chan[1];
DivDispatchOscBuffer* oscBuf[1];
bool isMuted[1];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
okim6258_device* msm;
unsigned char regPool[512];
unsigned char lastBusy;
unsigned char* adpcmMem;
size_t adpcmMemLen;
unsigned char sampleBank;
int delay, updateOsc, sample, samplePos;
bool extMode;
short oldWrites[512];
short pendingWrites[512];
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);
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 keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
void setFlags(unsigned int flags);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);
void renderSamples();
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformMSM6258();
};
#endif

View file

@ -0,0 +1,345 @@
/**
* 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 "msm6295.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <string.h>
#include <math.h>
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
return NULL;
}
u8 DivMSM6295Interface::read_byte(u32 address) {
return adpcmMem[address&0xffff];
}
void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
if (delay<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
msm->command_w(w.val);
writes.pop();
delay=32;
}
} else {
delay--;
}
msm->tick();
bufL[h]=msm->out()<<4;
if (++updateOsc>=22) {
updateOsc=0;
// TODO: per-channel osc
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
}
}
}
}
void DivPlatformMSM6295::tick(bool sysTick) {
// nothing
}
int DivPlatformMSM6295::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
} else {
chan[c.chan].furnacePCM=false;
}
if (skipRegisterWrites) break;
if (chan[c.chan].furnacePCM) {
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
//DivSample* s=parent->getSample(chan[c.chan].sample);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} else {
break;
}
} else {
chan[c.chan].sample=-1;
chan[c.chan].macroInit(NULL);
chan[c.chan].outVol=chan[c.chan].vol;
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
break;
}
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
chan[c.chan].sample=12*sampleBank+c.value%12;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
chan[c.chan].std.release();
break;
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_VOLUME: {
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: {
return chan[c.chan].vol;
break;
}
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].insChanged=true;
}
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PITCH: {
break;
}
case DIV_CMD_NOTE_PORTA: {
return 2;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
}
iface.sampleBank=sampleBank;
break;
case DIV_CMD_LEGATO: {
break;
}
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;
case DIV_CMD_GET_VOLMAX:
return 8;
break;
case DIV_CMD_PRE_PORTA:
break;
case DIV_CMD_PRE_NOTE:
break;
default:
//printf("WARNING: unimplemented command %d\n",c.cmd);
break;
}
return 1;
}
void DivPlatformMSM6295::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
msm->m_voice[ch].m_muted=mute;
}
void DivPlatformMSM6295::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
}
}
void* DivPlatformMSM6295::getChanState(int ch) {
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformMSM6295::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformMSM6295::getRegisterPool() {
return NULL;
}
int DivPlatformMSM6295::getRegisterPoolSize() {
return 0;
}
void DivPlatformMSM6295::poke(unsigned int addr, unsigned short val) {
//immWrite(addr,val);
}
void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
}
void DivPlatformMSM6295::reset() {
while (!writes.empty()) writes.pop();
msm->reset();
if (dumpWrites) {
addWrite(0xffffffff,0);
}
for (int i=0; i<4; i++) {
chan[i]=DivPlatformMSM6295::Channel();
chan[i].std.setEngine(parent);
}
for (int i=0; i<4; i++) {
chan[i].vol=8;
chan[i].outVol=8;
}
sampleBank=0;
delay=0;
}
bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
return false;
}
void DivPlatformMSM6295::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformMSM6295::notifyInsDeletion(void* ins) {
}
const void* DivPlatformMSM6295::getSampleMem(int index) {
return index == 0 ? adpcmMem : NULL;
}
size_t DivPlatformMSM6295::getSampleMemCapacity(int index) {
return index == 0 ? 262144 : 0;
}
size_t DivPlatformMSM6295::getSampleMemUsage(int index) {
return index == 0 ? adpcmMemLen : 0;
}
void DivPlatformMSM6295::renderSamples() {
memset(adpcmMem,0,getSampleMemCapacity(0));
// sample data
size_t memPos=128*8;
int sampleCount=parent->song.sampleLen;
if (sampleCount>128) sampleCount=128;
for (int i=0; i<sampleCount; i++) {
DivSample* s=parent->song.sample[i];
int paddedLen=s->lengthVOX;
if (memPos>=getSampleMemCapacity(0)) {
logW("out of ADPCM memory for sample %d!",i);
break;
}
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
memcpy(adpcmMem+memPos,s->dataVOX,getSampleMemCapacity(0)-memPos);
logW("out of ADPCM memory for sample %d!",i);
} else {
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
}
s->offVOX=memPos;
memPos+=paddedLen;
}
adpcmMemLen=memPos+256;
// phrase book
for (int i=0; i<sampleCount; i++) {
DivSample* s=parent->song.sample[i];
int endPos=s->offVOX+s->lengthVOX;
adpcmMem[i*8]=(s->offVOX>>16)&0xff;
adpcmMem[1+i*8]=(s->offVOX>>8)&0xff;
adpcmMem[2+i*8]=(s->offVOX)&0xff;
adpcmMem[3+i*8]=(endPos>>16)&0xff;
adpcmMem[4+i*8]=(endPos>>8)&0xff;
adpcmMem[5+i*8]=(endPos)&0xff;
}
}
void DivPlatformMSM6295::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=8448000;
} else {
chipClock=8000000;
}
rate=chipClock/((flags&2)?6:24);
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/22;
}
}
int DivPlatformMSM6295::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
adpcmMem=new unsigned char[getSampleMemCapacity(0)];
adpcmMemLen=0;
iface.adpcmMem=adpcmMem;
iface.sampleBank=0;
dumpWrites=false;
skipRegisterWrites=false;
updateOsc=0;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
msm=new msm6295_core(iface);
setFlags(flags);
reset();
return 4;
}
void DivPlatformMSM6295::quit() {
for (int i=0; i<4; i++) {
delete oscBuf[i];
}
delete msm;
delete[] adpcmMem;
}
DivPlatformMSM6295::~DivPlatformMSM6295() {
}

View file

@ -0,0 +1,138 @@
/**
* 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 _MSM6295_H
#define _MSM6295_H
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include "sound/oki/msm6295.hpp"
class DivMSM6295Interface: public vgsound_emu_mem_intf {
public:
unsigned char* adpcmMem;
int sampleBank;
u8 read_byte(u32 address);
DivMSM6295Interface(): adpcmMem(NULL), sampleBank(0) {}
};
class DivPlatformMSM6295: public DivDispatch {
protected:
const unsigned short chanOffs[6]={
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
};
struct Channel {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
int vol, outVol;
int sample;
unsigned char pan;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
portaPauseFreq(0),
note(0),
ins(-1),
psgMode(1),
autoEnvNum(0),
autoEnvDen(0),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
portaPause(false),
inPorta(false),
furnacePCM(false),
hardReset(false),
vol(0),
outVol(15),
sample(-1),
pan(3) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
msm6295_core* msm;
unsigned char regPool[512];
unsigned char lastBusy;
unsigned char* adpcmMem;
size_t adpcmMemLen;
DivMSM6295Interface iface;
unsigned char sampleBank;
int delay, updateOsc;
bool extMode;
short oldWrites[512];
short pendingWrites[512];
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);
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 keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
void setFlags(unsigned int flags);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);
void renderSamples();
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformMSM6295();
};
#endif

View file

@ -268,7 +268,7 @@ void DivPlatformN163::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -244,7 +244,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -284,7 +284,7 @@ void DivPlatformNES::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -352,7 +352,7 @@ void DivPlatformOPL::tick(bool sysTick) {
if (isMuted[i]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -384,7 +384,7 @@ void DivPlatformOPL::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -392,7 +392,7 @@ void DivPlatformOPL::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -479,7 +479,7 @@ void DivPlatformOPL::tick(bool sysTick) {
if (isMuted[i]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -688,7 +688,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>=melodicChans) {
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -734,12 +734,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(9,(s->offB>>5)&0xff);
immWrite(10,(s->offB>>13)&0xff);
immWrite(8,0);
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>5)&0xff);
immWrite(12,(end>>13)&0xff);
immWrite(8,1);
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
@ -769,12 +769,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
}
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
immWrite(8,0);
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(8,1);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
int freq=(65536.0*(double)s->rate)/(double)rate;
immWrite(16,freq&0xff);
@ -808,7 +808,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -911,7 +911,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -1060,7 +1060,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -1290,7 +1290,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>=melodicChans) {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -1307,7 +1307,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>=melodicChans) {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -1367,9 +1367,10 @@ void DivPlatformOPL::forceIns() {
totalChans=properDrums?11:9;
}
for (int i=0; i<totalChans; i++) {
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
//int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
chan[i].insChanged=true;
chan[i].freqChanged=true;
/*
chan[i].fourOp=(ops==4);
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
@ -1380,7 +1381,7 @@ void DivPlatformOPL::forceIns() {
if (isMuted[i]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>=melodicChans) {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
@ -1406,6 +1407,10 @@ void DivPlatformOPL::forceIns() {
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
}
}
*/
}
for (int i=0; i<512; i++) {
oldWrites[i]=-1;
}
immWrite(0xbd,(dam<<7)|(dvb<<6)|(properDrums<<5)|drumState);
update4OpMask=true;
@ -1698,7 +1703,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
adpcmBMemLen=0;
iface.adpcmBMem=adpcmBMem;
iface.sampleBank=0;
adpcmB=new ymfm::adpcm_b_engine(iface,5);
adpcmB=new ymfm::adpcm_b_engine(iface,2);
}
reset();

View file

@ -181,7 +181,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -189,7 +189,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}

View file

@ -214,7 +214,7 @@ void DivPlatformPCE::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -216,7 +216,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -331,7 +331,7 @@ void DivPlatformQSound::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -104,7 +104,7 @@ void DivPlatformRF5C68::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -189,7 +189,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -146,7 +146,7 @@ void DivPlatformSCC::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -161,9 +161,14 @@ void DivPlatformSCC::tick(bool sysTick) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>4095) chan[i].freq=4095;
rWrite(regBase+0+i*2,chan[i].freq&0xff);
rWrite(regBase+1+i*2,chan[i].freq>>8);
if (!chan[i].freqInit || regPool[regBase+0+i*2]!=(chan[i].freq&0xff)) {
rWrite(regBase+0+i*2,chan[i].freq&0xff);
}
if (!chan[i].freqInit || regPool[regBase+1+i*2]!=(chan[i].freq>>8)) {
rWrite(regBase+1+i*2,chan[i].freq>>8);
}
chan[i].freqChanged=false;
chan[i].freqInit=!skipRegisterWrites;
}
}
}
@ -286,6 +291,7 @@ void DivPlatformSCC::forceIns() {
for (int i=0; i<5; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].freqInit=false;
if (isPlus || i<3) {
updateWave(i);
}

View file

@ -29,7 +29,7 @@
class DivPlatformSCC: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, pitch2, note, ins;
bool active, insChanged, freqChanged, inPorta;
bool active, insChanged, freqChanged, freqInit, inPorta;
signed char vol, outVol, wave;
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
DivMacroInt std;
@ -48,6 +48,7 @@ class DivPlatformSCC: public DivDispatch {
active(false),
insChanged(true),
freqChanged(false),
freqInit(false),
inPorta(false),
vol(15),
outVol(15),

View file

@ -122,7 +122,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -21,7 +21,7 @@
#include "../engine.h"
#include <math.h>
#define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}}
#define rWrite(v) {if (!skipRegisterWrites) {writes.push(v); if (dumpWrites) {addWrite(0x200,v);}}}
const char* regCheatSheetSN[]={
"DATA", "0",
@ -41,7 +41,47 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) {
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
if (!writes.empty()) {
unsigned char w=writes.front();
YMPSG_Write(&sn_nuked,w);
writes.pop();
}
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
bufL[h]=YMPSG_GetOutput(&sn_nuked)*8192.0;
/*
for (int i=0; i<4; i++) {
if (isMuted[i]) {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i);
}
}*/
}
}
void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t len) {
while (!writes.empty()) {
unsigned char w=writes.front();
sn->write(w);
writes.pop();
}
for (size_t h=start; h<start+len; h++) {
sn->sound_stream_update(bufL+h,1);
for (int i=0; i<4; i++) {
@ -54,6 +94,14 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len)
}
}
void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (nuked) {
acquire_nuked(bufL,bufR,start,len);
} else {
acquire_mame(bufL,bufR,start,len);
}
}
int DivPlatformSMS::acquireOne() {
short v;
sn->sound_stream_update(&v,1);
@ -112,7 +160,7 @@ void DivPlatformSMS::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -301,6 +349,7 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) {
}
void DivPlatformSMS::reset() {
while (!writes.empty()) writes.pop();
for (int i=0; i<4; i++) {
chan[i]=DivPlatformSMS::Channel();
chan[i].std.setEngine(parent);
@ -309,6 +358,7 @@ void DivPlatformSMS::reset() {
addWrite(0xffffffff,0);
}
sn->device_start();
YMPSG_Init(&sn_nuked,isRealSN);
snNoiseMode=3;
rWrite(0xe7);
updateSNMode=false;
@ -352,6 +402,7 @@ void DivPlatformSMS::setFlags(unsigned int flags) {
chipClock=COLOR_NTSC;
}
resetPhase=!(flags&16);
if (sn!=NULL) delete sn;
switch ((flags>>2)&3) {
case 1: // TI
@ -377,6 +428,10 @@ void DivPlatformSMS::setFlags(unsigned int flags) {
}
}
void DivPlatformSMS::setNuked(bool value) {
nuked=value;
}
int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
dumpWrites=false;

View file

@ -23,6 +23,10 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include "sound/sn76496.h"
extern "C" {
#include "../../../extern/Nuked-PSG/ympsg.h"
}
#include <queue>
class DivPlatformSMS: public DivDispatch {
struct Channel {
@ -59,8 +63,14 @@ class DivPlatformSMS: public DivDispatch {
bool updateSNMode;
bool resetPhase;
bool isRealSN;
bool nuked;
sn76496_base_device* sn;
ympsg_t sn_nuked;
std::queue<unsigned char> writes;
friend void putDispatchChan(void*,int,int);
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
void acquire_mame(short* bufL, short* bufR, size_t start, size_t len);
public:
int acquireOne();
void acquire(short* bufL, short* bufR, size_t start, size_t len);
@ -80,6 +90,7 @@ class DivPlatformSMS: public DivDispatch {
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setNuked(bool value);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformSMS();

View file

@ -67,6 +67,7 @@
*/
#include "n163.hpp"
#include <string.h>
void n163_core::tick()
{
@ -126,12 +127,12 @@ void n163_core::reset()
// reset this chip
m_disable = false;
m_multiplex = true;
std::fill(std::begin(m_ram), std::end(m_ram), 0);
memset(m_ram,0,sizeof(m_ram));
m_voice_cycle = 0x78;
m_addr_latch.reset();
m_out = 0;
m_acc = 0;
std::fill(std::begin(m_ch_out), std::end(m_ch_out), 0);
memset(m_ch_out,0,sizeof(m_ch_out));
}
// accessor

View file

@ -243,7 +243,7 @@ unsigned int NES_FDS::Render (int b[2])
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };
int v = fout * MASTER[master_vol] >> 8;
int v = fout * MASTER[master_vol] >> 1;
// lowpass RC filter
int rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS;
@ -252,8 +252,8 @@ unsigned int NES_FDS::Render (int b[2])
// output mix
int m = mask ? 0 : v;
b[0] = (m * sm[0]) >> 7;
b[1] = (m * sm[1]) >> 7;
b[0] = (m * sm[0]) >> 14;
b[1] = (m * sm[1]) >> 14;
return 2;
}

View file

@ -0,0 +1,214 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
Copyright holder(s): cam900
OKI MSM6295 emulation core
It is 4 channel ADPCM playback chip from OKI semiconductor.
It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance.
The chip itself is pretty barebone: there is no "register" in chip.
It can't control volume and pitch in currently playing channels, only stopable them.
And volume is must be determined at playback start command.
Command format:
Playback command (2 byte):
Byte Bit Description
76543210
0 1xxxxxxx Phrase select (Header stored in ROM)
1 x000---- Play channel 4
0x00---- Play channel 3
00x0---- Play channel 2
000x---- Play channel 1
----xxxx Volume
----0000 0.0dB
----0001 -3.2dB
----0010 -6.0dB
----0011 -9.2dB
----0100 -12.0dB
----0101 -14.5dB
----0110 -18.0dB
----0111 -20.5dB
----1000 -24.0dB
Suspend command (1 byte):
Byte Bit Description
76543210
0 0x------ Suspend channel 4
0-x----- Suspend channel 3
0--x---- Suspend channel 2
0---x--- Suspend channel 1
Frequency calculation:
if (SS) then
Frequency = Input clock / 165
else then
Frequency = Input clock / 132
*/
#include "msm6295.hpp"
#define CORE_DIVIDER 3
#define CHANNEL_DELAY (15/CORE_DIVIDER)
#define MASTER_DELAY (33/CORE_DIVIDER)
void msm6295_core::tick()
{
// command handler
if (m_command_pending)
{
if (bitfield(m_command, 7)) // play voice
{
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
{
m_clock = 0;
if (bitfield(m_next_command, 4, 4) != 0)
{
for (int i = 0; i < 4; i++)
{
if (bitfield(m_next_command, 4 + i))
{
if (!m_voice[i].m_busy)
{
m_voice[i].m_command = m_command;
m_voice[i].m_volume = (bitfield(m_next_command, 0, 4) < 9) ? m_volume_table[std::min<u8>(8, bitfield(m_next_command, 0, 4))] : 0;
}
break; // voices aren't be playable simultaneously at once
}
}
}
m_command = 0;
m_command_pending = false;
}
}
else if (bitfield(m_next_command, 7)) // select phrase
{
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
{
m_clock = 0;
m_command = m_next_command;
m_command_pending = false;
}
}
else
{
if (bitfield(m_next_command, 3, 4) != 0) // suspend voices
{
for (int i = 0; i < 4; i++)
{
if (bitfield(m_next_command, 3 + i))
{
if (m_voice[i].m_busy)
m_voice[i].m_command = m_next_command;
}
}
m_next_command &= ~0x78;
}
m_command_pending = false;
}
}
m_out = 0;
for (int i = 0; i < 4; i++)
{
m_voice[i].tick();
if (!m_voice[i].m_muted) m_out += m_voice[i].m_out;
}
}
void msm6295_core::reset()
{
for (auto & elem : m_voice)
elem.reset();
m_command = 0;
m_next_command = 0;
m_command_pending = false;
m_clock = 0;
m_out = 0;
}
void msm6295_core::voice_t::tick()
{
if (!m_busy)
{
if (bitfield(m_command, 7))
{
// get phrase header (stored in data memory)
const u32 phrase = bitfield(m_command, 0, 7) << 3;
// Start address
m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16)
| (m_host.m_intf.read_byte(phrase | 1) << 8)
| (m_host.m_intf.read_byte(phrase | 2) << 0);
// End address
m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16)
| (m_host.m_intf.read_byte(phrase | 4) << 8)
| (m_host.m_intf.read_byte(phrase | 5) << 0);
m_nibble = 4; // MSB first, LSB second
m_command = 0;
m_busy = true;
vox_decoder_t::reset();
}
m_out = 0;
}
else
{
// playback
if ((++m_clock) >= ((MASTER_DELAY * (m_host.m_ss ? 5 : 4))))
{
m_clock = 0;
bool is_end = (m_command != 0);
m_curr.decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4));
if (m_nibble <= 0)
{
m_nibble = 4;
if (++m_addr > m_end)
is_end = true;
}
else
m_nibble -= 4;
if (is_end)
{
m_command = 0;
m_busy = false;
}
m_out = (out() * m_volume) >> 7; // scale out to 12 bit output
}
}
}
void msm6295_core::voice_t::reset()
{
vox_decoder_t::reset();
m_clock = 0;
m_busy = false;
m_command = 0;
m_addr = 0;
m_nibble = 0;
m_end = 0;
m_volume = 0;
m_out = 0;
}
// accessors
u8 msm6295_core::busy_r()
{
return (m_voice[0].m_busy ? 0x01 : 0x00)
| (m_voice[1].m_busy ? 0x02 : 0x00)
| (m_voice[2].m_busy ? 0x04 : 0x00)
| (m_voice[3].m_busy ? 0x08 : 0x00);
}
void msm6295_core::command_w(u8 data)
{
if (!m_command_pending)
{
m_next_command = data;
m_command_pending = true;
}
}

View file

@ -0,0 +1,94 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
Copyright holder(s): cam900
OKI MSM6295 emulation core
See msm6295.cpp for more info.
*/
#include "util.hpp"
#include "vox.hpp"
#include <algorithm>
#include <memory>
#ifndef _VGSOUND_EMU_MSM6295_HPP
#define _VGSOUND_EMU_MSM6295_HPP
#pragma once
class msm6295_core : public vox_core
{
friend class vgsound_emu_mem_intf; // common memory interface
public:
// constructor
msm6295_core(vgsound_emu_mem_intf &intf)
: m_voice{{*this,*this},{*this,*this},{*this,*this},{*this,*this}}
, m_intf(intf)
{
}
// accessors, getters, setters
u8 busy_r();
void command_w(u8 data);
void ss_w(bool ss) { m_ss = ss; } // SS pin
// internal state
void reset();
void tick();
s32 out() { return m_out; } // built in 12 bit DAC
private:
// Internal volume table, 9 step
const s32 m_volume_table[9] = {
32/* 0.0dB */,
22/* -3.2dB */,
16/* -6.0dB */,
11/* -9.2dB */,
8/* -12.0dB */,
6/* -14.5dB */,
4/* -18.0dB */,
3/* -20.5dB */,
2/* -24.0dB */ }; // scale out to 5 bit for optimization
public:
// msm6295 voice structs
struct voice_t : vox_decoder_t
{
// constructor
voice_t(vox_core &vox, msm6295_core &host)
: vox_decoder_t(vox)
, m_host(host)
{};
// internal state
virtual void reset() override;
void tick();
// accessors, getters, setters
// registers
msm6295_core &m_host;
u16 m_clock = 0; // clock counter
bool m_busy = false; // busy status
bool m_muted = false; // muted
u8 m_command = 0; // current command
u32 m_addr = 0; // current address
s8 m_nibble = 0; // current nibble
u32 m_end = 0; // end address
s32 m_volume = 0; // volume
s32 m_out = 0; // output
};
voice_t m_voice[4];
private:
vgsound_emu_mem_intf &m_intf; // common memory interface
bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4)
u8 m_command = 0; // Command byte
u8 m_next_command = 0; // Next command
bool m_command_pending = false; // command pending flag
u16 m_clock = 0; // clock counter
s32 m_out = 0; // 12 bit output
};
#endif

View file

@ -11,8 +11,10 @@
**********************************************************************************************/
#include "emu.h"
#include "okim6258.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#define COMMAND_STOP (1 << 0)
#define COMMAND_PLAY (1 << 1)
@ -34,9 +36,6 @@ static int tables_computed = 0;
// device type definition
DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM")
//**************************************************************************
// LIVE DEVICE
@ -46,19 +45,18 @@ DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM")
// okim6258_device - constructor
//-------------------------------------------------
okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, OKIM6258, tag, owner, clock),
device_sound_interface(mconfig, *this),
okim6258_device::okim6258_device(uint32_t clock)
:
m_status(0),
m_start_divider(0),
m_divider(512),
m_adpcm_type(0),
m_data_in(0),
m_nibble_shift(0),
m_stream(nullptr),
m_output_bits(0),
m_signal(0),
m_step(0)
m_step(0),
m_clock(clock)
{
}
@ -114,12 +112,9 @@ void okim6258_device::device_start()
m_divider = dividers[m_start_divider];
m_stream = stream_alloc(0, 1, clock()/m_divider);
m_signal = -2;
m_step = 0;
state_save_register();
m_has_data = false;
}
@ -129,11 +124,10 @@ void okim6258_device::device_start()
void okim6258_device::device_reset()
{
m_stream->update();
m_signal = -2;
m_step = 0;
m_status = 0;
m_has_data = false;
}
@ -141,7 +135,7 @@ void okim6258_device::device_reset()
// sound_stream_update - handle a stream update
//-------------------------------------------------
void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
void okim6258_device::sound_stream_update(short** outputs, int len)
{
auto &buffer = outputs[0];
@ -149,7 +143,7 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
{
int nibble_shift = m_nibble_shift;
for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
for (int sampindex = 0; sampindex < len; sampindex++)
{
/* Compute the new amplitude and update the current step */
int nibble = (m_data_in >> nibble_shift) & 0xf;
@ -159,7 +153,9 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
nibble_shift ^= 4;
buffer.put_int(sampindex, sample, 32768);
if (nibble_shift==0) m_has_data=false;
buffer[sampindex]=sample;
}
/* Update the parameters */
@ -167,29 +163,12 @@ void okim6258_device::sound_stream_update(sound_stream &stream, std::vector<read
}
else
{
buffer.fill(0);
memset(buffer,0,len*sizeof(short));
}
}
/**********************************************************************************************
state save support for MAME
***********************************************************************************************/
void okim6258_device::state_save_register()
{
save_item(NAME(m_status));
save_item(NAME(m_divider));
save_item(NAME(m_data_in));
save_item(NAME(m_nibble_shift));
save_item(NAME(m_signal));
save_item(NAME(m_step));
}
int16_t okim6258_device::clock_adpcm(uint8_t nibble)
{
int32_t max = (1 << (m_output_bits - 1)) - 1;
@ -224,7 +203,6 @@ int16_t okim6258_device::clock_adpcm(uint8_t nibble)
void okim6258_device::set_divider(int val)
{
m_divider = dividers[val];
notify_clock_changed();
}
@ -236,7 +214,6 @@ void okim6258_device::set_divider(int val)
void okim6258_device::device_clock_changed()
{
m_stream->set_sample_rate(clock() / m_divider);
}
@ -248,7 +225,7 @@ void okim6258_device::device_clock_changed()
int okim6258_device::get_vclk()
{
return (clock() / m_divider);
return (m_clock / m_divider);
}
@ -260,8 +237,6 @@ int okim6258_device::get_vclk()
uint8_t okim6258_device::status_r()
{
m_stream->update();
return (m_status & STATUS_PLAYING) ? 0x00 : 0x80;
}
@ -271,13 +246,13 @@ uint8_t okim6258_device::status_r()
okim6258_data_w -- write to the control port of an OKIM6258-compatible chip
***********************************************************************************************/
void okim6258_device::data_w(uint8_t data)
bool okim6258_device::data_w(uint8_t data)
{
/* update the stream */
m_stream->update();
if (m_has_data) return false;
m_data_in = data;
m_nibble_shift = 0;
m_has_data = true;
return true;
}
@ -289,11 +264,10 @@ void okim6258_device::data_w(uint8_t data)
void okim6258_device::ctrl_w(uint8_t data)
{
m_stream->update();
if (data & COMMAND_STOP)
{
m_status &= ~(STATUS_PLAYING | STATUS_RECORDING);
m_has_data = false;
return;
}
@ -316,7 +290,7 @@ void okim6258_device::ctrl_w(uint8_t data)
if (data & COMMAND_RECORD)
{
logerror("M6258: Record enabled\n");
//logerror("M6258: Record enabled\n");
m_status |= STATUS_RECORDING;
}
else

View file

@ -3,8 +3,7 @@
#ifndef MAME_SOUND_OKIM6258_H
#define MAME_SOUND_OKIM6258_H
#pragma once
#include <stdint.h>
//**************************************************************************
// TYPE DEFINITIONS
@ -12,9 +11,7 @@
// ======================> okim6258_device
class okim6258_device : public device_t,
public device_sound_interface
{
class okim6258_device {
public:
static constexpr int FOSC_DIV_BY_1024 = 0;
static constexpr int FOSC_DIV_BY_768 = 1;
@ -26,7 +23,7 @@ public:
static constexpr int OUTPUT_10BITS = 10;
static constexpr int OUTPUT_12BITS = 12;
okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
okim6258_device(uint32_t clock);
// configuration
void set_start_div(int div) { m_start_divider = div; }
@ -34,23 +31,21 @@ public:
void set_outbits(int outbit) { m_output_bits = outbit; }
uint8_t status_r();
void data_w(uint8_t data);
bool data_w(uint8_t data);
void ctrl_w(uint8_t data);
void set_divider(int val);
int get_vclk();
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_clock_changed() override;
// device-levels
void device_start();
void device_reset();
void device_clock_changed();
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// sound stream updates
void sound_stream_update(short** outputs, int len);
private:
void state_save_register();
int16_t clock_adpcm(uint8_t nibble);
uint8_t m_status;
@ -59,16 +54,15 @@ private:
uint32_t m_divider; /* master clock divider */
uint8_t m_adpcm_type; /* 3/4 bit ADPCM select */
uint8_t m_data_in; /* ADPCM data-in register */
bool m_has_data; /* whether we already have data */
uint8_t m_nibble_shift; /* nibble select */
sound_stream *m_stream; /* which stream are we playing on? */
uint8_t m_output_bits; /* D/A precision is 10-bits but 12-bit data can be
output serially to an external DAC */
int32_t m_signal;
int32_t m_step;
unsigned int m_clock;
};
DECLARE_DEVICE_TYPE(OKIM6258, okim6258_device)
#endif // MAME_SOUND_OKIM6258_H

View file

@ -0,0 +1,158 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
Copyright holders: cam900
Various core utilities for vgsound_emu
*/
#include <algorithm>
#include <memory>
#include <math.h>
#ifndef _VGSOUND_EMU_CORE_UTIL_HPP
#define _VGSOUND_EMU_CORE_UTIL_HPP
#pragma once
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef float f32;
typedef double f64;
const f64 PI = 3.1415926535897932384626433832795;
// get bitfield, bitfield(input, position, len)
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
{
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
}
// get sign extended value, sign_ext<type>(input, len)
template<typename T> T sign_ext(T in, u8 len)
{
len = std::max<u8>(0, (8 * sizeof(T)) - len);
return T(T(in) << len) >> len;
}
// convert attenuation decibel value to gain
inline f32 dB_to_gain(f32 attenuation)
{
return powf(10.0f, attenuation / 20.0f);
}
class vgsound_emu_mem_intf
{
public:
virtual u8 read_byte(u32 address) { return 0; }
virtual u16 read_word(u32 address) { return 0; }
virtual u32 read_dword(u32 address) { return 0; }
virtual u64 read_qword(u32 address) { return 0; }
virtual void write_byte(u32 address, u8 data) { }
virtual void write_word(u32 address, u16 data) { }
virtual void write_dword(u32 address, u32 data) { }
virtual void write_qword(u32 address, u64 data) { }
};
template<typename T, T InitWidth, u8 InitEdge = 0>
struct clock_pulse_t
{
void reset(T init = InitWidth)
{
m_edge.reset();
m_width = m_width_latch = m_counter = init;
m_cycle = 0;
}
bool tick(T width = 0)
{
bool carry = ((--m_counter) <= 0);
if (carry)
{
if (!width)
m_width = m_width_latch;
else
m_width = width; // reset width
m_counter = m_width;
m_cycle = 0;
}
else
m_cycle++;
m_edge.tick(carry);
return carry;
}
void set_width(T width) { m_width = width; }
void set_width_latch(T width) { m_width_latch = width; }
// Accessors
bool current_edge() { return m_edge.m_current; }
bool rising_edge() { return m_edge.m_rising; }
bool falling_edge() { return m_edge.m_rising; }
T cycle() { return m_cycle; }
struct edge_t
{
edge_t()
: m_current(InitEdge ^ 1)
, m_previous(InitEdge)
, m_rising(0)
, m_falling(0)
, m_changed(0)
{
set(InitEdge);
}
void tick(bool toggle)
{
u8 current = m_current;
if (toggle)
current ^= 1;
set(current);
}
void set(u8 edge)
{
edge &= 1;
m_rising = m_falling = m_changed = 0;
if (m_current != edge)
{
m_changed = 1;
if (m_current && (!edge))
m_falling = 1;
else if ((!m_current) && edge)
m_rising = 1;
m_current = edge;
}
m_previous = m_current;
}
void reset()
{
m_previous = InitEdge;
m_current = InitEdge ^ 1;
set(InitEdge);
}
u8 m_current : 1; // current edge
u8 m_previous : 1; // previous edge
u8 m_rising : 1; // rising edge
u8 m_falling : 1; // falling edge
u8 m_changed : 1; // changed flag
};
edge_t m_edge;
T m_width = InitWidth; // clock pulse width
T m_width_latch = InitWidth; // clock pulse width latch
T m_counter = InitWidth; // clock counter
T m_cycle = 0; // clock cycle
};
#endif

View file

@ -0,0 +1,115 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
Copyright holder(s): cam900
Dialogic ADPCM core
*/
#include "util.hpp"
#include <algorithm>
#include <memory>
#ifndef _VGSOUND_EMU_CORE_VOX_HPP
#define _VGSOUND_EMU_CORE_VOX_HPP
#pragma once
#define MODIFIED_CLAMP(x,xMin,xMax) (std::min(std::max((x),(xMin)),(xMax)))
class vox_core
{
protected:
struct vox_decoder_t
{
vox_decoder_t(vox_core &vox)
: m_curr(vox)
, m_loop(vox)
{ };
virtual void reset()
{
m_curr.reset();
m_loop.reset();
m_loop_saved = false;
}
void save()
{
if (!m_loop_saved)
{
m_loop.copy(m_curr);
m_loop_saved = true;
}
}
void restore()
{
if (m_loop_saved)
m_curr.copy(m_loop);
}
s32 out() { return m_curr.m_step; }
struct decoder_state_t
{
decoder_state_t(vox_core &vox)
: m_vox(vox)
{ };
void reset()
{
m_index = 0;
m_step = 16;
}
void copy(decoder_state_t src)
{
m_index = src.m_index;
m_step = src.m_step;
}
void decode(u8 nibble)
{
const u8 delta = bitfield(nibble, 0, 3);
s16 ss = m_vox.m_step_table[m_index]; // ss(n)
// d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + (ss(n) / 8)
s16 d = ss >> 3;
if (bitfield(delta, 2))
d += ss;
if (bitfield(delta, 1))
d += (ss >> 1);
if (bitfield(delta, 0))
d += (ss >> 2);
// if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n)
if (bitfield(nibble, 3))
m_step = std::max(m_step - d, -2048);
else
m_step = std::min(m_step + d, 2047);
// adjust step index
m_index = MODIFIED_CLAMP(m_index + m_vox.m_index_table[delta], 0, 48);
}
vox_core &m_vox;
s8 m_index = 0;
s32 m_step = 16;
};
decoder_state_t m_curr;
decoder_state_t m_loop;
bool m_loop_saved = false;
};
s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8};
s32 m_step_table[49] = {
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
};
};
#endif

View file

@ -322,6 +322,7 @@
*/
#include "scc.hpp"
#include <string.h>
// shared SCC features
void scc_core::tick()
@ -372,12 +373,12 @@ void scc_core::reset()
m_test.reset();
m_out = 0;
std::fill(std::begin(m_reg), std::end(m_reg), 0);
memset(m_reg,0,sizeof(m_reg));
}
void scc_core::voice_t::reset()
{
std::fill(std::begin(wave), std::end(wave), 0);
memset(wave,0,sizeof(wave));
enable = false;
pitch = 0;
volume = 0;
@ -454,6 +455,7 @@ void scc_core::freq_vol_enable_w(u8 address, u8 data)
if (m_test.resetpos) // Reset address
m_voice[voice_freq].addr = 0;
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data;
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
break;
case 0x1: // 0x*1 Voice 0 Pitch MSB
case 0x3: // 0x*3 Voice 1 Pitch MSB
@ -463,6 +465,7 @@ void scc_core::freq_vol_enable_w(u8 address, u8 data)
if (m_test.resetpos) // Reset address
m_voice[voice_freq].addr = 0;
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8);
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
break;
case 0xa: // 0x*a Voice 0 Volume
case 0xb: // 0x*b Voice 1 Volume

View file

@ -81,6 +81,7 @@
*/
#include "vrcvi.hpp"
#include <string.h>
void vrcvi_core::tick()
{
@ -119,7 +120,7 @@ void vrcvi_core::reset()
m_timer.reset();
m_control.reset();
m_out = 0;
std::fill(std::begin(m_ch_out),std::end(m_ch_out),0);
memset(m_ch_out,0,sizeof(m_ch_out));
}
bool vrcvi_core::alu_t::tick()

View file

@ -168,7 +168,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -190,7 +190,7 @@ void DivPlatformSwan::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -119,7 +119,7 @@ void DivPlatformTIA::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -317,7 +317,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -325,7 +325,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}

View file

@ -201,7 +201,7 @@ void DivPlatformVERA::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -125,7 +125,7 @@ void DivPlatformVIC20::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -189,7 +189,7 @@ void DivPlatformVRC6::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -390,7 +390,7 @@ void DivPlatformX1_010::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -373,7 +373,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -381,7 +381,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -659,48 +659,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
}
chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true;
chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
PLEASE_HELP_ME(chan[c.chan]);
break;
}
case DIV_CMD_LEGATO: {

View file

@ -143,52 +143,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
PLEASE_HELP_ME(opChan[ch]);
break;
}
case DIV_CMD_LEGATO: {

View file

@ -27,10 +27,12 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
int vol;
unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
// UGLY
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
inPorta(false), vol(0), pan(3) {}
};
OpChannel opChan[4];
bool isOpMuted[4];

View file

@ -536,7 +536,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -544,7 +544,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -980,48 +980,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
}
chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true;
chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
PLEASE_HELP_ME(chan[c.chan]);
break;
}
case DIV_CMD_SAMPLE_BANK:

View file

@ -143,52 +143,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
PLEASE_HELP_ME(opChan[ch]);
break;
}
case DIV_CMD_LEGATO: {

View file

@ -27,10 +27,12 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
int vol;
unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
// UGLY
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
inPorta(false), vol(0), pan(3) {}
};
OpChannel opChan[4];
bool isOpMuted[4];

View file

@ -580,7 +580,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -588,7 +588,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -1027,48 +1027,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
}
chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true;
chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
PLEASE_HELP_ME(chan[c.chan]);
break;
}
case DIV_CMD_SAMPLE_BANK:

View file

@ -559,7 +559,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
@ -567,7 +567,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].keyOn=true;
}
}
@ -1005,48 +1005,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
}
chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true;
chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
PLEASE_HELP_ME(chan[c.chan]);
break;
}
case DIV_CMD_SAMPLE_BANK:

View file

@ -143,52 +143,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
PLEASE_HELP_ME(opChan[ch]);
break;
}
case DIV_CMD_LEGATO: {

View file

@ -27,10 +27,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
int vol;
unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
// UGLY
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
inPorta(false), vol(0), pan(3) {}
};
OpChannel opChan[4];
bool isOpMuted[4];

View file

@ -143,52 +143,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
}
break;
}
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq;
bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
}
}
// what the heck!
if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
}
}
opChan[ch].portaPause=false;
opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2;
PLEASE_HELP_ME(opChan[ch]);
break;
}
case DIV_CMD_LEGATO: {

View file

@ -27,10 +27,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
int vol;
unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
// UGLY
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false),
inPorta(false), vol(0), pan(3) {}
};
OpChannel opChan[4];
bool isOpMuted[4];

View file

@ -115,7 +115,7 @@ void DivPlatformYMZ280B::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -107,7 +107,7 @@ void DivPlatformZXBeeper::tick(bool sysTick) {
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-2048,2048);
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}

View file

@ -23,7 +23,9 @@
#include "engine.h"
#include "../ta-log.h"
#include <math.h>
#ifdef HAVE_SNDFILE
#include <sndfile.h>
#endif
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
@ -566,6 +568,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (divider<10) divider=10;
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider;
clockDrift=0;
subticks=0;
break;
case 0xe0: // arp speed
if (effectVal>0) {
@ -646,6 +649,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (divider<10) divider=10;
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider;
clockDrift=0;
subticks=0;
break;
case 0xf1: // single pitch ramp up
case 0xf2: // single pitch ramp down

View file

@ -21,7 +21,9 @@
#include "../ta-log.h"
#include <math.h>
#include <string.h>
#ifdef HAVE_SNDFILE
#include <sndfile.h>
#endif
#include "filter.h"
extern "C" {
@ -41,6 +43,10 @@ bool DivSample::isLoopable() {
}
bool DivSample::save(const char* path) {
#ifndef HAVE_SNDFILE
logE("Furnace was not compiled with libsndfile!");
return false;
#else
SNDFILE* f;
SF_INFO si;
memset(&si,0,sizeof(SF_INFO));
@ -80,6 +86,7 @@ bool DivSample::save(const char* path) {
sf_close(f);
return true;
#endif
}
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.

View file

@ -394,6 +394,7 @@ struct DivSong {
bool fbPortaPause;
bool snDutyReset;
bool pitchMacroIsLinear;
bool oldOctaveBoundary;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -486,7 +487,8 @@ struct DivSong {
newSegaPCM(true),
fbPortaPause(false),
snDutyReset(false),
pitchMacroIsLinear(true) {
pitchMacroIsLinear(true),
oldOctaveBoundary(false) {
for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64;

View file

@ -176,7 +176,7 @@ String DivEngine::getSongSystemName(bool isMultiSystemAcceptable) {
return "Famicom Disk System";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_N163) {
return "NES + Namco 163";
return "NES + Namco C163";
}
if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_MMC5) {
return "NES + MMC5";
@ -1234,7 +1234,7 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_N163]=new DivSysDef(
"Namco 163", NULL, 0x8c, 0, 8, false, true, 0, false,
"Namco C163", NULL, 0x8c, 0, 8, false, true, 0, false,
"an expansion chip for the Famicom, with full wavetable.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
@ -1655,7 +1655,7 @@ void DivEngine::registerSystems() {
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
{DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY},
{},
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
[](int,unsigned char,unsigned char) -> bool {return false;},
[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
if (effect>=0x30 && effect<0x40) {
@ -2045,7 +2045,7 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_YMZ280B]=new DivSysDef(
"Yamaha YMZ280B", NULL, 0xb8, 0, 8, false, true, 0x151, false,
"Yamaha YMZ280B (PCMD8)", NULL, 0xb8, 0, 8, false, true, 0x151, false,
"used in some arcade boards. Can play back either 4-bit ADPCM, 8-bit PCM or 16-bit PCM.",
{"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8"},
{"1", "2", "3", "4", "5", "6", "7", "8"},
@ -2079,7 +2079,7 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_NAMCO_15XX]=new DivSysDef(
"Namco 15XX WSG", NULL, 0xba, 0, 8, false, true, 0, false,
"Namco C15 WSG", NULL, 0xba, 0, 8, false, true, 0, false,
"successor of the original Namco WSG chip, used in later Namco arcade games.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
@ -2090,8 +2090,8 @@ void DivEngine::registerSystems() {
);
sysDefs[DIV_SYSTEM_NAMCO_CUS30]=new DivSysDef(
"Namco CUS30 WSG", NULL, 0xbb, 0, 8, false, true, 0, false,
"like Namco 15XX but with stereo sound.",
"Namco C30 WSG", NULL, 0xbb, 0, 8, false, true, 0, false,
"like Namco C15 but with stereo sound.",
{"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_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},

View file

@ -186,7 +186,7 @@ bool DivWaveSynth::tick(bool skipSubDiv) {
break;
case DIV_WS_PHASE_MOD:
for (int i=0; i<=state.speed; i++) {
int mod=(wave2[pos]*(state.param2-stage))>>8;
int mod=(wave2[pos]*(state.param2-stage)*width)/512;
output[pos]=wave1[(pos+mod)%width];
if (++pos>=width) {
pos=0;