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

This commit is contained in:
Natt Akuma 2023-03-01 12:58:27 +07:00
commit e0803d9bb1
420 changed files with 125299 additions and 7468 deletions

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -175,7 +175,7 @@ String DivConfig::getString(String key, String fallback) const {
return fallback;
}
bool DivConfig::has(String key) {
bool DivConfig::has(String key) const {
try {
String test=conf.at(key);
} catch (std::out_of_range& e) {

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -46,7 +46,7 @@ class DivConfig {
String getString(String key, String fallback) const;
// check for existence
bool has(String key);
bool has(String key) const;
// set a config value
void set(String key, bool value);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -161,3 +161,7 @@ void DivEngine::setConf(String key, const char* value) {
void DivEngine::setConf(String key, String value) {
conf.set(key,value);
}
bool DivEngine::hasConf(String key) {
return conf.has(key);
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -24,6 +24,7 @@
#define DIV_MAX_CHIPS 32
#define DIV_MAX_CHANS 128
#define DIV_MAX_PATTERNS 256
#define DIV_MAX_CHIP_DEFS 256
// in-pattern
#define DIV_MAX_ROWS 256

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -103,9 +103,8 @@ enum DivDispatchCmds {
DIV_CMD_FM_AM_DEPTH, // (depth)
DIV_CMD_FM_PM_DEPTH, // (depth)
DIV_CMD_GENESIS_LFO, // unused?
DIV_CMD_ARCADE_LFO, // unused?
DIV_CMD_FM_LFO2, // (speed)
DIV_CMD_FM_LFO2_WAVE, // (waveform)
DIV_CMD_STD_NOISE_FREQ, // (freq)
DIV_CMD_STD_NOISE_MODE, // (mode)
@ -215,6 +214,21 @@ enum DivDispatchCmds {
DIV_CMD_SURROUND_PANNING, // (out, val)
DIV_CMD_FM_AM2_DEPTH, // (depth)
DIV_CMD_FM_PM2_DEPTH, // (depth)
DIV_CMD_ES5506_FILTER_MODE, // (value)
DIV_CMD_ES5506_FILTER_K1, // (value, mask)
DIV_CMD_ES5506_FILTER_K2, // (value, mask)
DIV_CMD_ES5506_FILTER_K1_SLIDE, // (value, negative)
DIV_CMD_ES5506_FILTER_K2_SLIDE, // (value, negative)
DIV_CMD_ES5506_ENVELOPE_COUNT, // (count)
DIV_CMD_ES5506_ENVELOPE_LVRAMP, // (ramp)
DIV_CMD_ES5506_ENVELOPE_RVRAMP, // (ramp)
DIV_CMD_ES5506_ENVELOPE_K1RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_PAUSE, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX
@ -632,8 +646,8 @@ class DivDispatch {
#define COLOR_PAL (283.75*15625.0+25.0)
#define CLAMP_VAR(x,xMin,xMax) \
if (x<xMin) x=xMin; \
if (x>xMax) x=xMax;
if ((x)<(xMin)) (x)=(xMin); \
if ((x)>(xMax)) (x)=(xMax);
#define NEW_ARP_STRAT (parent->song.linearPitch==2 && !parent->song.oldArpStrategy)
#define HACKY_LEGATO_MESS chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode && !NEW_ARP_STRAT

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -68,6 +68,7 @@
#include "platform/vrc6.h"
#include "platform/fds.h"
#include "platform/mmc5.h"
#include "platform/es5506.h"
#include "platform/scc.h"
#include "platform/ymz280b.h"
#include "platform/rf5c68.h"
@ -75,6 +76,7 @@
#include "platform/vb.h"
#include "platform/k007232.h"
#include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -440,6 +442,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_MMC5:
dispatch=new DivPlatformMMC5;
break;
case DIV_SYSTEM_ES5506:
dispatch=new DivPlatformES5506;
break;
case DIV_SYSTEM_SCC:
dispatch=new DivPlatformSCC;
((DivPlatformSCC*)dispatch)->setChipModel(false);
@ -489,6 +494,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_GA20:
dispatch=new DivPlatformGA20;
break;
case DIV_SYSTEM_SM8521:
dispatch=new DivPlatformSM8521;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0x08:
return "08xy: Set panning (x: left; y: right)";
case 0x09:
return "09xx: Set speed 1";
return "09xx: Set groove pattern (speed 1 if no grooves exist)";
case 0x0a:
return "0Axy: Volume slide (0y: down; x0: up)";
case 0x0b:
@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0x0d:
return "0Dxx: Jump to next pattern";
case 0x0f:
return "0Fxx: Set speed 2";
return "0Fxx: Set speed (speed 2 if no grooves exist)";
case 0x80:
return "80xx: Set panning (00: left; 80: center; FF: right)";
case 0x81:
@ -110,8 +110,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "EDxx: Note delay";
case 0xee:
return "EExx: Send external command";
case 0xef:
return "EFxx: Set global tuning (quirky!)";
case 0xf0:
return "F0xx: Set tick rate (bpm)";
case 0xf1:
@ -1471,6 +1469,43 @@ void DivEngine::createNew(const char* description, String sysName, bool inBase64
BUSY_END;
}
void DivEngine::createNewFromDefaults() {
quitDispatch();
BUSY_BEGIN;
saveLock.lock();
song.unload();
song=DivSong();
changeSong(0);
String preset=getConfString("initialSys2","");
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
if (preset.empty()) {
// try loading old preset
logD("trying to load old preset");
preset=decodeSysDesc(getConfString("initialSys",""));
oldVol=false;
}
logD("preset size %ld",preset.size());
if (preset.size()>0 && (preset.size()&3)==0) {
initSongWithDesc(preset.c_str(),true,oldVol);
}
String sysName=getConfString("initialSysName","");
if (sysName=="") {
song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
} else {
song.systemName=sysName;
}
recalcChans();
saveLock.unlock();
BUSY_END;
initDispatch();
BUSY_BEGIN;
renderSamples();
reset();
BUSY_END;
}
void DivEngine::swapChannels(int src, int dest) {
logV("swapping channel %d with %d",src,dest);
if (src==dest) {
@ -1961,14 +1996,12 @@ String DivEngine::getPlaybackDebugInfo() {
"cmdsPerSecond: %d\n"
"globalPitch: %d\n"
"extValue: %d\n"
"speed1: %d\n"
"speed2: %d\n"
"tempoAccum: %d\n"
"totalProcessed: %d\n"
"bufferPos: %d\n",
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
(int)extValue,(int)speed1,(int)speed2,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
);
}
@ -2093,7 +2126,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
lastLoopPos=-1;
}
endOfSong=false;
speedAB=false;
// whaaaaa?
curSpeed=0;
playing=true;
skipping=true;
memset(walked,0,8192);
@ -2441,15 +2475,14 @@ void DivEngine::reset() {
}
extValue=0;
extValuePresent=0;
speed1=curSubSong->speed1;
speed2=curSubSong->speed2;
speeds=curSubSong->speeds;
firstTick=false;
shallStop=false;
shallStopSched=false;
pendingMetroTick=0;
elapsedBars=0;
elapsedBeats=0;
nextSpeed=speed1;
nextSpeed=speeds.val[0];
divider=60;
if (curSubSong->customTempo) {
divider=curSubSong->hz;
@ -2519,6 +2552,8 @@ int DivEngine::getEffectiveSampleRate(int rate) {
return (48828*MIN(128,(rate*128/48828)))/128;
case DIV_SYSTEM_X1_010:
return (31250*MIN(255,(rate*16/31250)))/16; // TODO: support variable clock case
case DIV_SYSTEM_ES5506:
return (31250*MIN(131071,(rate*2048/31250)))/2048; // TODO: support variable clock, channel limit case
default:
break;
}
@ -2649,12 +2684,8 @@ size_t DivEngine::getCurrentSubSong() {
return curSubSongIndex;
}
unsigned char DivEngine::getSpeed1() {
return speed1;
}
unsigned char DivEngine::getSpeed2() {
return speed2;
const DivGroovePattern& DivEngine::getSpeeds() {
return speeds;
}
float DivEngine::getHz() {
@ -3552,14 +3583,14 @@ void DivEngine::delSample(int index) {
BUSY_END;
}
void DivEngine::addOrder(bool duplicate, bool where) {
void DivEngine::addOrder(int pos, bool duplicate, bool where) {
unsigned char order[DIV_MAX_CHANS];
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
memset(order,0,DIV_MAX_CHANS);
BUSY_BEGIN_SOFT;
if (duplicate) {
for (int i=0; i<DIV_MAX_CHANS; i++) {
order[i]=curOrders->ord[i][curOrder];
order[i]=curOrders->ord[i][pos];
}
} else {
bool used[DIV_MAX_PATTERNS];
@ -3587,14 +3618,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
} else { // after current order
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
for (int j=curSubSong->ordersLen; j>pos; j--) {
curOrders->ord[i][j]=curOrders->ord[i][j-1];
}
curOrders->ord[i][curOrder+1]=order[i];
curOrders->ord[i][pos+1]=order[i];
}
curSubSong->ordersLen++;
saveLock.unlock();
curOrder++;
if (pos<=curOrder) curOrder++;
if (playing && !freelance) {
playSub(false);
}
@ -3602,7 +3633,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
BUSY_END;
}
void DivEngine::deepCloneOrder(bool where) {
void DivEngine::deepCloneOrder(int pos, bool where) {
unsigned char order[DIV_MAX_CHANS];
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
warnings="";
@ -3610,7 +3641,7 @@ void DivEngine::deepCloneOrder(bool where) {
for (int i=0; i<chans; i++) {
bool didNotFind=true;
logD("channel %d",i);
order[i]=curOrders->ord[i][curOrder];
order[i]=curOrders->ord[i][pos];
// find free slot
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
logD("finding free slot in %d...",j);
@ -3639,14 +3670,14 @@ void DivEngine::deepCloneOrder(bool where) {
} else { // after current order
saveLock.lock();
for (int i=0; i<chans; i++) {
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
for (int j=curSubSong->ordersLen; j>pos; j--) {
curOrders->ord[i][j]=curOrders->ord[i][j-1];
}
curOrders->ord[i][curOrder+1]=order[i];
curOrders->ord[i][pos+1]=order[i];
}
curSubSong->ordersLen++;
saveLock.unlock();
curOrder++;
if (pos<=curOrder) curOrder++;
if (playing && !freelance) {
playSub(false);
}
@ -3654,17 +3685,18 @@ void DivEngine::deepCloneOrder(bool where) {
BUSY_END;
}
void DivEngine::deleteOrder() {
void DivEngine::deleteOrder(int pos) {
if (curSubSong->ordersLen<=1) return;
BUSY_BEGIN_SOFT;
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curOrder; j<curSubSong->ordersLen; j++) {
for (int j=pos; j<curSubSong->ordersLen; j++) {
curOrders->ord[i][j]=curOrders->ord[i][j+1];
}
}
curSubSong->ordersLen--;
saveLock.unlock();
if (curOrder>pos) curOrder--;
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
if (playing && !freelance) {
playSub(false);
@ -3672,40 +3704,46 @@ void DivEngine::deleteOrder() {
BUSY_END;
}
void DivEngine::moveOrderUp() {
void DivEngine::moveOrderUp(int& pos) {
BUSY_BEGIN_SOFT;
if (curOrder<1) {
if (pos<1) {
BUSY_END;
return;
}
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
curOrders->ord[i][pos-1]^=curOrders->ord[i][pos];
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
}
saveLock.unlock();
curOrder--;
if (curOrder==pos) {
curOrder--;
}
pos--;
if (playing && !freelance) {
playSub(false);
}
BUSY_END;
}
void DivEngine::moveOrderDown() {
void DivEngine::moveOrderDown(int& pos) {
BUSY_BEGIN_SOFT;
if (curOrder>=curSubSong->ordersLen-1) {
if (pos>=curSubSong->ordersLen-1) {
BUSY_END;
return;
}
saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) {
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
curOrders->ord[i][pos+1]^=curOrders->ord[i][pos];
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
}
saveLock.unlock();
curOrder++;
if (curOrder==pos) {
curOrder++;
}
pos++;
if (playing && !freelance) {
playSub(false);
}
@ -4236,7 +4274,7 @@ void DivEngine::quitDispatch() {
clockDrift=0;
chans=0;
playing=false;
speedAB=false;
curSpeed=0;
endOfSong=false;
ticks=0;
tempoAccum=0;
@ -4415,6 +4453,8 @@ void DivEngine::preInit() {
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
startLogFile(logPath.c_str());
logI("Furnace version " DIV_VERSION ".");
loadConf();
}
@ -4429,6 +4469,7 @@ bool DivEngine::init() {
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
if (preset.empty()) {
// try loading old preset
logD("trying to load old preset");
preset=decodeSysDesc(getConfString("initialSys",""));
oldVol=false;
}
@ -4472,6 +4513,9 @@ bool DivEngine::init() {
for (int i=0; i<64; i++) {
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
}
for (int i=0; i<128; i++) {
tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI)));
}
for (int i=0; i<4096; i++) {
reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0)));
pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0)));

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -47,8 +47,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev138"
#define DIV_ENGINE_VERSION 138
#define DIV_VERSION "0.6pre4-hotfix"
#define DIV_ENGINE_VERSION 144
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -100,6 +100,7 @@ struct DivChannelState {
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
bool wentThroughNote, goneThroughNote;
int midiNote, curMidiNote, midiPitch;
size_t midiAge;
@ -152,6 +153,8 @@ struct DivChannelState {
wasShorthandPorta(false),
noteOnInhibit(false),
resetArp(false),
wentThroughNote(false),
goneThroughNote(false),
midiNote(-1),
curMidiNote(-1),
midiPitch(-1),
@ -337,7 +340,6 @@ class DivEngine {
bool playing;
bool freelance;
bool shallStop, shallStopSched;
bool speedAB;
bool endOfSong;
bool consoleMode;
bool extValuePresent;
@ -359,7 +361,7 @@ class DivEngine {
bool midiOutClock;
int midiOutMode;
int softLockCount;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
size_t curSubSongIndex;
size_t bufferPos;
double divider;
@ -368,7 +370,7 @@ class DivEngine {
int stepPlay;
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
unsigned char extValue, pendingMetroTick;
unsigned char speed1, speed2;
DivGroovePattern speeds;
short tempoAccum;
DivStatusView view;
DivHaltPositions haltOn;
@ -391,9 +393,9 @@ class DivEngine {
std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes;
static DivSysDef* sysDefs[256];
static DivSystem sysFileMapFur[256];
static DivSystem sysFileMapDMF[256];
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
struct SamplePreview {
double rate;
@ -413,6 +415,7 @@ class DivEngine {
} sPreview;
short vibTable[64];
short tremTable[128];
int reversePitchTable[4096];
int pitchTable[4096];
char c163NameCS[1024];
@ -513,6 +516,7 @@ class DivEngine {
String decodeSysDesc(String desc);
// start fresh
void createNew(const char* description, String sysName, bool inBase64=true);
void createNewFromDefaults();
// load a file.
bool load(unsigned char* f, size_t length);
// save as .dmf.
@ -524,7 +528,12 @@ class DivEngine {
// specify system to build ROM for.
SafeWriter* buildROM(int sys);
// dump to VGM.
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false);
// set trailingTicks to:
// - 0 to add one tick of trailing
// - x to add x+1 ticks of trailing
// - -1 to auto-determine trailing
// - -2 to add a whole loop of trailing
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
// dump to ZSM.
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
// dump command stream.
@ -581,6 +590,9 @@ class DivEngine {
void setConf(String key, const char* value);
void setConf(String key, String value);
// get whether config value exists
bool hasConf(String key);
// calculate base frequency/period
double calcBaseFreq(double clock, double divider, int note, bool period);
@ -729,11 +741,8 @@ class DivEngine {
// get current subsong
size_t getCurrentSubSong();
// get speed 1
unsigned char getSpeed1();
// get speed 2
unsigned char getSpeed2();
// get speeds
const DivGroovePattern& getSpeeds();
// get Hz
float getHz();
@ -813,19 +822,19 @@ class DivEngine {
void delSample(int index);
// add order
void addOrder(bool duplicate, bool where);
void addOrder(int pos, bool duplicate, bool where);
// deep clone orders
void deepCloneOrder(bool where);
void deepCloneOrder(int pos, bool where);
// delete order
void deleteOrder();
void deleteOrder(int pos);
// move order up
void moveOrderUp();
void moveOrderUp(int& pos);
// move order down
void moveOrderDown();
void moveOrderDown(int& pos);
// move thing up
bool moveInsUp(int which);
@ -1064,7 +1073,6 @@ class DivEngine {
freelance(false),
shallStop(false),
shallStopSched(false),
speedAB(false),
endOfSong(false),
consoleMode(false),
extValuePresent(false),
@ -1098,6 +1106,7 @@ class DivEngine {
nextSpeed(3),
elapsedBars(0),
elapsedBeats(0),
curSpeed(0),
curSubSongIndex(0),
bufferPos(0),
divider(60),
@ -1115,8 +1124,6 @@ class DivEngine {
globalPitch(0),
extValue(0),
pendingMetroTick(0),
speed1(3),
speed2(3),
tempoAccum(0),
view(DIV_STATUS_NOTHING),
haltOn(DIV_HALT_NONE),
@ -1158,13 +1165,14 @@ class DivEngine {
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(vibTable,0,64*sizeof(short));
memset(tremTable,0,128*sizeof(short));
memset(reversePitchTable,0,4096*sizeof(int));
memset(pitchTable,0,4096*sizeof(int));
memset(sysDefs,0,256*sizeof(void*));
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
memset(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
for (int i=0; i<256; i++) {
for (int i=0; i<DIV_MAX_CHIP_DEFS; i++) {
sysFileMapFur[i]=DIV_SYSTEM_NULL;
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
ds.version=(unsigned char)reader.readC();
logI("module version %d (0x%.2x)",ds.version,ds.version);
if (ds.version>0x1a) {
if (ds.version>0x1b) {
logE("this version is not supported by Furnace yet!");
lastError="this version is not supported by Furnace yet";
delete[] file;
@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
ds.subsong[0]->timeBase=reader.readC();
ds.subsong[0]->speed1=reader.readC();
ds.subsong[0]->speeds.len=2;
ds.subsong[0]->speeds.val[0]=reader.readC();
if (ds.version>0x07) {
ds.subsong[0]->speed2=reader.readC();
ds.subsong[0]->speeds.val[1]=reader.readC();
ds.subsong[0]->pal=reader.readC();
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
ds.subsong[0]->customTempo=reader.readC();
} else {
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
ds.subsong[0]->speeds.len=1;
}
if (ds.version>0x0a) {
String hz=reader.readString(3);
@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
for (int i=0; i<ds.sampleLen; i++) {
DivSample* sample=new DivSample;
int length=reader.readI();
int cutStart=0;
int cutEnd=length;
int pitch=5;
int vol=50;
short* data;
@ -866,6 +869,29 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
}
}
if (ds.version>=0x1b) {
// what the hell man...
cutStart=reader.readI();
cutEnd=reader.readI();
if (cutStart<0 || cutStart>length) {
logE("cutStart is out of range! (%d)",cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<0 || cutEnd>length) {
logE("cutEnd is out of range! (%d)",cutEnd);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<cutStart) {
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
}
if (length>0) {
if (ds.version>0x08) {
if (ds.version<0x0b) {
@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length];
reader.read(data,length*2);
}
if (ds.version>0x1b) {
if (cutStart!=0 || cutEnd!=length) {
// cut data
short* newData=new short[cutEnd-cutStart];
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
delete[] data;
data=newData;
length=cutEnd-cutStart;
cutStart=0;
cutEnd=length;
}
}
#ifdef TA_BIG_ENDIAN
// convert to big-endian
@ -1743,8 +1782,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.readI();
subSong->timeBase=reader.readC();
subSong->speed1=reader.readC();
subSong->speed2=reader.readC();
subSong->speeds.len=2;
subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53);
@ -2232,6 +2272,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
if (ds.version>=139) {
subSong->speeds.len=reader.readC();
for (int i=0; i<16; i++) {
subSong->speeds.val[i]=reader.readC();
}
// grooves
unsigned char grooveCount=reader.readC();
for (int i=0; i<grooveCount; i++) {
DivGroovePattern gp;
gp.len=reader.readC();
for (int j=0; j<16; j++) {
gp.val[j]=reader.readC();
}
ds.grooves.push_back(gp);
}
}
// read system flags
if (ds.version>=119) {
logD("reading chip flags...");
@ -2290,8 +2349,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
subSong=ds.subsong[i+1];
subSong->timeBase=reader.readC();
subSong->speed1=reader.readC();
subSong->speed2=reader.readC();
subSong->speeds.len=2;
subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53);
@ -2339,6 +2399,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
for (int i=0; i<tchans; i++) {
subSong->chanShortName[i]=reader.readString();
}
if (ds.version>=139) {
subSong->speeds.len=reader.readC();
for (int i=0; i<16; i++) {
subSong->speeds.val[i]=reader.readC();
}
}
}
}
@ -2957,7 +3024,6 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
if (fxVal>0x20 && ds.name!="klisje paa klisje") {
writeFxCol(0xf0,fxVal);
} else {
writeFxCol(0x09,fxVal);
writeFxCol(0x0f,fxVal);
}
break;
@ -3436,8 +3502,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->pal=true;
ds.subsong[0]->customTempo=true;
ds.subsong[0]->pat[3].effectCols=3;
ds.subsong[0]->speed1=3;
ds.subsong[0]->speed2=3;
ds.subsong[0]->speeds.val[0]=3;
ds.subsong[0]->speeds.len=1;
int lastIns[4];
int lastNote[4];
@ -3454,10 +3520,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->orders.ord[j][i]=i;
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
if (j==3 && seq[i].speed) {
p->data[0][6]=0x09;
p->data[0][6]=0x0f;
p->data[0][7]=seq[i].speed;
p->data[0][8]=0x0f;
p->data[0][9]=seq[i].speed;
}
bool ignoreNext=false;
@ -4344,8 +4408,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0);
w->writeC(subSong->timeBase);
w->writeC(subSong->speed1);
w->writeC(subSong->speed2);
// these are for compatibility
w->writeC(subSong->speeds.val[0]);
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
w->writeC(subSong->arpLen);
w->writeF(subSong->hz);
w->writeS(subSong->patLen);
@ -4532,6 +4597,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(0);
}
// speeds of first song
w->writeC(subSong->speeds.len);
for (int i=0; i<16; i++) {
w->writeC(subSong->speeds.val[i]);
}
// groove list
w->writeC((unsigned char)song.grooves.size());
for (const DivGroovePattern& i: song.grooves) {
w->writeC(i.len);
for (int j=0; j<16; j++) {
w->writeC(i.val[j]);
}
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
@ -4546,8 +4626,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0);
w->writeC(subSong->timeBase);
w->writeC(subSong->speed1);
w->writeC(subSong->speed2);
w->writeC(subSong->speeds.val[0]);
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
w->writeC(subSong->arpLen);
w->writeF(subSong->hz);
w->writeS(subSong->patLen);
@ -4586,6 +4666,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeString(subSong->chanShortName[i],false);
}
// speeds
w->writeC(subSong->speeds.len);
for (int i=0; i<16; i++) {
w->writeC(subSong->speeds.val[i]);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
@ -4841,8 +4927,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(curSubSong->hilightB);
w->writeC(curSubSong->timeBase);
w->writeC(curSubSong->speed1);
w->writeC(curSubSong->speed2);
w->writeC(curSubSong->speeds.val[0]);
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
w->writeC(curSubSong->pal);
w->writeC(curSubSong->customTempo);
char customHz[4];
@ -4866,6 +4952,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
addWarning("only the currently selected subsong will be saved");
}
if (!song.grooves.empty()) {
addWarning("grooves will not be saved");
}
if (curSubSong->speeds.len>2) {
addWarning("only the first two speeds will be effective");
}
if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
addWarning(".dmf format does not support virtual tempo");
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -924,6 +924,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_POKEMINI:
break;
case DIV_INS_SM8521:
checkForWL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_MAX:
break;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -78,6 +78,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_K007232=45,
DIV_INS_GA20=46,
DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_MAX,
DIV_INS_NULL
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -56,7 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
}
if (delay>0) {
delay--;
had=false;
if (!linger) had=false;
return;
}
if (began && source.delay>0) {
@ -523,4 +523,4 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) {
return NULL;
}
#undef CONSIDER
#undef CONSIDER

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -89,7 +89,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
chan[i].audSub-=AMIGA_DIVIDER;
if (chan[i].audSub<0) {
if (chan[i].useWave) {
writeAudDat(chan[i].ws.output[chan[i].audPos++]^0x80);
writeAudDat(chan[i].ws.output[(chan[i].audPos++)&255]^0x80);
if (chan[i].audPos>=(unsigned int)(chan[i].audLen<<1)) {
chan[i].audPos=0;
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -152,7 +152,9 @@ void DivPlatformArcade::tick(bool sysTick) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (KVS(i,j)) {
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
@ -235,7 +237,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) {
if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(i,j)) {
@ -296,7 +298,9 @@ void DivPlatformArcade::tick(bool sysTick) {
}
if (m.tl.had) {
op.tl=127-m.tl.val;
if (KVS(i,j)) {
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
@ -319,27 +323,6 @@ void DivPlatformArcade::tick(bool sysTick) {
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
}
}
if (chan[i].keyOn || chan[i].keyOff) {
if (chan[i].hardReset && chan[i].keyOn) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
immWrite(baseAddr+ADDR_TL,0x7f);
oldWrites[baseAddr+ADDR_SL_RR]=-1;
oldWrites[baseAddr+ADDR_TL]=-1;
}
}
immWrite(0x08,i);
if (chan[i].hardReset && chan[i].keyOn) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
for (int k=0; k<9; k++) {
immWrite(baseAddr+ADDR_SL_RR,0x0f);
}
}
}
chan[i].keyOff=false;
}
}
for (int i=0; i<256; i++) {
@ -349,6 +332,24 @@ void DivPlatformArcade::tick(bool sysTick) {
}
}
int hardResetElapsed=0;
bool mustHardReset=false;
for (int i=0; i<8; i++) {
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(0x08,i);
if (chan[i].hardReset && chan[i].keyOn) {
mustHardReset=true;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
chan[i].keyOff=false;
}
}
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
@ -363,14 +364,37 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1;
immWrite(i+0x28,hScale(chan[i].freq>>6));
immWrite(i+0x30,chan[i].freq<<2);
hardResetElapsed+=2;
chan[i].freqChanged=false;
}
if (chan[i].keyOn || chan[i].opMaskChanged) {
if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) {
immWrite(0x08,(chan[i].opMask<<3)|i);
hardResetElapsed++;
chan[i].opMaskChanged=false;
chan[i].keyOn=false;
}
}
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0x1f,i&0xff);
}
for (int i=0; i<8; i++) {
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
// restore SL/RR
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
immWrite(0x08,(chan[i].opMask<<3)|i);
chan[i].opMaskChanged=false;
chan[i].keyOn=false;
}
}
}
}
void DivPlatformArcade::muteChannel(int ch, bool mute) {
@ -382,53 +406,59 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
}
}
void DivPlatformArcade::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator op=chan[ch].state.op[i];
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
if (chan[ch].insChanged) {
if (isMuted[ch]) {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
} else {
rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7));
}
rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3));
}
}
int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6));
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
if (chan[c.chan].insChanged) {
if (isMuted[c.chan]) {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
} else {
rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -462,7 +492,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) {
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(c.chan,i)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
@ -521,6 +553,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_LINEAR(c.value);
chan[c.chan].freqChanged=true;
break;
@ -559,7 +596,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (KVS(c.chan,c.value)) {
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(c.chan,c.value)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
@ -766,7 +805,9 @@ void DivPlatformArcade::forceIns() {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator op=chan[i].state.op[j];
if (KVS(i,j)) {
if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -57,6 +57,7 @@ class DivPlatformArcade: public DivPlatformOPM {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
void acquire_nuked(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
signed int out=0;
// K005289 part
k005289.tick();
k005289.tick(8);
// Wavetable part
for (int i=0; i<2; i++) {
@ -60,7 +60,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) {
}
}
if (++writeOscBuf>=64) writeOscBuf=0;
if (++writeOscBuf>=8) writeOscBuf=0;
out<<=6; // scale output to 16 bit
@ -332,9 +332,9 @@ void DivPlatformBubSysWSG::notifyInsDeletion(void* ins) {
void DivPlatformBubSysWSG::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC;
CHECK_CUSTOM_CLOCK;
rate=chipClock;
rate=chipClock/8;
for (int i=0; i<2; i++) {
oscBuf[i]->rate=rate/64;
oscBuf[i]->rate=rate/8;
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,327 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _ES5506_H
#define _ES5506_H
#include "../dispatch.h"
#include "../engine.h"
#include <queue>
#include "../macroInt.h"
#include "../sample.h"
#include "vgsound_emu/src/es550x/es5506.hpp"
class DivPlatformES5506: public DivDispatch, public es550x_intf {
struct Channel : public SharedChannel<int> {
struct PCM {
bool isNoteMap;
int index, next;
int note;
double freqOffs;
double nextFreqOffs;
bool pause, direction;
unsigned int bank;
unsigned int start;
unsigned int end;
unsigned int length;
unsigned int loopStart;
unsigned int loopEnd;
unsigned int nextPos;
bool setPos;
DivSampleLoopMode loopMode;
PCM():
isNoteMap(false),
index(-1),
next(-1),
note(0),
freqOffs(1.0),
nextFreqOffs(1.0),
pause(false),
direction(false),
bank(0),
start(0),
end(0),
length(0),
loopStart(0),
loopEnd(0),
nextPos(0),
setPos(false),
loopMode(DIV_SAMPLE_LOOP_MAX) {}
} pcm;
int nextFreq, nextNote, currNote, wave;
int volMacroMax, panMacroMax;
bool useWave, isReverseLoop;
unsigned int cr;
struct NoteChanged { // Note changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char offs: 1; // frequency offset
unsigned char note: 1; // note
unsigned char freq: 1; // base frequency
unsigned char dummy: 5; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
NoteChanged() :
changed(0) {}
} noteChanged;
struct VolChanged { // Volume changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char lVol: 1; // left volume
unsigned char rVol: 1; // right volume
unsigned char ca: 1; // Channel assignment
unsigned char dummy: 5; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
VolChanged() :
changed(0) {}
} volChanged;
struct FilterChanged { // Filter changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char mode: 1; // Filter mode
unsigned char k1: 1; // K1
unsigned char k2: 1; // K2
unsigned char dummy: 5; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
FilterChanged():
changed(0) {}
} filterChanged;
struct EnvChanged { // Envelope changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char ecount: 1; // Envelope count
unsigned char lVRamp: 1; // Left volume Ramp
unsigned char rVRamp: 1; // Right volume Ramp
unsigned char k1Ramp: 1; // K1 Ramp w/Slow flag
unsigned char k2Ramp: 1; // K2 Ramp w/Slow flag
unsigned char dummy: 3; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
EnvChanged():
changed(0) {}
} envChanged;
struct PCMChanged {
union {
struct {
unsigned char index: 1; // sample index
unsigned char slice: 1; // transwave slice
unsigned char position: 1; // sample position in memory
unsigned char loopBank: 1; // Loop mode and Bank
unsigned char dummy: 4; // dummy for bit padding
};
unsigned char changed;
};
PCMChanged():
changed(0) {}
} pcmChanged;
struct Overwrite {
DivInstrumentES5506::Filter filter;
DivInstrumentES5506::Envelope envelope;
struct State {
// overwrited flag
union {
struct {
unsigned char mode: 1; // filter mode
unsigned char k1: 1; // k1
unsigned char k2: 1; // k2
unsigned char ecount: 1; // envelope count
unsigned char lVRamp: 1; // left volume ramp
unsigned char rVRamp: 1; // right volume ramp
unsigned char k1Ramp: 1; // k1 ramp
unsigned char k2Ramp: 1; // k2 ramp
};
unsigned char overwrited;
};
State():
overwrited(0) {}
} state;
Overwrite():
filter(DivInstrumentES5506::Filter()),
envelope(DivInstrumentES5506::Envelope()),
state(State()) {}
} overwrite;
unsigned char ca;
signed int k1Offs, k2Offs;
signed int k1Slide, k2Slide;
signed int k1Prev, k2Prev;
int lVol, rVol;
int outLVol, outRVol;
int resLVol, resRVol;
signed int oscOut;
DivInstrumentES5506::Filter filter;
DivInstrumentES5506::Envelope envelope;
Channel():
SharedChannel<int>(0xff),
pcm(PCM()),
nextFreq(0),
nextNote(0),
currNote(0),
wave(-1),
volMacroMax(0xfff),
panMacroMax(0xfff),
useWave(false),
isReverseLoop(false),
cr(0),
noteChanged(NoteChanged()),
volChanged(VolChanged()),
filterChanged(FilterChanged()),
envChanged(EnvChanged()),
pcmChanged(PCMChanged()),
overwrite(Overwrite()),
ca(0),
k1Offs(0),
k2Offs(0),
k1Slide(0),
k2Slide(0),
k1Prev(0xffff),
k2Prev(0xffff),
lVol(0xff),
rVol(0xff),
outLVol(0xfff),
outRVol(0xfff),
resLVol(0xfff),
resRVol(0xfff),
oscOut(0),
filter(DivInstrumentES5506::Filter()),
envelope(DivInstrumentES5506::Envelope()) {
outVol=0xfff;
}
};
Channel chan[32];
DivDispatchOscBuffer* oscBuf[32];
bool isMuted[32];
signed short* sampleMem; // ES5506 uses 16 bit data bus for samples
size_t sampleMemLen;
unsigned int sampleOffES5506[256];
bool sampleLoaded[256];
struct QueuedHostIntf {
unsigned char state;
unsigned char step;
unsigned char addr;
unsigned int val;
unsigned int mask;
unsigned int* read;
unsigned short delay;
bool isRead;
QueuedHostIntf(unsigned char s, unsigned char a, unsigned int v, unsigned int m=(unsigned int)(~0), unsigned short d=0):
state(0),
step(s),
addr(a),
val(v),
mask(m),
read(NULL),
delay(0),
isRead(false) {}
QueuedHostIntf(unsigned char st, unsigned char s, unsigned char a, unsigned int* r, unsigned int m=(unsigned int)(~0), unsigned short d=0):
state(st),
step(s),
addr(a),
val(0),
mask(m),
read(r),
delay(d),
isRead(true) {}
};
struct QueuedReadState {
unsigned int* read;
unsigned char state;
QueuedReadState(unsigned int* r, unsigned char s):
read(r),
state(s) {}
};
std::queue<QueuedHostIntf> hostIntf32;
std::queue<QueuedHostIntf> hostIntf8;
std::queue<unsigned char> queuedRead;
std::queue<QueuedReadState> queuedReadState;
int cycle, curPage, volScale;
unsigned char maskedVal;
unsigned int irqv;
bool isMasked, isReaded;
bool irqTrigger;
unsigned int curCR;
unsigned char initChanMax, chanMax;
es5506_core es5506;
unsigned char regPool[4*16*128]; // 7 bit page x 16 registers per page x 32 bit per registers
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
virtual void e_pin(bool state) override; // E output
virtual void irqb(bool state) override; // IRQB output
virtual s16 read_sample(u8 bank, u32 address) override {
if (sampleMem==NULL) return 0;
return sampleMem[((bank&3)<<21)|(address&0x1fffff)];
}
virtual void acquire(short** buf, size_t len) override;
virtual int dispatch(DivCommand c) override;
virtual void* getChanState(int chan) override;
virtual DivMacroInt* getChanMacroInt(int ch) override;
virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
virtual unsigned char* getRegisterPool() override;
virtual int getRegisterPoolSize() override;
virtual void reset() override;
virtual void forceIns() override;
virtual void tick(bool sysTick=true) override;
virtual void muteChannel(int ch, bool mute) override;
virtual int getOutputCount() override;
virtual bool keyOffAffectsArp(int ch) override;
virtual void setFlags(const DivConfig& flags) override;
virtual void notifyInsChange(int ins) override;
virtual void notifyWaveChange(int wave) override;
virtual void notifyInsDeletion(void* ins) override;
virtual void poke(unsigned int addr, unsigned short val) override;
virtual void poke(std::vector<DivRegWrite>& wlist) override;
virtual const void* getSampleMem(int index = 0) override;
virtual size_t getSampleMemCapacity(int index = 0) override;
virtual size_t getSampleMemUsage(int index = 0) override;
virtual bool isSampleLoaded(int index, int sample) override;
virtual void renderSamples(int sysID) override;
virtual const char** getRegisterSheet() override;
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
virtual void quit() override;
DivPlatformES5506():
DivDispatch(),
es550x_intf(),
es5506(*this) {}
};
#endif

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -41,8 +41,14 @@ class DivPlatformOPM: public DivPlatformFMBase {
0x00, 0x08, 0x10, 0x18
};
unsigned char lfoValue, lfoValue2, lfoShape, lfoShape2;
DivPlatformOPM():
DivPlatformFMBase() {}
DivPlatformFMBase(),
lfoValue(0),
lfoValue2(0),
lfoShape(0),
lfoShape2(0) {}
};
#endif

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -154,6 +154,8 @@ class DivPlatformOPN: public DivPlatformFMBase {
unsigned int ayDiv;
unsigned char csmChan;
unsigned char lfoValue;
unsigned short ssgVol;
unsigned short fmVol;
bool extSys, useCombo, fbAllOps;
DivConfig ayFlags;
@ -172,6 +174,8 @@ class DivPlatformOPN: public DivPlatformFMBase {
ayDiv(a),
csmChan(cc),
lfoValue(0),
ssgVol(128),
fmVol(256),
extSys(isExtSys),
useCombo(false),
fbAllOps(false) {}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -47,6 +47,8 @@ class DivPlatformFMBase: public DivDispatch {
0,2,1,3
};
const unsigned int hardResetCycles=127;
struct FMChannel: public SharedChannel<int> {
DivInstrumentFM state;
unsigned char freqH, freqL;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -92,7 +92,7 @@ void DivPlatformGA20::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
const signed char macroVol=VOL_SCALE_LOG((chan[i].vol&0xff),(0xff*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul,0xff);
if ((!isMuted[i]) && (macroVol!=chan[i].outVol)) {
if (macroVol!=chan[i].outVol) {
chan[i].outVol=macroVol;
chan[i].volumeChanged=true;
}
@ -121,7 +121,7 @@ void DivPlatformGA20::tick(bool sysTick) {
}
}
if (chan[i].volumeChanged) {
chan[i].resVol=(chan[i].active && isMuted[i])?0:chan[i].outVol&0xff;
chan[i].resVol=chan[i].outVol&0xff;
chWrite(i,0x5,chan[i].resVol);
chan[i].volumeChanged=false;
}
@ -175,9 +175,7 @@ void DivPlatformGA20::tick(bool sysTick) {
chWrite(i,6,2);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
if (!isMuted[i]) {
chan[i].volumeChanged=true;
}
chan[i].volumeChanged=true;
}
chan[i].keyOn=false;
}
@ -218,9 +216,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
chan[c.chan].volumeChanged=true;
}
break;
}
@ -244,9 +240,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
chan[c.chan].volumeChanged=true;
}
}
break;
@ -320,6 +314,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
void DivPlatformGA20::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
ga20.set_mute(ch,mute);
chan[ch].volumeChanged=true;
}
@ -358,6 +353,7 @@ void DivPlatformGA20::reset() {
// keyoff all channels
chWrite(i,5,0);
chWrite(i,6,0);
if (isMuted[i]) ga20.set_mute(i,true);
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -30,17 +30,26 @@
void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) {
if (tnum==1) {
countB=duration_in_clocks;
setB=duration_in_clocks;
} else if (tnum==0) {
countA=duration_in_clocks;
setA=duration_in_clocks;
}
//logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks);
}
void DivYM2612Interface::clock() {
if (countA>=0) {
if (setA>=0) {
countA-=144;
if (countA<0) m_engine->engine_timer_expired(0);
if (countA<0) {
m_engine->engine_timer_expired(0);
countA+=setA;
}
}
if (setB>=0) {
countB-=144;
if (countB<0) {
m_engine->engine_timer_expired(1);
countB+=setB;
}
}
}
@ -174,18 +183,18 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
}
}
os[0]=(os[0]<<5);
if (chipType!=2) os[0]=(os[0]<<5);
if (os[0]<-32768) os[0]=-32768;
if (os[0]>32767) os[0]=32767;
os[1]=(os[1]<<5);
if (chipType!=2) os[1]=(os[1]<<5);
if (os[1]<-32768) os[1]=-32768;
if (os[1]>32767) os[1]=32767;
@ -214,7 +223,7 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
flushFirst=false;
}
if (ladder) {
if (chipType==1) {
fm_ymfm->generate(&out_ymfm);
} else {
((ymfm::ym3438*)fm_ymfm)->generate(&out_ymfm);
@ -225,6 +234,9 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
//OPN2_Write(&fm,0,0);
for (int i=0; i<6; i++) {
int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
if (chOut<-32768) chOut=-32768;
if (chOut>32767) chOut=32767;
if (i==5) {
if (fm_ymfm->debug_dac_enable()) {
if (softPCM) {
@ -234,10 +246,10 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
}
}
@ -288,7 +300,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (i<6) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) {
if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(i,j)) {
@ -371,7 +383,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) {
if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(i,j)) {
@ -432,7 +444,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isMuted[i]) {
if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(i,j)) {
@ -472,26 +484,19 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
int hardResetElapsed=0;
bool mustHardReset=false;
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(0x28,0x00|konOffs[i]);
if (chan[i].hardReset && chan[i].keyOn) {
mustHardReset=true;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
immWrite(baseAddr+ADDR_TL,0x7f);
oldWrites[baseAddr+ADDR_SL_RR]=-1;
oldWrites[baseAddr+ADDR_TL]=-1;
//rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
immWrite(0x28,0x00|konOffs[i]);
if (chan[i].hardReset && chan[i].keyOn) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
for (int k=0; k<5; k++) {
immWrite(baseAddr+ADDR_SL_RR,0x0f);
}
hardResetElapsed++;
}
}
chan[i].keyOff=false;
@ -520,6 +525,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (i<6) {
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
hardResetElapsed+=2;
}
if (chan[i].furnaceDac && chan[i].dacMode) {
double off=1.0;
@ -538,12 +544,38 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
chan[i].freqChanged=false;
}
if (chan[i].keyOn || chan[i].opMaskChanged) {
if (i<6) immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) {
if (i<6) {
immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
hardResetElapsed++;
}
chan[i].opMaskChanged=false;
chan[i].keyOn=false;
}
}
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<csmChan; i++) {
if (i==2 && extMode) continue;
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
if (i<6) {
// restore SL/RR
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
}
chan[i].opMaskChanged=false;
chan[i].keyOn=false;
}
}
}
}
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
@ -553,7 +585,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
if (isMuted[ch] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(ch,j)) {
@ -569,6 +601,47 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
void DivPlatformGenesis::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
chan[ch].opMask=
(chan[ch].state.op[0].enable?1:0)|
(chan[ch].state.op[2].enable?2:0)|
(chan[ch].state.op[1].enable?4:0)|
(chan[ch].state.op[3].enable?8:0);
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
if (isMuted[ch] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(ch,i)) {
if (!chan[ch].active || chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
}
} else {
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[ch].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[ch].insChanged) {
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
}
int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
@ -608,7 +681,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
} else {
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
if (dumpWrites) {
addWrite(0xffff0000,chan[c.chan].dacSample);
addWrite(0xffff0003,chan[c.chan].dacDirection);
}
}
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
@ -651,49 +727,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
if (c.chan>=6) break;
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
chan[c.chan].opMask=
(chan[c.chan].state.op[0].enable?1:0)|
(chan[c.chan].state.op[2].enable?2:0)|
(chan[c.chan].state.op[1].enable?4:0)|
(chan[c.chan].state.op[3].enable?8:0);
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,i)) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
if (chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -741,7 +780,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isMuted[c.chan]) {
if (isMuted[c.chan] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,i)) {
@ -876,6 +915,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
}
chan[c.chan].note=c.value;
@ -914,7 +958,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isMuted[c.chan]) {
if (isMuted[c.chan] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(c.chan,c.value)) {
@ -1111,7 +1155,7 @@ void DivPlatformGenesis::forceIns() {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) {
if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (KVS(i,j)) {
@ -1181,7 +1225,17 @@ void DivPlatformGenesis::reset() {
fm_ymfm->reset();
}
OPN2_Reset(&fm);
OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0);
switch (chipType) {
case 1: // YM2612
OPN2_SetChipType(&fm,ym3438_mode_ym2612);
break;
case 2: // YMF276
OPN2_SetChipType(&fm,ym3438_mode_opn);
break;
default: // YM3438
OPN2_SetChipType(&fm,0);
break;
}
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -1281,14 +1335,28 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC*15.0/7.0;
break;
}
ladder=flags.getBool("ladderEffect",false);
if (flags.has("chipType")) {
chipType=flags.getInt("chipType",0);
} else {
chipType=flags.getBool("ladderEffect",false)?1:0;
}
noExtMacros=flags.getBool("noExtMacros",false);
fbAllOps=flags.getBool("fbAllOps",false);
OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0);
switch (chipType) {
case 1: // YM2612
OPN2_SetChipType(&fm,ym3438_mode_ym2612);
break;
case 2: // YMF276
OPN2_SetChipType(&fm,ym3438_mode_opn);
break;
default: // YM3438
OPN2_SetChipType(&fm,0);
break;
}
CHECK_CUSTOM_CLOCK;
if (useYMFM) {
if (fm_ymfm!=NULL) delete fm_ymfm;
if (ladder) {
if (chipType==1) {
fm_ymfm=new ymfm::ym2612(iface);
} else {
fm_ymfm=new ymfm::ym3438(iface);
@ -1305,7 +1373,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
ladder=false;
chipType=0;
skipRegisterWrites=false;
flushFirst=false;
for (int i=0; i<10; i++) {

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -25,6 +25,7 @@
class DivYM2612Interface: public ymfm::ymfm_interface {
int setA, setB;
int countA, countB;
public:
@ -32,8 +33,8 @@ class DivYM2612Interface: public ymfm::ymfm_interface {
void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks);
DivYM2612Interface():
ymfm::ymfm_interface(),
countA(-1),
countB(-1) {}
countA(0),
countB(0) {}
};
class DivPlatformGenesis: public DivPlatformOPN {
@ -84,7 +85,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
int softPCMTimer;
bool extMode, softPCM, noExtMacros, useYMFM;
bool ladder;
unsigned char chipType;
unsigned char dacVolTable[128];
@ -92,6 +93,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
friend void putDispatchChan(void*,int,int);
inline void processDAC(int iRate);
inline void commitState(int ch, DivInstrument* ins);
void acquire_nuked(short** buf, size_t len);
void acquire_ymfm(short** buf, size_t len);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -26,6 +26,44 @@
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
void DivPlatformGenesisExt::commitStateExt(int ch, DivInstrument* ins) {
int ordch=orderedOps[ch];
if (opChan[ch].insChanged) {
chan[2].state.alg=ins->fm.alg;
if (ch==0 || fbAllOps) {
chan[2].state.fb=ins->fm.fb;
}
chan[2].state.fms=ins->fm.fms;
chan[2].state.ams=ins->fm.ams;
chan[2].state.op[ordch]=ins->fm.op[ordch];
}
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
// TODO: how does this work?!
if (isOpMuted[ch] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+0x70,op.d2r&31);
rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+0x90,op.ssgEnv&15);
opChan[ch].mask=op.enable;
}
if (opChan[ch].insChanged) { // TODO how does this work?
rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
}
}
int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (c.chan<2) {
return DivPlatformGenesis::dispatch(c);
@ -44,16 +82,6 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (opChan[ch].insChanged) {
chan[2].state.alg=ins->fm.alg;
if (ch==0 || fbAllOps) {
chan[2].state.fb=ins->fm.fb;
}
chan[2].state.fms=ins->fm.fms;
chan[2].state.ams=ins->fm.ams;
chan[2].state.op[ordch]=ins->fm.op[ordch];
}
if (noExtMacros) {
opChan[ch].macroInit(NULL);
} else {
@ -62,30 +90,8 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (!opChan[ch].std.vol.will) {
opChan[ch].outVol=opChan[ch].vol;
}
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
// TODO: how does this work?!
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7));
rWrite(baseAddr+0x70,op.d2r&31);
rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
rWrite(baseAddr+0x90,op.ssgEnv&15);
opChan[ch].mask=op.enable;
}
if (opChan[ch].insChanged) { // TODO how does this work?
rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
}
commitStateExt(ch,ins);
opChan[ch].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -121,7 +127,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
}
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
if (isOpMuted[ch]) {
if (isOpMuted[ch] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
@ -202,6 +208,11 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
}
break;
case DIV_CMD_LEGATO: {
if (opChan[ch].insChanged) {
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
commitStateExt(ch,ins);
opChan[ch].insChanged=false;
}
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
opChan[ch].freqChanged=true;
break;
@ -232,7 +243,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOpMuted[ch]) {
if (isOpMuted[ch] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else if (KVS(2,c.value)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
@ -420,7 +431,7 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
int ordch=orderedOps[ch-2];
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2]) {
if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
@ -578,6 +589,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
immWrite(opChanOffsH[i],opChan[i].freq>>8);
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
opChan[i].freqChanged=false;
}
writeMask|=(unsigned char)(opChan[i].mask && opChan[i].active)<<(4+i);
if (opChan[i].keyOn) {
@ -640,10 +652,10 @@ void DivPlatformGenesisExt::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (i==2 && extMode) { // extended channel
if (isOpMuted[j]) {
if (isOpMuted[orderedOps[j]] || !op.enable) {
rWrite(baseAddr+0x40,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -29,6 +29,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
bool isOpMuted[4];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
inline void commitStateExt(int ch, DivInstrument* ins);
public:
int dispatch(DivCommand c);
void* getChanState(int chan);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -446,35 +446,34 @@ void DivPlatformOPL::tick(bool sysTick) {
}
}
}
}
if (i<melodicChans) {
if (chan[i].hardReset && chan[i].keyOn) {
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
oldWrites[baseAddr+ADDR_SL_RR]=-1;
oldWrites[baseAddr+ADDR_KSL_TL]=-1;
}
}
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
chan[i].keyOff=false;
}
if (chan[i].hardReset && chan[i].keyOn) {
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
for (int k=0; k<5; k++) {
immWrite(baseAddr+ADDR_SL_RR,0x0f);
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
}
int hardResetElapsed=0;
bool mustHardReset=false;
bool weWillWriteRRLater[64];
memset(weWillWriteRRLater,0,64*sizeof(bool));
for (int i=0; i<melodicChans; i++) {
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
if (chan[i].keyOn || chan[i].keyOff) {
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
chan[i].keyOff=false;
}
if (chan[i].hardReset && chan[i].keyOn) {
mustHardReset=true;
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
if (baseAddr>0x100) {
weWillWriteRRLater[(baseAddr&0xff)|32]=true;
} else {
weWillWriteRRLater[(baseAddr&0xff)]=true;
}
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
}
@ -562,6 +561,11 @@ void DivPlatformOPL::tick(bool sysTick) {
for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
if ((i>=0x80 && i<0xa0)) {
if (weWillWriteRRLater[i-0x80]) continue;
} else if ((i>=0x180 && i<0x1a0)) {
if (weWillWriteRRLater[32|(i-0x180)]) continue;
}
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
@ -580,11 +584,15 @@ void DivPlatformOPL::tick(bool sysTick) {
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
}
if (i<melodicChans) {
if (chan[i].keyOn) {
if (chan[i].keyOn && !chan[i].hardReset) {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
chan[i].keyOn=false;
} else if (chan[i].freqChanged) {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
if (chan[i].keyOn && chan[i].hardReset) {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|0);
} else {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
}
}
} else {
if (chan[i].keyOn) {
@ -602,6 +610,35 @@ void DivPlatformOPL::tick(bool sysTick) {
if (updateDrums) {
immWrite(0xbd,(dam<<7)|(dvb<<6)|(properDrums<<5)|drumState);
}
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<128; i++) {
immWrite(0x3f,i&0xff);
}
for (int i=0x80; i<0xa0; i++) {
if (weWillWriteRRLater[i-0x80]) {
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
}
for (int i=0x180; i<0x1a0; i++) {
if (weWillWriteRRLater[32|(i-0x180)]) {
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
}
for (int i=0; i<melodicChans; i++) {
if (chan[i].hardReset) {
if (chan[i].keyOn) {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
chan[i].keyOn=false;
} else if (chan[i].freqChanged) {
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
}
}
}
}
}
#define OPLL_C_NUM 686
@ -689,6 +726,112 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
}
}
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
chan[melodicChans+i+1].state.alg=ins->fm.alg;
chan[melodicChans+i+1].state.fb=ins->fm.fb;
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
}
} else {
chan[ch].state=ins->fm;
}
}
if (chan[ch].insChanged) {
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
int ch=melodicChans+1+i;
unsigned char slot=slots[0][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
chan[ch].fourOp=false;
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
} else {
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
chan[ch].fourOp=(ops==4);
if (chan[ch].fourOp) {
/*
if (chan[ch+1].active) {
chan[ch+1].keyOff=true;
chan[ch+1].keyOn=false;
chan[ch+1].active=false;
}*/
chan[ch+1].insChanged=true;
chan[ch+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[(ops==4)?orderedOpsL[i]:i];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (KVSL(ch,i) || ch>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1));
}
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
if (ops==4) {
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
}
}
}
int DivPlatformOPL::dispatch(DivCommand c) {
if (c.chan>=totalChans && c.chan!=adpcmChan) return 0;
// ineffective in 4-op mode
@ -771,114 +914,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL);
if (chan[c.chan].insChanged) {
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
chan[melodicChans+i+1].state.alg=ins->fm.alg;
chan[melodicChans+i+1].state.fb=ins->fm.fb;
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
}
} else {
chan[c.chan].state=ins->fm;
}
}
chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].insChanged) {
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
int ch=melodicChans+1+i;
unsigned char slot=slots[0][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
chan[ch].fourOp=false;
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
}
}
} else {
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) {
/*
if (chan[c.chan+1].active) {
chan[c.chan+1].keyOff=true;
chan[c.chan+1].keyOn=false;
chan[c.chan+1].active=false;
}*/
chan[c.chan+1].insChanged=true;
chan[c.chan+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][c.chan];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (KVSL(c.chan,i) || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
if (isMuted[c.chan]) {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
}
} else {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
}
}
}
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -1075,6 +1116,11 @@ int DivPlatformOPL::dispatch(DivCommand c) {
iface.sampleBank=sampleBank;
break;
case DIV_CMD_LEGATO: {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
@ -1509,7 +1555,11 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
}
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (ch>=totalChans) return NULL;
if (oplType==759) {
if (ch>=totalChans+1) return NULL;
} else {
if (ch>=totalChans) return NULL;
}
if (oplType==3 && ch<12) {
if (chan[ch&(~1)].fourOp) {
if (ch&1) {

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -100,6 +100,7 @@ class DivPlatformOPL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
double NOTE_ADPCMB(int note);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -19,6 +19,7 @@
#include "opll.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <string.h>
#include <math.h>
@ -331,6 +332,55 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
}
if (chan[ch].insChanged) {
// update custom preset
if (chan[ch].state.opllPreset==0) {
DivInstrumentFM::Operator& mod=chan[ch].state.op[0];
DivInstrumentFM::Operator& car=chan[ch].state.op[1];
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
rWrite(0x02,(mod.ksl<<6)|(mod.tl&63));
rWrite(0x03,(car.ksl<<6)|((chan[ch].state.fms&1)<<4)|((chan[ch].state.ams&1)<<3)|chan[ch].state.fb);
rWrite(0x04,(mod.ar<<4)|(mod.dr));
rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(0x06,(mod.sl<<4)|(mod.rr));
rWrite(0x07,(car.sl<<4)|(car.rr));
lastCustomMemory=ch;
}
if (chan[ch].state.opllPreset==16) { // compatible drums mode
if (ch>=6) {
drums=true;
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x18,0xC0);
immWrite(0x28,0x01);
}
} else {
if (ch>=6) {
if (drums) {
drums=false;
immWrite(0x0e,0);
drumState=0;
}
}
if (ch<9) {
rWrite(0x30+ch,((15-VOL_SCALE_LOG_BROKEN(chan[ch].outVol,15-chan[ch].state.op[1].tl,15))&15)|(chan[ch].state.opllPreset<<4));
}
}
}
}
int DivPlatformOPLL::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
@ -375,49 +425,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break;
}
if (chan[c.chan].insChanged) {
// update custom preset
if (chan[c.chan].state.opllPreset==0) {
DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0];
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
rWrite(0x02,(mod.ksl<<6)|(mod.tl&63));
rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb);
rWrite(0x04,(mod.ar<<4)|(mod.dr));
rWrite(0x05,(car.ar<<4)|(car.dr));
rWrite(0x06,(mod.sl<<4)|(mod.rr));
rWrite(0x07,(car.sl<<4)|(car.rr));
lastCustomMemory=c.chan;
}
if (chan[c.chan].state.opllPreset==16) { // compatible drums mode
if (c.chan>=6) {
drums=true;
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x16,0x20);
immWrite(0x26,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x17,0x50);
immWrite(0x27,0x05);
immWrite(0x18,0xC0);
immWrite(0x28,0x01);
}
} else {
if (c.chan>=6) {
if (drums) {
drums=false;
immWrite(0x0e,0);
drumState=0;
}
}
if (c.chan<9) {
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
}
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
@ -541,6 +549,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
}
case DIV_CMD_LEGATO: {
if (c.chan>=9 && !properDrums) return 0;
if (c.chan<6 || (!drums && !properDrums)) {
if (chan[c.chan].insChanged) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL);
commitState(c.chan,ins);
chan[c.chan].insChanged=false;
}
}
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -73,6 +73,7 @@ class DivPlatformOPLL: public DivDispatch {
int octave(int freq);
int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -163,21 +163,15 @@ void DivPlatformPCE::tick(bool sysTick) {
if (chan[i].std.duty.had && i>=4) {
chan[i].noise=chan[i].std.duty.val;
chan[i].freqChanged=true;
int noiseSeek=chan[i].note;
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
int noiseSeek=chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff);
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(noiseSeek);
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
chan[i].noiseSeek=noiseSeek;
}
chan[i].freqChanged=true;
}
@ -246,6 +240,15 @@ void DivPlatformPCE::tick(bool sysTick) {
if (chan[i].freq>4095) chan[i].freq=4095;
chWrite(i,0x02,chan[i].freq&0xff);
chWrite(i,0x03,chan[i].freq>>8);
if (i>=4) {
int noiseSeek=(chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff))+chan[i].pitch2;
if (!parent->song.properNoiseLayout && noiseSeek<0) noiseSeek=0;
if (!NEW_ARP_STRAT) {
noiseSeek=chan[i].noiseSeek;
}
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
}
if (chan[i].keyOn) {
//rWrite(16+i*5,0x80);
//chWrite(i,0x04,0x80|chan[i].vol);
@ -331,9 +334,8 @@ int DivPlatformPCE::dispatch(DivCommand c) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
int noiseSeek=chan[c.chan].note;
if (noiseSeek<0) noiseSeek=0;
chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
chan[c.chan].noiseSeek=c.value;
if (chan[c.chan].noiseSeek<0) chan[c.chan].noiseSeek=0;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
@ -431,7 +433,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
}
case DIV_CMD_STD_NOISE_MODE:
chan[c.chan].noise=c.value;
chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|chan[c.chan].note):0);
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_SAMPLE_MODE:
chan[c.chan].pcm=c.value;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -34,7 +34,7 @@ class DivPlatformPCE: public DivDispatch {
unsigned char pan;
bool noise, pcm, furnaceDac, deferredWaveUpdate;
signed short wave;
int macroVolMul;
int macroVolMul, noiseSeek;
DivWaveSynth ws;
Channel():
SharedChannel<signed char>(31),
@ -51,7 +51,8 @@ class DivPlatformPCE: public DivDispatch {
furnaceDac(false),
deferredWaveUpdate(false),
wave(-1),
macroVolMul(31) {}
macroVolMul(31),
noiseSeek(0) {}
};
Channel chan[6];
DivDispatchOscBuffer* oscBuf[6];

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -37,13 +37,12 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
continue;
}
if (chan[0].useWave || (chan[0].sample>=0 && chan[0].sample<parent->song.sampleLen)) {
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-(chan[0].freq>>16):(chan[0].freq>>16);
chan[0].audSub+=(chan[0].freq&0xffff);
if (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
}
chan[0].audSub+=chan[0].freq;
if (chan[0].useWave) {
while (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
}
if (chan[0].audPos>=(int)chan[0].audLen) {
chan[0].audPos%=chan[0].audLen;
chan[0].audDir=false;
@ -52,105 +51,123 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
} else {
DivSample* s=parent->getSample(chan[0].sample);
if (s->samples>0) {
if (chan[0].audDir) {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopStart+(s->loopStart-chan[0].audPos);
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopEnd-1-(s->loopStart-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos<0) {
chan[0].sample=-1;
}
break;
while (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
if (chan[0].audDir) {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopStart+(s->loopStart-chan[0].audPos);
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopEnd-1-(s->loopStart-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos<0) {
chan[0].sample=-1;
}
break;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
} else {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=(chan[0].audPos+s->loopStart)-s->loopEnd;
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=s->loopEnd-1-(s->loopEnd-1-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
break;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
} else {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=(chan[0].audPos+s->loopStart)-s->loopEnd;
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=s->loopEnd-1-(s->loopEnd-1-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
break;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
}
if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) {
int s_4=((chan[0].audPos-4)>=0)?s->data16[chan[0].audPos-4]:0;
int s_3=((chan[0].audPos-3)>=0)?s->data16[chan[0].audPos-3]:0;
int s_2=((chan[0].audPos-2)>=0)?s->data16[chan[0].audPos-2]:0;
int s_1=((chan[0].audPos-1)>=0)?s->data16[chan[0].audPos-1]:0;
int s0=s->data16[chan[0].audPos];
int s1=((chan[0].audPos+1)<(int)s->samples)?s->data16[chan[0].audPos+1]:0;
int s2=((chan[0].audPos+2)<(int)s->samples)?s->data16[chan[0].audPos+2]:0;
int s3=((chan[0].audPos+3)<(int)s->samples)?s->data16[chan[0].audPos+3]:0;
switch (interp) {
case 1: // linear
output=s0+((s1-s0)*(chan[0].audSub&0xffff)>>16);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
float result=(float)s_1*t[0]+(float)s0*t[1]+(float)s1*t[2]+(float)s2*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
float result=(
s_4*t2[3]+
s_3*t2[2]+
s_2*t2[1]+
s_1*t2[0]+
s0*t1[0]+
s1*t1[1]+
s2*t1[2]+
s3*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s0;
break;
chan[0].audDat[0]=chan[0].audDat[1];
chan[0].audDat[1]=chan[0].audDat[2];
chan[0].audDat[2]=chan[0].audDat[3];
chan[0].audDat[3]=chan[0].audDat[4];
chan[0].audDat[4]=chan[0].audDat[5];
chan[0].audDat[5]=chan[0].audDat[6];
chan[0].audDat[6]=chan[0].audDat[7];
if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) {
chan[0].audDat[7]=s->data16[chan[0].audPos];
} else {
chan[0].audDat[7]=0;
}
}
} else {
chan[0].sample=-1;
chan[0].audSub=0;
chan[0].audPos=0;
}
const short s0=chan[0].audDat[0];
const short s1=chan[0].audDat[1];
const short s2=chan[0].audDat[2];
const short s3=chan[0].audDat[3];
const short s4=chan[0].audDat[4];
const short s5=chan[0].audDat[5];
const short s6=chan[0].audDat[6];
const short s7=chan[0].audDat[7];
switch (interp) {
case 1: // linear
output=s6+((s7-s6)*(chan[0].audSub&0xffff)>>16);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
float result=(
s0*t2[3]+
s1*t2[2]+
s2*t2[1]+
s3*t2[0]+
s4*t1[0]+
s5*t1[1]+
s6*t1[2]+
s7*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s7;
break;
}
}
}
@ -266,6 +283,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
chan[0].audPos=0;
}
chan[0].audSub=0;
memset(chan[0].audDat,0,8*sizeof(short));
if (c.value!=DIV_NOTE_NULL) {
chan[0].freqChanged=true;
chan[0].note=c.value;
@ -408,6 +426,7 @@ void DivPlatformPCMDAC::reset() {
chan[0].std.setEngine(parent);
chan[0].ws.setEngine(parent);
chan[0].ws.init(NULL,32,255);
memset(chan[0].audDat,0,8*sizeof(short));
}
int DivPlatformPCMDAC::getOutputCount() {

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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
@ -29,6 +29,7 @@ class DivPlatformPCMDAC: public DivDispatch {
bool audDir;
unsigned int audLoc;
unsigned short audLen;
short audDat[8];
int audPos;
int audSub;
int sample, wave;
@ -41,6 +42,7 @@ class DivPlatformPCMDAC: public DivDispatch {
audDir(false),
audLoc(0),
audLen(0),
audDat{0,0,0,0,0,0,0,0},
audPos(0),
audSub(0),
sample(-1),

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 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

Some files were not shown because too many files have changed in this diff Show more