Merge branch 'master' of https://github.com/tildearrow/furnace into quadtone
This commit is contained in:
commit
e0803d9bb1
420 changed files with 125299 additions and 7468 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
#include "platform/vrc6.h"
|
||||
#include "platform/fds.h"
|
||||
#include "platform/mmc5.h"
|
||||
#include "platform/es5506.h"
|
||||
#include "platform/scc.h"
|
||||
#include "platform/ymz280b.h"
|
||||
#include "platform/rf5c68.h"
|
||||
|
|
@ -75,6 +76,7 @@
|
|||
#include "platform/vb.h"
|
||||
#include "platform/k007232.h"
|
||||
#include "platform/ga20.h"
|
||||
#include "platform/sm8521.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
|
|
@ -440,6 +442,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_MMC5:
|
||||
dispatch=new DivPlatformMMC5;
|
||||
break;
|
||||
case DIV_SYSTEM_ES5506:
|
||||
dispatch=new DivPlatformES5506;
|
||||
break;
|
||||
case DIV_SYSTEM_SCC:
|
||||
dispatch=new DivPlatformSCC;
|
||||
((DivPlatformSCC*)dispatch)->setChipModel(false);
|
||||
|
|
@ -489,6 +494,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_GA20:
|
||||
dispatch=new DivPlatformGA20;
|
||||
break;
|
||||
case DIV_SYSTEM_SM8521:
|
||||
dispatch=new DivPlatformSM8521;
|
||||
break;
|
||||
case DIV_SYSTEM_PCM_DAC:
|
||||
dispatch=new DivPlatformPCMDAC;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0x08:
|
||||
return "08xy: Set panning (x: left; y: right)";
|
||||
case 0x09:
|
||||
return "09xx: Set speed 1";
|
||||
return "09xx: Set groove pattern (speed 1 if no grooves exist)";
|
||||
case 0x0a:
|
||||
return "0Axy: Volume slide (0y: down; x0: up)";
|
||||
case 0x0b:
|
||||
|
|
@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0x0d:
|
||||
return "0Dxx: Jump to next pattern";
|
||||
case 0x0f:
|
||||
return "0Fxx: Set speed 2";
|
||||
return "0Fxx: Set speed (speed 2 if no grooves exist)";
|
||||
case 0x80:
|
||||
return "80xx: Set panning (00: left; 80: center; FF: right)";
|
||||
case 0x81:
|
||||
|
|
@ -110,8 +110,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
return "EDxx: Note delay";
|
||||
case 0xee:
|
||||
return "EExx: Send external command";
|
||||
case 0xef:
|
||||
return "EFxx: Set global tuning (quirky!)";
|
||||
case 0xf0:
|
||||
return "F0xx: Set tick rate (bpm)";
|
||||
case 0xf1:
|
||||
|
|
@ -1471,6 +1469,43 @@ void DivEngine::createNew(const char* description, String sysName, bool inBase64
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::createNewFromDefaults() {
|
||||
quitDispatch();
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.unload();
|
||||
song=DivSong();
|
||||
changeSong(0);
|
||||
|
||||
String preset=getConfString("initialSys2","");
|
||||
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
|
||||
if (preset.empty()) {
|
||||
// try loading old preset
|
||||
logD("trying to load old preset");
|
||||
preset=decodeSysDesc(getConfString("initialSys",""));
|
||||
oldVol=false;
|
||||
}
|
||||
logD("preset size %ld",preset.size());
|
||||
if (preset.size()>0 && (preset.size()&3)==0) {
|
||||
initSongWithDesc(preset.c_str(),true,oldVol);
|
||||
}
|
||||
String sysName=getConfString("initialSysName","");
|
||||
if (sysName=="") {
|
||||
song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
|
||||
} else {
|
||||
song.systemName=sysName;
|
||||
}
|
||||
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
initDispatch();
|
||||
BUSY_BEGIN;
|
||||
renderSamples();
|
||||
reset();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::swapChannels(int src, int dest) {
|
||||
logV("swapping channel %d with %d",src,dest);
|
||||
if (src==dest) {
|
||||
|
|
@ -1961,14 +1996,12 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"cmdsPerSecond: %d\n"
|
||||
"globalPitch: %d\n"
|
||||
"extValue: %d\n"
|
||||
"speed1: %d\n"
|
||||
"speed2: %d\n"
|
||||
"tempoAccum: %d\n"
|
||||
"totalProcessed: %d\n"
|
||||
"bufferPos: %d\n",
|
||||
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
||||
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
(int)extValue,(int)speed1,(int)speed2,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2093,7 +2126,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
lastLoopPos=-1;
|
||||
}
|
||||
endOfSong=false;
|
||||
speedAB=false;
|
||||
// whaaaaa?
|
||||
curSpeed=0;
|
||||
playing=true;
|
||||
skipping=true;
|
||||
memset(walked,0,8192);
|
||||
|
|
@ -2441,15 +2475,14 @@ void DivEngine::reset() {
|
|||
}
|
||||
extValue=0;
|
||||
extValuePresent=0;
|
||||
speed1=curSubSong->speed1;
|
||||
speed2=curSubSong->speed2;
|
||||
speeds=curSubSong->speeds;
|
||||
firstTick=false;
|
||||
shallStop=false;
|
||||
shallStopSched=false;
|
||||
pendingMetroTick=0;
|
||||
elapsedBars=0;
|
||||
elapsedBeats=0;
|
||||
nextSpeed=speed1;
|
||||
nextSpeed=speeds.val[0];
|
||||
divider=60;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
|
|
@ -2519,6 +2552,8 @@ int DivEngine::getEffectiveSampleRate(int rate) {
|
|||
return (48828*MIN(128,(rate*128/48828)))/128;
|
||||
case DIV_SYSTEM_X1_010:
|
||||
return (31250*MIN(255,(rate*16/31250)))/16; // TODO: support variable clock case
|
||||
case DIV_SYSTEM_ES5506:
|
||||
return (31250*MIN(131071,(rate*2048/31250)))/2048; // TODO: support variable clock, channel limit case
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -2649,12 +2684,8 @@ size_t DivEngine::getCurrentSubSong() {
|
|||
return curSubSongIndex;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed1() {
|
||||
return speed1;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed2() {
|
||||
return speed2;
|
||||
const DivGroovePattern& DivEngine::getSpeeds() {
|
||||
return speeds;
|
||||
}
|
||||
|
||||
float DivEngine::getHz() {
|
||||
|
|
@ -3552,14 +3583,14 @@ void DivEngine::delSample(int index) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||
void DivEngine::addOrder(int pos, bool duplicate, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
memset(order,0,DIV_MAX_CHANS);
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][pos];
|
||||
}
|
||||
} else {
|
||||
bool used[DIV_MAX_PATTERNS];
|
||||
|
|
@ -3587,14 +3618,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
for (int j=curSubSong->ordersLen; j>pos; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][pos+1]=order[i];
|
||||
}
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (pos<=curOrder) curOrder++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
|
@ -3602,7 +3633,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
void DivEngine::deepCloneOrder(int pos, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
warnings="";
|
||||
|
|
@ -3610,7 +3641,7 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
bool didNotFind=true;
|
||||
logD("channel %d",i);
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][pos];
|
||||
// find free slot
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
logD("finding free slot in %d...",j);
|
||||
|
|
@ -3639,14 +3670,14 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
} else { // after current order
|
||||
saveLock.lock();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (int j=curSubSong->ordersLen; j>curOrder; j--) {
|
||||
for (int j=curSubSong->ordersLen; j>pos; j--) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j-1];
|
||||
}
|
||||
curOrders->ord[i][curOrder+1]=order[i];
|
||||
curOrders->ord[i][pos+1]=order[i];
|
||||
}
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (pos<=curOrder) curOrder++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
|
@ -3654,17 +3685,18 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::deleteOrder() {
|
||||
void DivEngine::deleteOrder(int pos) {
|
||||
if (curSubSong->ordersLen<=1) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
for (int j=curOrder; j<curSubSong->ordersLen; j++) {
|
||||
for (int j=pos; j<curSubSong->ordersLen; j++) {
|
||||
curOrders->ord[i][j]=curOrders->ord[i][j+1];
|
||||
}
|
||||
}
|
||||
curSubSong->ordersLen--;
|
||||
saveLock.unlock();
|
||||
if (curOrder>pos) curOrder--;
|
||||
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
|
|
@ -3672,40 +3704,46 @@ void DivEngine::deleteOrder() {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::moveOrderUp() {
|
||||
void DivEngine::moveOrderUp(int& pos) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder<1) {
|
||||
if (pos<1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
|
||||
curOrders->ord[i][pos-1]^=curOrders->ord[i][pos];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder--;
|
||||
if (curOrder==pos) {
|
||||
curOrder--;
|
||||
}
|
||||
pos--;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::moveOrderDown() {
|
||||
void DivEngine::moveOrderDown(int& pos) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder>=curSubSong->ordersLen-1) {
|
||||
if (pos>=curSubSong->ordersLen-1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
|
||||
curOrders->ord[i][pos+1]^=curOrders->ord[i][pos];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (curOrder==pos) {
|
||||
curOrder++;
|
||||
}
|
||||
pos++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
|
@ -4236,7 +4274,7 @@ void DivEngine::quitDispatch() {
|
|||
clockDrift=0;
|
||||
chans=0;
|
||||
playing=false;
|
||||
speedAB=false;
|
||||
curSpeed=0;
|
||||
endOfSong=false;
|
||||
ticks=0;
|
||||
tempoAccum=0;
|
||||
|
|
@ -4415,6 +4453,8 @@ void DivEngine::preInit() {
|
|||
|
||||
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
|
||||
startLogFile(logPath.c_str());
|
||||
|
||||
logI("Furnace version " DIV_VERSION ".");
|
||||
|
||||
loadConf();
|
||||
}
|
||||
|
|
@ -4429,6 +4469,7 @@ bool DivEngine::init() {
|
|||
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
|
||||
if (preset.empty()) {
|
||||
// try loading old preset
|
||||
logD("trying to load old preset");
|
||||
preset=decodeSysDesc(getConfString("initialSys",""));
|
||||
oldVol=false;
|
||||
}
|
||||
|
|
@ -4472,6 +4513,9 @@ bool DivEngine::init() {
|
|||
for (int i=0; i<64; i++) {
|
||||
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
|
||||
}
|
||||
for (int i=0; i<128; i++) {
|
||||
tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI)));
|
||||
}
|
||||
for (int i=0; i<4096; i++) {
|
||||
reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0)));
|
||||
pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0)));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -47,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev138"
|
||||
#define DIV_ENGINE_VERSION 138
|
||||
#define DIV_VERSION "0.6pre4-hotfix"
|
||||
#define DIV_ENGINE_VERSION 144
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -100,6 +100,7 @@ struct DivChannelState {
|
|||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||
bool wentThroughNote, goneThroughNote;
|
||||
|
||||
int midiNote, curMidiNote, midiPitch;
|
||||
size_t midiAge;
|
||||
|
|
@ -152,6 +153,8 @@ struct DivChannelState {
|
|||
wasShorthandPorta(false),
|
||||
noteOnInhibit(false),
|
||||
resetArp(false),
|
||||
wentThroughNote(false),
|
||||
goneThroughNote(false),
|
||||
midiNote(-1),
|
||||
curMidiNote(-1),
|
||||
midiPitch(-1),
|
||||
|
|
@ -337,7 +340,6 @@ class DivEngine {
|
|||
bool playing;
|
||||
bool freelance;
|
||||
bool shallStop, shallStopSched;
|
||||
bool speedAB;
|
||||
bool endOfSong;
|
||||
bool consoleMode;
|
||||
bool extValuePresent;
|
||||
|
|
@ -359,7 +361,7 @@ class DivEngine {
|
|||
bool midiOutClock;
|
||||
int midiOutMode;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
|
||||
size_t curSubSongIndex;
|
||||
size_t bufferPos;
|
||||
double divider;
|
||||
|
|
@ -368,7 +370,7 @@ class DivEngine {
|
|||
int stepPlay;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
unsigned char extValue, pendingMetroTick;
|
||||
unsigned char speed1, speed2;
|
||||
DivGroovePattern speeds;
|
||||
short tempoAccum;
|
||||
DivStatusView view;
|
||||
DivHaltPositions haltOn;
|
||||
|
|
@ -391,9 +393,9 @@ class DivEngine {
|
|||
std::vector<String> midiOuts;
|
||||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<DivInstrumentType> possibleInsTypes;
|
||||
static DivSysDef* sysDefs[256];
|
||||
static DivSystem sysFileMapFur[256];
|
||||
static DivSystem sysFileMapDMF[256];
|
||||
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
||||
struct SamplePreview {
|
||||
double rate;
|
||||
|
|
@ -413,6 +415,7 @@ class DivEngine {
|
|||
} sPreview;
|
||||
|
||||
short vibTable[64];
|
||||
short tremTable[128];
|
||||
int reversePitchTable[4096];
|
||||
int pitchTable[4096];
|
||||
char c163NameCS[1024];
|
||||
|
|
@ -513,6 +516,7 @@ class DivEngine {
|
|||
String decodeSysDesc(String desc);
|
||||
// start fresh
|
||||
void createNew(const char* description, String sysName, bool inBase64=true);
|
||||
void createNewFromDefaults();
|
||||
// load a file.
|
||||
bool load(unsigned char* f, size_t length);
|
||||
// save as .dmf.
|
||||
|
|
@ -524,7 +528,12 @@ class DivEngine {
|
|||
// specify system to build ROM for.
|
||||
SafeWriter* buildROM(int sys);
|
||||
// dump to VGM.
|
||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false);
|
||||
// set trailingTicks to:
|
||||
// - 0 to add one tick of trailing
|
||||
// - x to add x+1 ticks of trailing
|
||||
// - -1 to auto-determine trailing
|
||||
// - -2 to add a whole loop of trailing
|
||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
|
||||
// dump to ZSM.
|
||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
||||
// dump command stream.
|
||||
|
|
@ -581,6 +590,9 @@ class DivEngine {
|
|||
void setConf(String key, const char* value);
|
||||
void setConf(String key, String value);
|
||||
|
||||
// get whether config value exists
|
||||
bool hasConf(String key);
|
||||
|
||||
// calculate base frequency/period
|
||||
double calcBaseFreq(double clock, double divider, int note, bool period);
|
||||
|
||||
|
|
@ -729,11 +741,8 @@ class DivEngine {
|
|||
// get current subsong
|
||||
size_t getCurrentSubSong();
|
||||
|
||||
// get speed 1
|
||||
unsigned char getSpeed1();
|
||||
|
||||
// get speed 2
|
||||
unsigned char getSpeed2();
|
||||
// get speeds
|
||||
const DivGroovePattern& getSpeeds();
|
||||
|
||||
// get Hz
|
||||
float getHz();
|
||||
|
|
@ -813,19 +822,19 @@ class DivEngine {
|
|||
void delSample(int index);
|
||||
|
||||
// add order
|
||||
void addOrder(bool duplicate, bool where);
|
||||
void addOrder(int pos, bool duplicate, bool where);
|
||||
|
||||
// deep clone orders
|
||||
void deepCloneOrder(bool where);
|
||||
void deepCloneOrder(int pos, bool where);
|
||||
|
||||
// delete order
|
||||
void deleteOrder();
|
||||
void deleteOrder(int pos);
|
||||
|
||||
// move order up
|
||||
void moveOrderUp();
|
||||
void moveOrderUp(int& pos);
|
||||
|
||||
// move order down
|
||||
void moveOrderDown();
|
||||
void moveOrderDown(int& pos);
|
||||
|
||||
// move thing up
|
||||
bool moveInsUp(int which);
|
||||
|
|
@ -1064,7 +1073,6 @@ class DivEngine {
|
|||
freelance(false),
|
||||
shallStop(false),
|
||||
shallStopSched(false),
|
||||
speedAB(false),
|
||||
endOfSong(false),
|
||||
consoleMode(false),
|
||||
extValuePresent(false),
|
||||
|
|
@ -1098,6 +1106,7 @@ class DivEngine {
|
|||
nextSpeed(3),
|
||||
elapsedBars(0),
|
||||
elapsedBeats(0),
|
||||
curSpeed(0),
|
||||
curSubSongIndex(0),
|
||||
bufferPos(0),
|
||||
divider(60),
|
||||
|
|
@ -1115,8 +1124,6 @@ class DivEngine {
|
|||
globalPitch(0),
|
||||
extValue(0),
|
||||
pendingMetroTick(0),
|
||||
speed1(3),
|
||||
speed2(3),
|
||||
tempoAccum(0),
|
||||
view(DIV_STATUS_NOTHING),
|
||||
haltOn(DIV_HALT_NONE),
|
||||
|
|
@ -1158,13 +1165,14 @@ class DivEngine {
|
|||
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
|
||||
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));
|
||||
memset(vibTable,0,64*sizeof(short));
|
||||
memset(tremTable,0,128*sizeof(short));
|
||||
memset(reversePitchTable,0,4096*sizeof(int));
|
||||
memset(pitchTable,0,4096*sizeof(int));
|
||||
memset(sysDefs,0,256*sizeof(void*));
|
||||
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
|
||||
memset(walked,0,8192);
|
||||
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIP_DEFS; i++) {
|
||||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
ds.version=(unsigned char)reader.readC();
|
||||
logI("module version %d (0x%.2x)",ds.version,ds.version);
|
||||
if (ds.version>0x1a) {
|
||||
if (ds.version>0x1b) {
|
||||
logE("this version is not supported by Furnace yet!");
|
||||
lastError="this version is not supported by Furnace yet";
|
||||
delete[] file;
|
||||
|
|
@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
ds.subsong[0]->timeBase=reader.readC();
|
||||
ds.subsong[0]->speed1=reader.readC();
|
||||
ds.subsong[0]->speeds.len=2;
|
||||
ds.subsong[0]->speeds.val[0]=reader.readC();
|
||||
if (ds.version>0x07) {
|
||||
ds.subsong[0]->speed2=reader.readC();
|
||||
ds.subsong[0]->speeds.val[1]=reader.readC();
|
||||
ds.subsong[0]->pal=reader.readC();
|
||||
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||
ds.subsong[0]->customTempo=reader.readC();
|
||||
} else {
|
||||
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
||||
ds.subsong[0]->speeds.len=1;
|
||||
}
|
||||
if (ds.version>0x0a) {
|
||||
String hz=reader.readString(3);
|
||||
|
|
@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
for (int i=0; i<ds.sampleLen; i++) {
|
||||
DivSample* sample=new DivSample;
|
||||
int length=reader.readI();
|
||||
int cutStart=0;
|
||||
int cutEnd=length;
|
||||
int pitch=5;
|
||||
int vol=50;
|
||||
short* data;
|
||||
|
|
@ -866,6 +869,29 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
|
||||
}
|
||||
}
|
||||
if (ds.version>=0x1b) {
|
||||
// what the hell man...
|
||||
cutStart=reader.readI();
|
||||
cutEnd=reader.readI();
|
||||
if (cutStart<0 || cutStart>length) {
|
||||
logE("cutStart is out of range! (%d)",cutStart);
|
||||
lastError="file is corrupt or unreadable at samples";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (cutEnd<0 || cutEnd>length) {
|
||||
logE("cutEnd is out of range! (%d)",cutEnd);
|
||||
lastError="file is corrupt or unreadable at samples";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (cutEnd<cutStart) {
|
||||
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
|
||||
lastError="file is corrupt or unreadable at samples";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (length>0) {
|
||||
if (ds.version>0x08) {
|
||||
if (ds.version<0x0b) {
|
||||
|
|
@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
data=new short[length];
|
||||
reader.read(data,length*2);
|
||||
}
|
||||
|
||||
if (ds.version>0x1b) {
|
||||
if (cutStart!=0 || cutEnd!=length) {
|
||||
// cut data
|
||||
short* newData=new short[cutEnd-cutStart];
|
||||
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
|
||||
delete[] data;
|
||||
data=newData;
|
||||
length=cutEnd-cutStart;
|
||||
cutStart=0;
|
||||
cutEnd=length;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert to big-endian
|
||||
|
|
@ -1743,8 +1782,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.readI();
|
||||
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->speeds.len=2;
|
||||
subSong->speeds.val[0]=reader.readC();
|
||||
subSong->speeds.val[1]=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
|
|
@ -2232,6 +2272,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ds.version>=139) {
|
||||
subSong->speeds.len=reader.readC();
|
||||
for (int i=0; i<16; i++) {
|
||||
subSong->speeds.val[i]=reader.readC();
|
||||
}
|
||||
|
||||
// grooves
|
||||
unsigned char grooveCount=reader.readC();
|
||||
for (int i=0; i<grooveCount; i++) {
|
||||
DivGroovePattern gp;
|
||||
gp.len=reader.readC();
|
||||
for (int j=0; j<16; j++) {
|
||||
gp.val[j]=reader.readC();
|
||||
}
|
||||
|
||||
ds.grooves.push_back(gp);
|
||||
}
|
||||
}
|
||||
|
||||
// read system flags
|
||||
if (ds.version>=119) {
|
||||
logD("reading chip flags...");
|
||||
|
|
@ -2290,8 +2349,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
subSong=ds.subsong[i+1];
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->speeds.len=2;
|
||||
subSong->speeds.val[0]=reader.readC();
|
||||
subSong->speeds.val[1]=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
|
|
@ -2339,6 +2399,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
for (int i=0; i<tchans; i++) {
|
||||
subSong->chanShortName[i]=reader.readString();
|
||||
}
|
||||
|
||||
if (ds.version>=139) {
|
||||
subSong->speeds.len=reader.readC();
|
||||
for (int i=0; i<16; i++) {
|
||||
subSong->speeds.val[i]=reader.readC();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2957,7 +3024,6 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
if (fxVal>0x20 && ds.name!="klisje paa klisje") {
|
||||
writeFxCol(0xf0,fxVal);
|
||||
} else {
|
||||
writeFxCol(0x09,fxVal);
|
||||
writeFxCol(0x0f,fxVal);
|
||||
}
|
||||
break;
|
||||
|
|
@ -3436,8 +3502,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ds.subsong[0]->pal=true;
|
||||
ds.subsong[0]->customTempo=true;
|
||||
ds.subsong[0]->pat[3].effectCols=3;
|
||||
ds.subsong[0]->speed1=3;
|
||||
ds.subsong[0]->speed2=3;
|
||||
ds.subsong[0]->speeds.val[0]=3;
|
||||
ds.subsong[0]->speeds.len=1;
|
||||
|
||||
int lastIns[4];
|
||||
int lastNote[4];
|
||||
|
|
@ -3454,10 +3520,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ds.subsong[0]->orders.ord[j][i]=i;
|
||||
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
|
||||
if (j==3 && seq[i].speed) {
|
||||
p->data[0][6]=0x09;
|
||||
p->data[0][6]=0x0f;
|
||||
p->data[0][7]=seq[i].speed;
|
||||
p->data[0][8]=0x0f;
|
||||
p->data[0][9]=seq[i].speed;
|
||||
}
|
||||
|
||||
bool ignoreNext=false;
|
||||
|
|
@ -4344,8 +4408,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
// these are for compatibility
|
||||
w->writeC(subSong->speeds.val[0]);
|
||||
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
|
|
@ -4532,6 +4597,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeC(0);
|
||||
}
|
||||
|
||||
// speeds of first song
|
||||
w->writeC(subSong->speeds.len);
|
||||
for (int i=0; i<16; i++) {
|
||||
w->writeC(subSong->speeds.val[i]);
|
||||
}
|
||||
|
||||
// groove list
|
||||
w->writeC((unsigned char)song.grooves.size());
|
||||
for (const DivGroovePattern& i: song.grooves) {
|
||||
w->writeC(i.len);
|
||||
for (int j=0; j<16; j++) {
|
||||
w->writeC(i.val[j]);
|
||||
}
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
|
@ -4546,8 +4626,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->speeds.val[0]);
|
||||
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
|
|
@ -4586,6 +4666,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
|
||||
// speeds
|
||||
w->writeC(subSong->speeds.len);
|
||||
for (int i=0; i<16; i++) {
|
||||
w->writeC(subSong->speeds.val[i]);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
|
@ -4841,8 +4927,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
w->writeC(curSubSong->hilightB);
|
||||
|
||||
w->writeC(curSubSong->timeBase);
|
||||
w->writeC(curSubSong->speed1);
|
||||
w->writeC(curSubSong->speed2);
|
||||
w->writeC(curSubSong->speeds.val[0]);
|
||||
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
|
||||
w->writeC(curSubSong->pal);
|
||||
w->writeC(curSubSong->customTempo);
|
||||
char customHz[4];
|
||||
|
|
@ -4866,6 +4952,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
addWarning("only the currently selected subsong will be saved");
|
||||
}
|
||||
|
||||
if (!song.grooves.empty()) {
|
||||
addWarning("grooves will not be saved");
|
||||
}
|
||||
|
||||
if (curSubSong->speeds.len>2) {
|
||||
addWarning("only the first two speeds will be effective");
|
||||
}
|
||||
|
||||
if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
|
||||
addWarning(".dmf format does not support virtual tempo");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,18 +183,18 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
|||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7;
|
||||
}
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
|
||||
}
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
|
||||
}
|
||||
}
|
||||
|
||||
os[0]=(os[0]<<5);
|
||||
if (chipType!=2) os[0]=(os[0]<<5);
|
||||
if (os[0]<-32768) os[0]=-32768;
|
||||
if (os[0]>32767) os[0]=32767;
|
||||
|
||||
os[1]=(os[1]<<5);
|
||||
if (chipType!=2) os[1]=(os[1]<<5);
|
||||
if (os[1]<-32768) os[1]=-32768;
|
||||
if (os[1]>32767) os[1]=32767;
|
||||
|
||||
|
|
@ -214,7 +223,7 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
flushFirst=false;
|
||||
}
|
||||
|
||||
if (ladder) {
|
||||
if (chipType==1) {
|
||||
fm_ymfm->generate(&out_ymfm);
|
||||
} else {
|
||||
((ymfm::ym3438*)fm_ymfm)->generate(&out_ymfm);
|
||||
|
|
@ -225,6 +234,9 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
//OPN2_Write(&fm,0,0);
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
|
||||
if (chOut<-32768) chOut=-32768;
|
||||
if (chOut>32767) chOut=32767;
|
||||
if (i==5) {
|
||||
if (fm_ymfm->debug_dac_enable()) {
|
||||
if (softPCM) {
|
||||
|
|
@ -234,10 +246,10 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7;
|
||||
}
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
|
||||
}
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +300,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (i<6) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
if (isMuted[i] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
|
|
@ -371,7 +383,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
if (isMuted[i] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
|
|
@ -432,7 +444,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isMuted[i]) {
|
||||
if (isMuted[i] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
|
|
@ -472,26 +484,19 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
immWrite(0x28,0x00|konOffs[i]);
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_TL,0x7f);
|
||||
oldWrites[baseAddr+ADDR_SL_RR]=-1;
|
||||
oldWrites[baseAddr+ADDR_TL]=-1;
|
||||
//rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
}
|
||||
immWrite(0x28,0x00|konOffs[i]);
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
for (int k=0; k<5; k++) {
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
}
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
chan[i].keyOff=false;
|
||||
|
|
@ -520,6 +525,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (i<6) {
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
|
||||
hardResetElapsed+=2;
|
||||
}
|
||||
if (chan[i].furnaceDac && chan[i].dacMode) {
|
||||
double off=1.0;
|
||||
|
|
@ -538,12 +544,38 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].keyOn || chan[i].opMaskChanged) {
|
||||
if (i<6) immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
|
||||
if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) {
|
||||
if (i<6) {
|
||||
immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
chan[i].opMaskChanged=false;
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
|
||||
immWrite(0xf0,i&0xff);
|
||||
}
|
||||
for (int i=0; i<csmChan; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
|
||||
if (i<6) {
|
||||
// restore SL/RR
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]);
|
||||
}
|
||||
chan[i].opMaskChanged=false;
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
||||
|
|
@ -553,7 +585,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
|
||||
if (isMuted[ch]) {
|
||||
if (isMuted[ch] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(ch,j)) {
|
||||
|
|
@ -569,6 +601,47 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
|||
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::commitState(int ch, DivInstrument* ins) {
|
||||
if (chan[ch].insChanged) {
|
||||
chan[ch].state=ins->fm;
|
||||
chan[ch].opMask=
|
||||
(chan[ch].state.op[0].enable?1:0)|
|
||||
(chan[ch].state.op[2].enable?2:0)|
|
||||
(chan[ch].state.op[1].enable?4:0)|
|
||||
(chan[ch].state.op[3].enable?8:0);
|
||||
}
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[ch]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[i];
|
||||
if (isMuted[ch] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(ch,i)) {
|
||||
if (!chan[ch].active || chan[ch].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127));
|
||||
}
|
||||
} else {
|
||||
if (chan[ch].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[ch].insChanged) {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
if (chan[ch].insChanged) {
|
||||
rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3));
|
||||
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
|
|
@ -608,7 +681,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
} else {
|
||||
rWrite(0x2b,1<<7);
|
||||
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffff0000,chan[c.chan].dacSample);
|
||||
addWrite(0xffff0003,chan[c.chan].dacDirection);
|
||||
}
|
||||
}
|
||||
chan[c.chan].dacPos=0;
|
||||
chan[c.chan].dacPeriod=0;
|
||||
|
|
@ -651,49 +727,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
if (c.chan>=6) break;
|
||||
|
||||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].state=ins->fm;
|
||||
chan[c.chan].opMask=
|
||||
(chan[c.chan].state.op[0].enable?1:0)|
|
||||
(chan[c.chan].state.op[2].enable?2:0)|
|
||||
(chan[c.chan].state.op[1].enable?4:0)|
|
||||
(chan[c.chan].state.op[3].enable?8:0);
|
||||
}
|
||||
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
} else {
|
||||
if (chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
|
||||
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
|
||||
}
|
||||
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
|
|
@ -741,7 +780,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isMuted[c.chan]) {
|
||||
if (isMuted[c.chan] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(c.chan,i)) {
|
||||
|
|
@ -876,6 +915,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
} else {
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
}
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -914,7 +958,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isMuted[c.chan]) {
|
||||
if (isMuted[c.chan] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
|
|
@ -1111,7 +1155,7 @@ void DivPlatformGenesis::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
if (isMuted[i] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
|
|
@ -1181,7 +1225,17 @@ void DivPlatformGenesis::reset() {
|
|||
fm_ymfm->reset();
|
||||
}
|
||||
OPN2_Reset(&fm);
|
||||
OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0);
|
||||
switch (chipType) {
|
||||
case 1: // YM2612
|
||||
OPN2_SetChipType(&fm,ym3438_mode_ym2612);
|
||||
break;
|
||||
case 2: // YMF276
|
||||
OPN2_SetChipType(&fm,ym3438_mode_opn);
|
||||
break;
|
||||
default: // YM3438
|
||||
OPN2_SetChipType(&fm,0);
|
||||
break;
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
|
@ -1281,14 +1335,28 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
|
|||
chipClock=COLOR_NTSC*15.0/7.0;
|
||||
break;
|
||||
}
|
||||
ladder=flags.getBool("ladderEffect",false);
|
||||
if (flags.has("chipType")) {
|
||||
chipType=flags.getInt("chipType",0);
|
||||
} else {
|
||||
chipType=flags.getBool("ladderEffect",false)?1:0;
|
||||
}
|
||||
noExtMacros=flags.getBool("noExtMacros",false);
|
||||
fbAllOps=flags.getBool("fbAllOps",false);
|
||||
OPN2_SetChipType(&fm,ladder?ym3438_mode_ym2612:0);
|
||||
switch (chipType) {
|
||||
case 1: // YM2612
|
||||
OPN2_SetChipType(&fm,ym3438_mode_ym2612);
|
||||
break;
|
||||
case 2: // YMF276
|
||||
OPN2_SetChipType(&fm,ym3438_mode_opn);
|
||||
break;
|
||||
default: // YM3438
|
||||
OPN2_SetChipType(&fm,0);
|
||||
break;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
if (useYMFM) {
|
||||
if (fm_ymfm!=NULL) delete fm_ymfm;
|
||||
if (ladder) {
|
||||
if (chipType==1) {
|
||||
fm_ymfm=new ymfm::ym2612(iface);
|
||||
} else {
|
||||
fm_ymfm=new ymfm::ym3438(iface);
|
||||
|
|
@ -1305,7 +1373,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
|
|||
int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
ladder=false;
|
||||
chipType=0;
|
||||
skipRegisterWrites=false;
|
||||
flushFirst=false;
|
||||
for (int i=0; i<10; i++) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -446,35 +446,34 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i<melodicChans) {
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
oldWrites[baseAddr+ADDR_SL_RR]=-1;
|
||||
oldWrites[baseAddr+ADDR_KSL_TL]=-1;
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||
for (int k=0; k<5; k++) {
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
}
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
bool weWillWriteRRLater[64];
|
||||
|
||||
memset(weWillWriteRRLater,0,64*sizeof(bool));
|
||||
|
||||
for (int i=0; i<melodicChans; i++) {
|
||||
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
if (baseAddr>0x100) {
|
||||
weWillWriteRRLater[(baseAddr&0xff)|32]=true;
|
||||
} else {
|
||||
weWillWriteRRLater[(baseAddr&0xff)]=true;
|
||||
}
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -562,6 +561,11 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
|
||||
for (int i=0; i<512; i++) {
|
||||
if (pendingWrites[i]!=oldWrites[i]) {
|
||||
if ((i>=0x80 && i<0xa0)) {
|
||||
if (weWillWriteRRLater[i-0x80]) continue;
|
||||
} else if ((i>=0x180 && i<0x1a0)) {
|
||||
if (weWillWriteRRLater[32|(i-0x180)]) continue;
|
||||
}
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
oldWrites[i]=pendingWrites[i];
|
||||
}
|
||||
|
|
@ -580,11 +584,15 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
|
||||
}
|
||||
if (i<melodicChans) {
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].keyOn && !chan[i].hardReset) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||
chan[i].keyOn=false;
|
||||
} else if (chan[i].freqChanged) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||
if (chan[i].keyOn && chan[i].hardReset) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|0);
|
||||
} else {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -602,6 +610,35 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (updateDrums) {
|
||||
immWrite(0xbd,(dam<<7)|(dvb<<6)|(properDrums<<5)|drumState);
|
||||
}
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<128; i++) {
|
||||
immWrite(0x3f,i&0xff);
|
||||
}
|
||||
for (int i=0x80; i<0xa0; i++) {
|
||||
if (weWillWriteRRLater[i-0x80]) {
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
oldWrites[i]=pendingWrites[i];
|
||||
}
|
||||
}
|
||||
for (int i=0x180; i<0x1a0; i++) {
|
||||
if (weWillWriteRRLater[32|(i-0x180)]) {
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
oldWrites[i]=pendingWrites[i];
|
||||
}
|
||||
}
|
||||
for (int i=0; i<melodicChans; i++) {
|
||||
if (chan[i].hardReset) {
|
||||
if (chan[i].keyOn) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||
chan[i].keyOn=false;
|
||||
} else if (chan[i].freqChanged) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define OPLL_C_NUM 686
|
||||
|
|
@ -689,6 +726,112 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformOPL::commitState(int ch, DivInstrument* ins) {
|
||||
if (chan[ch].insChanged) {
|
||||
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[melodicChans+i+1].state.alg=ins->fm.alg;
|
||||
chan[melodicChans+i+1].state.fb=ins->fm.fb;
|
||||
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
|
||||
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
|
||||
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
|
||||
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
|
||||
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
|
||||
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
|
||||
}
|
||||
} else {
|
||||
chan[ch].state=ins->fm;
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[ch].insChanged) {
|
||||
if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
||||
for (int i=0; i<4; i++) {
|
||||
int ch=melodicChans+1+i;
|
||||
unsigned char slot=slots[0][ch];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
|
||||
chan[ch].fourOp=false;
|
||||
|
||||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
|
||||
}
|
||||
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
if (oplType>1) {
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
|
||||
if (isMuted[ch]) {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
|
||||
} else {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
|
||||
chan[ch].fourOp=(ops==4);
|
||||
if (chan[ch].fourOp) {
|
||||
/*
|
||||
if (chan[ch+1].active) {
|
||||
chan[ch+1].keyOff=true;
|
||||
chan[ch+1].keyOn=false;
|
||||
chan[ch+1].active=false;
|
||||
}*/
|
||||
chan[ch+1].insChanged=true;
|
||||
chan[ch+1].macroInit(NULL);
|
||||
}
|
||||
update4OpMask=true;
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][ch];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
|
||||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (KVSL(ch,i) || ch>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
if (oplType>1) {
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
}
|
||||
|
||||
if (isMuted[ch]) {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
|
||||
if (ops==4) {
|
||||
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1));
|
||||
}
|
||||
} else {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
|
||||
if (ops==4) {
|
||||
oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformOPL::dispatch(DivCommand c) {
|
||||
if (c.chan>=totalChans && c.chan!=adpcmChan) return 0;
|
||||
// ineffective in 4-op mode
|
||||
|
|
@ -771,114 +914,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
}
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL);
|
||||
|
||||
if (chan[c.chan].insChanged) {
|
||||
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[melodicChans+i+1].state.alg=ins->fm.alg;
|
||||
chan[melodicChans+i+1].state.fb=ins->fm.fb;
|
||||
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
|
||||
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
|
||||
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
|
||||
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
|
||||
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
|
||||
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].state=ins->fm;
|
||||
}
|
||||
}
|
||||
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
|
||||
for (int i=0; i<4; i++) {
|
||||
int ch=melodicChans+1+i;
|
||||
unsigned char slot=slots[0][ch];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
|
||||
chan[ch].fourOp=false;
|
||||
|
||||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
|
||||
}
|
||||
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
if (oplType>1) {
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
|
||||
if (isMuted[ch]) {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
|
||||
} else {
|
||||
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
chan[c.chan].fourOp=(ops==4);
|
||||
if (chan[c.chan].fourOp) {
|
||||
/*
|
||||
if (chan[c.chan+1].active) {
|
||||
chan[c.chan+1].keyOff=true;
|
||||
chan[c.chan+1].keyOn=false;
|
||||
chan[c.chan+1].active=false;
|
||||
}*/
|
||||
chan[c.chan+1].insChanged=true;
|
||||
chan[c.chan+1].macroInit(NULL);
|
||||
}
|
||||
update4OpMask=true;
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (KVSL(c.chan,i) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
|
||||
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
if (oplType>1) {
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
}
|
||||
|
||||
if (isMuted[c.chan]) {
|
||||
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
|
||||
if (ops==4) {
|
||||
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
|
||||
}
|
||||
} else {
|
||||
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
|
||||
if (ops==4) {
|
||||
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
|
||||
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
|
|
@ -1075,6 +1116,11 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
@ -1509,7 +1555,11 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
|
|||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||
if (ch>=totalChans) return NULL;
|
||||
if (oplType==759) {
|
||||
if (ch>=totalChans+1) return NULL;
|
||||
} else {
|
||||
if (ch>=totalChans) return NULL;
|
||||
}
|
||||
if (oplType==3 && ch<12) {
|
||||
if (chan[ch&(~1)].fourOp) {
|
||||
if (ch&1) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
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