Merge branch 'master' of https://github.com/tildearrow/furnace into ymf289b
This commit is contained in:
commit
e694687fff
415 changed files with 152066 additions and 7336 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -67,6 +67,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"
|
||||
|
|
@ -74,6 +75,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"
|
||||
|
|
@ -436,6 +438,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);
|
||||
|
|
@ -485,6 +490,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;
|
||||
|
|
|
|||
|
|
@ -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,42 @@ 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
|
||||
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 +1995,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 +2125,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 +2474,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 +2551,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 +2683,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 +3582,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 +3617,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 +3632,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 +3640,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 +3669,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 +3684,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 +3703,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 +4273,7 @@ void DivEngine::quitDispatch() {
|
|||
clockDrift=0;
|
||||
chans=0;
|
||||
playing=false;
|
||||
speedAB=false;
|
||||
curSpeed=0;
|
||||
endOfSong=false;
|
||||
ticks=0;
|
||||
tempoAccum=0;
|
||||
|
|
@ -4415,6 +4452,8 @@ void DivEngine::preInit() {
|
|||
|
||||
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
|
||||
startLogFile(logPath.c_str());
|
||||
|
||||
logI("Furnace version " DIV_VERSION ".");
|
||||
|
||||
loadConf();
|
||||
}
|
||||
|
|
@ -4472,6 +4511,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)));
|
||||
|
|
|
|||
|
|
@ -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 "dev136"
|
||||
#define DIV_ENGINE_VERSION 136
|
||||
#define DIV_VERSION "dev142"
|
||||
#define DIV_ENGINE_VERSION 142
|
||||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1716,6 +1755,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<130) {
|
||||
ds.oldArpStrategy=true;
|
||||
}
|
||||
if (ds.version<138) {
|
||||
ds.brokenPortaLegato=true;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
|
@ -1739,8 +1781,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);
|
||||
|
|
@ -2221,6 +2264,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=136) song.patchbayAuto=reader.readC();
|
||||
|
||||
if (ds.version>=138) {
|
||||
ds.brokenPortaLegato=reader.readC();
|
||||
for (int i=0; i<7; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
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...");
|
||||
|
|
@ -2279,8 +2348,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);
|
||||
|
|
@ -2328,6 +2398,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2574,6 +2651,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// new YM2612/SN/X1-010 volumes
|
||||
if (ds.version<137) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
switch (ds.system[i]) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
case DIV_SYSTEM_YM2612_CSM:
|
||||
ds.systemVol[i]/=2.0;
|
||||
break;
|
||||
case DIV_SYSTEM_SMS:
|
||||
case DIV_SYSTEM_T6W28:
|
||||
case DIV_SYSTEM_OPLL:
|
||||
case DIV_SYSTEM_OPLL_DRUMS:
|
||||
ds.systemVol[i]/=1.5;
|
||||
break;
|
||||
case DIV_SYSTEM_X1_010:
|
||||
ds.systemVol[i]/=4.0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
@ -2920,7 +3023,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;
|
||||
|
|
@ -3399,8 +3501,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];
|
||||
|
|
@ -3417,10 +3519,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;
|
||||
|
|
@ -4307,8 +4407,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);
|
||||
|
|
@ -4489,6 +4590,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
w->writeC(song.patchbayAuto);
|
||||
|
||||
// even more compat flags
|
||||
w->writeC(song.brokenPortaLegato);
|
||||
for (int i=0; i<7; i++) {
|
||||
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);
|
||||
|
|
@ -4503,8 +4625,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);
|
||||
|
|
@ -4543,6 +4665,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);
|
||||
|
|
@ -4798,8 +4926,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];
|
||||
|
|
@ -4823,6 +4951,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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
void DivDispatch::acquire(short** buf, size_t len) {
|
||||
}
|
||||
|
|
@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) {
|
|||
}
|
||||
|
||||
void DivDispatch::notifyInsDeletion(void* ins) {
|
||||
|
||||
logE("notifyInsDeletion NOT implemented!");
|
||||
abort();
|
||||
}
|
||||
|
||||
void DivDispatch::notifyPlaybackStop() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -800,6 +841,12 @@ void DivPlatformArcade::notifyInsChange(int ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformArcade::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformArcade::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -76,6 +77,7 @@ class DivPlatformArcade: public DivPlatformOPM {
|
|||
void muteChannel(int ch, bool mute);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void setFlags(const DivConfig& flags);
|
||||
int getOutputCount();
|
||||
void setYMFM(bool use);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -131,6 +131,10 @@ int DivPlatformDummy::dispatch(DivCommand c) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformDummy::notifyInsDeletion(void* ins) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
void DivPlatformDummy::reset() {
|
||||
for (int i=0; i<chans; i++) {
|
||||
chan[i]=DivPlatformDummy::Channel();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -40,6 +40,7 @@ class DivPlatformDummy: public DivDispatch {
|
|||
void acquire(short** buf, size_t len);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int dispatch(DivCommand c);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
void reset();
|
||||
|
|
|
|||
1283
src/engine/platform/es5506.cpp
Normal file
1283
src/engine/platform/es5506.cpp
Normal file
File diff suppressed because it is too large
Load diff
327
src/engine/platform/es5506.h
Normal file
327
src/engine/platform/es5506.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -83,6 +85,7 @@ class DivPlatformFMBase: public DivDispatch {
|
|||
|
||||
unsigned char lastBusy;
|
||||
int delay;
|
||||
bool flushFirst;
|
||||
|
||||
unsigned char regPool[512];
|
||||
short oldWrites[512];
|
||||
|
|
@ -102,7 +105,7 @@ class DivPlatformFMBase: public DivDispatch {
|
|||
}
|
||||
}
|
||||
inline void urgentWrite(unsigned short a, unsigned char v) {
|
||||
if (!skipRegisterWrites) {
|
||||
if (!skipRegisterWrites && !flushFirst) {
|
||||
if (writes.empty()) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
} else if (writes.size()>16 || writes.front().addrOrVal) {
|
||||
|
|
@ -118,9 +121,11 @@ class DivPlatformFMBase: public DivDispatch {
|
|||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
DivPlatformFMBase():DivDispatch(),
|
||||
lastBusy(0),
|
||||
delay(0) {}
|
||||
DivPlatformFMBase():
|
||||
DivDispatch(),
|
||||
lastBusy(0),
|
||||
delay(0),
|
||||
flushFirst(false) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,23 +150,26 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
|||
|
||||
os[0]=0; os[1]=0;
|
||||
for (int i=0; i<6; i++) {
|
||||
if (!writes.empty() && --delay<0) {
|
||||
delay=0;
|
||||
QueuedWrite& w=writes.front();
|
||||
if (w.addrOrVal) {
|
||||
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
|
||||
//printf("write: %x = %.2x\n",w.addr,w.val);
|
||||
lastBusy=0;
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
} else {
|
||||
lastBusy++;
|
||||
if (fm.write_busy==0) {
|
||||
//printf("busycounter: %d\n",lastBusy);
|
||||
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
|
||||
w.addrOrVal=true;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<0) {
|
||||
delay=0;
|
||||
QueuedWrite& w=writes.front();
|
||||
if (w.addrOrVal) {
|
||||
//logV("%.3x = %.2x",w.addr,w.val);
|
||||
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
|
||||
lastBusy=0;
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
} else {
|
||||
lastBusy++;
|
||||
if (fm.write_busy==0) {
|
||||
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
|
||||
w.addrOrVal=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
flushFirst=false;
|
||||
}
|
||||
|
||||
OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1];
|
||||
|
|
@ -171,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;
|
||||
|
||||
|
|
@ -207,9 +219,11 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
lastBusy=1;
|
||||
} else {
|
||||
flushFirst=false;
|
||||
}
|
||||
|
||||
if (ladder) {
|
||||
if (chipType==1) {
|
||||
fm_ymfm->generate(&out_ymfm);
|
||||
} else {
|
||||
((ymfm::ym3438*)fm_ymfm)->generate(&out_ymfm);
|
||||
|
|
@ -220,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) {
|
||||
|
|
@ -229,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -283,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)) {
|
||||
|
|
@ -366,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)) {
|
||||
|
|
@ -389,6 +406,10 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
chan[i].state.ams=chan[i].std.ams.val;
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
}
|
||||
if (chan[i].std.ex3.had) {
|
||||
lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7));
|
||||
rWrite(0x22,lfoValue);
|
||||
}
|
||||
if (chan[i].std.ex4.had && chan[i].active) {
|
||||
chan[i].opMask=chan[i].std.ex4.val&15;
|
||||
chan[i].opMaskChanged=true;
|
||||
|
|
@ -423,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)) {
|
||||
|
|
@ -454,31 +475,28 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
|
||||
for (int i=0; i<512; i++) {
|
||||
if (pendingWrites[i]!=oldWrites[i]) {
|
||||
if (i==0x2b && pendingWrites[i]!=0 && !parent->song.brokenDACMode) {
|
||||
if (chan[5].keyOn) chan[5].keyOn=false;
|
||||
chan[5].keyOff=true;
|
||||
}
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
oldWrites[i]=pendingWrites[i];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -507,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;
|
||||
|
|
@ -525,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) {
|
||||
|
|
@ -540,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)) {
|
||||
|
|
@ -556,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: {
|
||||
|
|
@ -595,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;
|
||||
|
|
@ -638,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) {
|
||||
|
|
@ -728,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)) {
|
||||
|
|
@ -863,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;
|
||||
|
|
@ -901,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)) {
|
||||
|
|
@ -1098,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)) {
|
||||
|
|
@ -1117,16 +1174,20 @@ void DivPlatformGenesis::forceIns() {
|
|||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
if (i<5 || !chan[i].dacMode) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
immWrite(0x2b,0x00);
|
||||
//rWrite(0x2a,0x00);
|
||||
if (chan[5].dacMode) {
|
||||
chan[5].dacSample=-1;
|
||||
chan[6].dacSample=-1;
|
||||
rWrite(0x2b,0x80);
|
||||
}
|
||||
immWrite(0x22,lfoValue);
|
||||
flushFirst=true;
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::toggleRegisterDump(bool enable) {
|
||||
|
|
@ -1153,6 +1214,10 @@ int DivPlatformGenesis::getRegisterPoolSize() {
|
|||
return 512;
|
||||
}
|
||||
|
||||
float DivPlatformGenesis::getPostAmp() {
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::reset() {
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,512);
|
||||
|
|
@ -1160,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);
|
||||
}
|
||||
|
|
@ -1180,6 +1255,7 @@ void DivPlatformGenesis::reset() {
|
|||
lfoValue=8;
|
||||
softPCMTimer=0;
|
||||
extMode=false;
|
||||
flushFirst=false;
|
||||
|
||||
if (softPCM) {
|
||||
chan[5].dacMode=true;
|
||||
|
|
@ -1216,6 +1292,9 @@ void DivPlatformGenesis::notifyInsChange(int ins) {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<10; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::poke(unsigned int addr, unsigned short val) {
|
||||
|
|
@ -1256,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);
|
||||
|
|
@ -1280,8 +1373,9 @@ 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++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -114,10 +116,11 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
void setYMFM(bool use);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
bool keyOffAffectsPorta(int ch);
|
||||
float getPostAmp();
|
||||
void toggleRegisterDump(bool enable);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
virtual void notifyInsDeletion(void* ins);
|
||||
void setSoftPCM(bool value);
|
||||
int getPortaFloor(int ch);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -746,6 +758,13 @@ void DivPlatformGenesisExt::notifyInsChange(int ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformGenesisExt::notifyInsDeletion(void* ins) {
|
||||
DivPlatformGenesis::notifyInsDeletion(ins);
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGenesisExt::getPortaFloor(int ch) {
|
||||
return (ch>8)?12:0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -41,6 +42,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
|||
bool keyOffAffectsArp(int ch);
|
||||
bool keyOffAffectsPorta(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
int getPortaFloor(int ch);
|
||||
void setCSMChannel(unsigned char ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -353,6 +353,9 @@ void DivPlatformMSM6258::notifyInsChange(int ins) {
|
|||
}
|
||||
|
||||
void DivPlatformMSM6258::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::setFlags(const DivConfig& flags) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -326,6 +326,9 @@ void DivPlatformMSM6295::notifyInsChange(int ins) {
|
|||
}
|
||||
|
||||
void DivPlatformMSM6295::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
const void* DivPlatformMSM6295::getSampleMem(int index) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -455,35 +455,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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -571,6 +570,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];
|
||||
}
|
||||
|
|
@ -589,11 +593,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) {
|
||||
|
|
@ -611,6 +619,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
|
||||
|
|
@ -698,6 +735,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
|
||||
|
|
@ -780,114 +923,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) {
|
||||
|
|
@ -1084,6 +1125,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;
|
||||
|
|
@ -1518,7 +1564,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) {
|
||||
|
|
@ -1649,6 +1699,9 @@ void DivPlatformOPL::notifyInsChange(int ins) {
|
|||
}
|
||||
|
||||
void DivPlatformOPL::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformOPL::poke(unsigned int addr, unsigned short val) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -940,6 +955,9 @@ void DivPlatformOPLL::notifyInsChange(int ins) {
|
|||
}
|
||||
|
||||
void DivPlatformOPLL::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<11; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformOPLL::poke(unsigned int addr, unsigned short val) {
|
||||
|
|
@ -958,6 +976,10 @@ void DivPlatformOPLL::setYMFM(bool use) {
|
|||
useYMFM=use;
|
||||
}
|
||||
|
||||
float DivPlatformOPLL::getPostAmp() {
|
||||
return 1.5f;
|
||||
}
|
||||
|
||||
void DivPlatformOPLL::setFlags(const DivConfig& flags) {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
if (clockSel==3) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -95,6 +96,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
void setYMFM(bool use);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
bool keyOffAffectsPorta(int ch);
|
||||
float getPostAmp();
|
||||
void toggleRegisterDump(bool enable);
|
||||
void setVRC7(bool vrc);
|
||||
void setProperDrums(bool pd);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -606,7 +606,7 @@ void DivPlatformQSound::forceIns() {
|
|||
for (int i=0; i<19; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].sample=-1;
|
||||
//chan[i].sample=-1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -645,7 +645,7 @@ bool DivPlatformQSound::keyOffAffectsArp(int ch) {
|
|||
}
|
||||
|
||||
void DivPlatformQSound::notifyInsChange(int ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<19; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
|
@ -657,7 +657,7 @@ void DivPlatformQSound::notifyWaveChange(int wave) {
|
|||
}
|
||||
|
||||
void DivPlatformQSound::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<19; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,8 +73,8 @@ void DivPlatformSAA1099::acquire_saaSound(short** buf, size_t len) {
|
|||
saa_saaSound->GenerateMany((unsigned char*)saaBuf[0],len,oscBuf);
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
for (size_t i=0; i<len; i++) {
|
||||
buf[0][i]=(short)((((unsigned short)saaBuf[0][1+(i<<1)])<<8)|(((unsigned short)saaBuf[0][1+(i<<1)])>>8));
|
||||
buf[1][i]=(short)((((unsigned short)saaBuf[0][i<<1])<<8)|(((unsigned short)saaBuf[0][i<<1])>>8));
|
||||
buf[0][i]=(short)((((unsigned short)saaBuf[0][i<<1])<<8)|(((unsigned short)saaBuf[0][i<<1])>>8));
|
||||
buf[1][i]=(short)((((unsigned short)saaBuf[0][1+(i<<1)])<<8)|(((unsigned short)saaBuf[0][1+(i<<1)])>>8));
|
||||
}
|
||||
#else
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -82,9 +82,7 @@ const char** DivPlatformSCC::getRegisterSheet() {
|
|||
|
||||
void DivPlatformSCC::acquire(short** buf, size_t len) {
|
||||
for (size_t h=0; h<len; h++) {
|
||||
for (int i=0; i<16; i++) {
|
||||
scc->tick();
|
||||
}
|
||||
scc->tick(16);
|
||||
short out=(short)scc->out()<<5;
|
||||
buf[0][h]=out;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -23,52 +23,34 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
//#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWrite(c,a,v) rWrite(((c)<<3)+(a),v)
|
||||
|
||||
void DivPlatformSegaPCM::acquire(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
os[0]=0; os[1]=0;
|
||||
// do a PCM cycle
|
||||
pcmL=0; pcmR=0;
|
||||
for (int i=0; i<16; i++) {
|
||||
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
if (s->samples<=0) {
|
||||
chan[i].pcm.sample=-1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
continue;
|
||||
}
|
||||
if (!isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=s->data8[chan[i].pcm.pos>>8]*(chan[i].chVolL+chan[i].chVolR)>>1;
|
||||
pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL);
|
||||
pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR);
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
}
|
||||
chan[i].pcm.pos+=chan[i].pcm.freq;
|
||||
if (s->isLoopable() && chan[i].pcm.pos>=((unsigned int)s->loopEnd<<8)) {
|
||||
chan[i].pcm.pos=s->loopStart<<8;
|
||||
} else if (chan[i].pcm.pos>=(s->samples<<8)) {
|
||||
chan[i].pcm.sample=-1;
|
||||
}
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
}
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
pcm.write(w.addr,w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
os[0]=pcmL;
|
||||
pcm.sound_stream_update(os);
|
||||
|
||||
if (os[0]<-32768) os[0]=-32768;
|
||||
if (os[0]>32767) os[0]=32767;
|
||||
|
||||
os[1]=pcmR;
|
||||
if (os[1]<-32768) os[1]=-32768;
|
||||
if (os[1]>32767) os[1]=32767;
|
||||
|
||||
buf[0][h]=os[0];
|
||||
buf[1][h]=os[1];
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=pcm.lastOut[i][0]+pcm.lastOut[i][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +63,8 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
chan[i].outVol=(chan[i].vol*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
|
||||
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
|
||||
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10002+(i<<3),chan[i].chVolL);
|
||||
addWrite(0x10003+(i<<3),chan[i].chVolR);
|
||||
}
|
||||
rWrite(2+(i<<3),chan[i].chVolL);
|
||||
rWrite(3+(i<<3),chan[i].chVolR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,17 +80,13 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
if (parent->song.newSegaPCM) if (chan[i].std.panL.had) {
|
||||
chan[i].chPanL=chan[i].std.panL.val&127;
|
||||
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10002+(i<<3),chan[i].chVolL);
|
||||
}
|
||||
rWrite(2+(i<<3),chan[i].chVolL);
|
||||
}
|
||||
|
||||
if (parent->song.newSegaPCM) if (chan[i].std.panR.had) {
|
||||
chan[i].chPanR=chan[i].std.panR.val&127;
|
||||
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10003+(i<<3),chan[i].chVolR);
|
||||
}
|
||||
rWrite(3+(i<<3),chan[i].chVolR);
|
||||
}
|
||||
|
||||
if (chan[i].std.pitch.had) {
|
||||
|
|
@ -145,56 +121,48 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
off=(double)s->centerRate/8363.0;
|
||||
}
|
||||
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10007+(i<<3),chan[i].pcm.freq);
|
||||
}
|
||||
rWrite(7+(i<<3),chan[i].pcm.freq);
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
if (chan[i].keyOn && !chan[i].keyOff) {
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10086+(i<<3),3);
|
||||
}
|
||||
rWrite(0x86+(i<<3),3);
|
||||
chan[i].pcm.pos=0;
|
||||
if (chan[i].furnacePCM) {
|
||||
if (dumpWrites) { // Sega PCM writes
|
||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
if (actualLength>0xfeff) actualLength=0xfeff;
|
||||
addWrite(0x10086+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
addWrite(0x10084+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
|
||||
addWrite(0x10085+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
|
||||
addWrite(0x10006+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
|
||||
if (loopStart<0 || loopStart>=actualLength) {
|
||||
addWrite(0x10086+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+s->loopOffP;
|
||||
addWrite(0x10004+(i<<3),loopPos&0xff);
|
||||
addWrite(0x10005+(i<<3),(loopPos>>8)&0xff);
|
||||
addWrite(0x10086+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
}
|
||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
if (actualLength>0xfeff) actualLength=0xfeff;
|
||||
rWrite(0x86+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
|
||||
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
|
||||
rWrite(6+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
|
||||
if (loopStart<0 || loopStart>=actualLength) {
|
||||
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+sampleLoopOff[chan[i].pcm.sample];
|
||||
rWrite(4+(i<<3),loopPos&0xff);
|
||||
rWrite(5+(i<<3),(loopPos>>8)&0xff);
|
||||
rWrite(0x86+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
}
|
||||
} else {
|
||||
if (dumpWrites) { // Sega PCM writes
|
||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
if (actualLength>65536) actualLength=65536;
|
||||
addWrite(0x10086+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
addWrite(0x10084+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
|
||||
addWrite(0x10085+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
|
||||
addWrite(0x10006+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
|
||||
if (loopStart<0 || loopStart>=actualLength) {
|
||||
addWrite(0x10086+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+s->loopOffP;
|
||||
addWrite(0x10004+(i<<3),loopPos&0xff);
|
||||
addWrite(0x10005+(i<<3),(loopPos>>8)&0xff);
|
||||
addWrite(0x10086+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
}
|
||||
addWrite(0x10007+(i<<3),chan[i].pcm.freq);
|
||||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
if (actualLength>0xfeff) actualLength=0xfeff;
|
||||
rWrite(0x86+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
|
||||
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
|
||||
rWrite(6+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
|
||||
if (loopStart<0 || loopStart>=actualLength) {
|
||||
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+sampleLoopOff[chan[i].pcm.sample];
|
||||
rWrite(4+(i<<3),loopPos&0xff);
|
||||
rWrite(5+(i<<3),(loopPos>>8)&0xff);
|
||||
rWrite(0x86+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
|
||||
}
|
||||
rWrite(7+(i<<3),chan[i].pcm.freq);
|
||||
}
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
|
|
@ -206,6 +174,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
|
||||
void DivPlatformSegaPCM::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
pcm.mute(ch,mute);
|
||||
}
|
||||
|
||||
int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||
|
|
@ -219,9 +188,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.sample=-1;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10086+(c.chan<<3),3);
|
||||
}
|
||||
rWrite(0x86+(c.chan<<3),3);
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
|
|
@ -245,9 +212,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12;
|
||||
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.sample=-1;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10086+(c.chan<<3),3);
|
||||
}
|
||||
rWrite(0x86+(c.chan<<3),3);
|
||||
break;
|
||||
}
|
||||
chan[c.chan].pcm.freq=MIN(255,(parent->getSample(chan[c.chan].pcm.sample)->rate*255)/31250);
|
||||
|
|
@ -259,9 +224,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].pcm.sample=-1;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10086+(c.chan<<3),3);
|
||||
}
|
||||
rWrite(0x86+(c.chan<<3),3);
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
|
|
@ -288,10 +251,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].chVolL=c.value;
|
||||
chan[c.chan].chVolR=c.value;
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
|
||||
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
|
||||
}
|
||||
rWrite(2+(c.chan<<3),chan[c.chan].chVolL);
|
||||
rWrite(3+(c.chan<<3),chan[c.chan].chVolR);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
|
|
@ -314,10 +275,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].chVolL=c.value>>1;
|
||||
chan[c.chan].chVolR=c.value2>>1;
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
|
||||
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
|
||||
}
|
||||
rWrite(2+(c.chan<<3),chan[c.chan].chVolL);
|
||||
rWrite(3+(c.chan<<3),chan[c.chan].chVolR);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
|
@ -381,9 +340,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_SAMPLE_FREQ:
|
||||
chan[c.chan].pcm.freq=c.value;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq);
|
||||
}
|
||||
rWrite(7+(c.chan<<3),chan[c.chan].pcm.freq);
|
||||
break;
|
||||
default:
|
||||
//printf("WARNING: unimplemented command %d\n",c.cmd);
|
||||
|
|
@ -395,6 +352,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
void DivPlatformSegaPCM::forceIns() {
|
||||
for (int i=0; i<16; i++) {
|
||||
chan[i].insChanged=true;
|
||||
|
||||
rWrite(2+(i<<3),chan[i].chVolL);
|
||||
rWrite(3+(i<<3),chan[i].chVolR);
|
||||
rWrite(7+(i<<3),chan[i].pcm.freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +367,12 @@ void DivPlatformSegaPCM::notifyInsChange(int ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<16; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSegaPCM::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
|
@ -419,7 +386,7 @@ DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) {
|
|||
}
|
||||
|
||||
unsigned char* DivPlatformSegaPCM::getRegisterPool() {
|
||||
return regPool;
|
||||
return pcm.get_ram();
|
||||
}
|
||||
|
||||
int DivPlatformSegaPCM::getRegisterPoolSize() {
|
||||
|
|
@ -427,11 +394,29 @@ int DivPlatformSegaPCM::getRegisterPoolSize() {
|
|||
}
|
||||
|
||||
void DivPlatformSegaPCM::poke(unsigned int addr, unsigned short val) {
|
||||
//immWrite(addr,val);
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::poke(std::vector<DivRegWrite>& wlist) {
|
||||
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
const void* DivPlatformSegaPCM::getSampleMem(int index) {
|
||||
return index == 0 ? sampleMem : NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformSegaPCM::getSampleMemCapacity(int index) {
|
||||
return index == 0 ? 16777216 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformSegaPCM::getSampleMemUsage(int index) {
|
||||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
bool DivPlatformSegaPCM::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::reset() {
|
||||
|
|
@ -451,17 +436,20 @@ void DivPlatformSegaPCM::reset() {
|
|||
sampleBank=0;
|
||||
delay=0;
|
||||
|
||||
if (dumpWrites) {
|
||||
for (int i=0; i<16; i++) {
|
||||
addWrite(0x10086+(i<<3),3);
|
||||
addWrite(0x10002+(i<<3),0x7f);
|
||||
addWrite(0x10003+(i<<3),0x7f);
|
||||
}
|
||||
pcm.device_start();
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
rWrite(0x86+(i<<3),3);
|
||||
rWrite(2+(i<<3),0x7f);
|
||||
rWrite(3+(i<<3),0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::renderSamples(int sysID) {
|
||||
size_t memPos=0;
|
||||
|
||||
memset(sampleMem,0,16777216);
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* sample=parent->getSample(i);
|
||||
|
|
@ -471,6 +459,7 @@ void DivPlatformSegaPCM::reset() {
|
|||
memPos=(memPos+0xffff)&0xff0000;
|
||||
}
|
||||
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
|
||||
sampleLoaded[i]=true;
|
||||
if (memPos>=16777216) break;
|
||||
sampleOffSegaPCM[i]=memPos;
|
||||
unsigned int readPos=0;
|
||||
|
|
@ -478,20 +467,21 @@ void DivPlatformSegaPCM::reset() {
|
|||
if (readPos>=(unsigned int)sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
|
||||
if (sample->isLoopable()) {
|
||||
readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
memPos++;
|
||||
sampleMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||
} else {
|
||||
memPos++;
|
||||
sampleMem[memPos++]=0x80;
|
||||
}
|
||||
} else {
|
||||
memPos++;
|
||||
sampleMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||
}
|
||||
readPos++;
|
||||
if (memPos>=16777216) break;
|
||||
}
|
||||
sample->loopOffP=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
sampleLoopOff[i]=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
if (memPos>=16777216) break;
|
||||
}
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::setFlags(const DivConfig& flags) {
|
||||
chipClock=8000000.0;
|
||||
|
|
@ -514,6 +504,11 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, const DivC
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
sampleMem=new unsigned char[16777216];
|
||||
pcm.set_bank(segapcm_device::BANK_12M|segapcm_device::BANK_MASKF8);
|
||||
pcm.set_read([this](unsigned int addr) -> unsigned char {
|
||||
return sampleMem[addr&0xffffff];
|
||||
});
|
||||
setFlags(flags);
|
||||
reset();
|
||||
|
||||
|
|
@ -524,6 +519,7 @@ void DivPlatformSegaPCM::quit() {
|
|||
for (int i=0; i<16; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete sampleMem;
|
||||
}
|
||||
|
||||
DivPlatformSegaPCM::~DivPlatformSegaPCM() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
#include "../instrument.h"
|
||||
#include "sound/segapcm.h"
|
||||
#include <queue>
|
||||
|
||||
class DivPlatformSegaPCM: public DivDispatch {
|
||||
|
|
@ -52,6 +53,8 @@ class DivPlatformSegaPCM: public DivDispatch {
|
|||
};
|
||||
Channel chan[16];
|
||||
DivDispatchOscBuffer* oscBuf[16];
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
|
|
@ -59,6 +62,7 @@ class DivPlatformSegaPCM: public DivDispatch {
|
|||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
segapcm_device pcm;
|
||||
int delay;
|
||||
int pcmL, pcmR, pcmCycles;
|
||||
unsigned char sampleBank;
|
||||
|
|
@ -72,6 +76,8 @@ class DivPlatformSegaPCM: public DivDispatch {
|
|||
short pendingWrites[256];
|
||||
|
||||
unsigned int sampleOffSegaPCM[256];
|
||||
unsigned int sampleLoopOff[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
@ -89,11 +95,16 @@ class DivPlatformSegaPCM: public DivDispatch {
|
|||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void renderSamples(int chipID);
|
||||
void setFlags(const DivConfig& flags);
|
||||
int getOutputCount();
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const void* getSampleMem(int index=0);
|
||||
size_t getSampleMemCapacity(int index=0);
|
||||
size_t getSampleMemUsage(int index=0);
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformSegaPCM();
|
||||
|
|
|
|||
402
src/engine/platform/sm8521.cpp
Normal file
402
src/engine/platform/sm8521.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "sm8521.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 64
|
||||
|
||||
const char* regCheatSheetSM8521[]={
|
||||
"SGC", "40",
|
||||
"SG0L", "42",
|
||||
"SG1L", "44",
|
||||
"SG0TL", "46",
|
||||
"SG0TH", "47",
|
||||
"SG1TL", "48",
|
||||
"SG1TH", "49",
|
||||
"SG2L", "4A",
|
||||
"SG2TL", "4C",
|
||||
"SG2TH", "4D",
|
||||
"SGDA", "4E",
|
||||
"SG0Wn", "60+n",
|
||||
"SG1Wn", "70+n",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSM8521::getRegisterSheet() {
|
||||
return regCheatSheetSM8521;
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::acquire(short** buf, size_t len) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
sm8521_write(&sm8521,w.addr,w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
for (size_t h=0; h<len; h++) {
|
||||
sm8521_sound_tick(&sm8521,8);
|
||||
buf[0][h]=sm8521.out<<6;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<6;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<6;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::updateWave(int ch) {
|
||||
if (ch<2) {
|
||||
const unsigned char temp=regPool[0x40];
|
||||
rWrite(0x40,temp&~(1<<ch));
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=(chan[ch].ws.output[((i<<1)+chan[ch].antiClickWavePos-1)&31]-8)&0xf;
|
||||
int nibble2=(chan[ch].ws.output[((1+(i<<1))+chan[ch].antiClickWavePos-1)&31]-8)&0xf;
|
||||
rWrite(0x60+i+(ch*16),(nibble2<<4)|nibble1);
|
||||
}
|
||||
if (chan[ch].active) {
|
||||
rWrite(0x40,temp|(1<<ch));
|
||||
}
|
||||
chan[ch].antiClickWavePos&=31;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::tick(bool sysTick) {
|
||||
unsigned char keyState=0x80;
|
||||
for (int i=0; i<3; i++) {
|
||||
// anti-click
|
||||
if (antiClickEnabled && sysTick && chan[i].freq>0) {
|
||||
chan[i].antiClickPeriodCount+=(chipClock/MAX(parent->getCurHz(),1.0f));
|
||||
chan[i].antiClickWavePos+=chan[i].antiClickPeriodCount/chan[i].freq;
|
||||
chan[i].antiClickPeriodCount%=chan[i].freq;
|
||||
}
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5;
|
||||
if (!isMuted[i]) {
|
||||
chan[i].volumeChanged=true;
|
||||
}
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].volumeChanged) {
|
||||
if (isMuted[i]) {
|
||||
rWrite(volMap[i],0);
|
||||
} else {
|
||||
rWrite(volMap[i],chan[i].outVol&0x1f);
|
||||
}
|
||||
chan[i].volumeChanged=false;
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
|
||||
chan[i].antiClickWavePos=0;
|
||||
chan[i].antiClickPeriodCount=0;
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
rWrite(freqMap[i][0],chan[i].freq>>8);
|
||||
rWrite(freqMap[i][1],chan[i].freq&0xff);
|
||||
if (chan[i].keyOn) {
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (!isMuted[i] && chan[i].active) {
|
||||
keyState|=(1<<i);
|
||||
} else {
|
||||
keyState&=~(1<<i);
|
||||
}
|
||||
}
|
||||
if (regPool[0x40]!=keyState) {
|
||||
rWrite(0x40,keyState);
|
||||
regPool[0x40]=keyState;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSM8521::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,15,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
if (!isMuted[c.chan]) {
|
||||
chan[c.chan].volumeChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (!isMuted[c.chan]) {
|
||||
chan[c.chan].volumeChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:8);
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:8);
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 31;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chan[ch].volumeChanged=true;
|
||||
if (mute) {
|
||||
rWrite(0x40,regPool[0x40]&~(1<<ch));
|
||||
} else if (chan[ch].active) {
|
||||
rWrite(0x40,regPool[0x40]|0x80|(1<<ch));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::forceIns() {
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSM8521::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformSM8521::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSM8521::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformSM8521::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformSM8521::getRegisterPoolSize() {
|
||||
return 256;
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
memset(regPool,0,256);
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i]=DivPlatformSM8521::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,15,false);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
sm8521_reset(&sm8521);
|
||||
rWrite(0x40,0x80); // initialize SGC
|
||||
}
|
||||
|
||||
int DivPlatformSM8521::getOutputCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool DivPlatformSM8521::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<2; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::setFlags(const DivConfig& flags) {
|
||||
antiClickEnabled=!flags.getBool("noAntiClick",false);
|
||||
chipClock=11059200;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/4/8; // CKIN -> fCLK(/2) -> Function blocks (/2)
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformSM8521::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<3; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 6;
|
||||
}
|
||||
|
||||
void DivPlatformSM8521::quit() {
|
||||
for (int i=0; i<3; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformSM8521::~DivPlatformSM8521() {
|
||||
}
|
||||
86
src/engine/platform/sm8521.h
Normal file
86
src/engine/platform/sm8521.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* 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 _SM8521_H
|
||||
#define _SM8521_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/sm8521.h"
|
||||
|
||||
class DivPlatformSM8521: public DivDispatch {
|
||||
const unsigned char volMap[3]={0x42,0x44,0x4a};
|
||||
|
||||
const unsigned char freqMap[3][2]={{0x46,0x47},{0x48,0x49},{0x4c,0x4d}};
|
||||
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
int antiClickPeriodCount, antiClickWavePos;
|
||||
signed short wave;
|
||||
bool volumeChanged;
|
||||
DivWaveSynth ws;
|
||||
Channel():
|
||||
SharedChannel<signed char>(31),
|
||||
antiClickPeriodCount(0),
|
||||
antiClickWavePos(0),
|
||||
wave(-1),
|
||||
volumeChanged(false) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
bool isMuted[3];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
|
||||
bool antiClickEnabled;
|
||||
struct sm8521_t sm8521;
|
||||
unsigned char regPool[256];
|
||||
void updateWave(int ch);
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int getOutputCount();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformSM8521();
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue