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

This commit is contained in:
cam900 2022-08-31 19:18:15 +09:00
commit d82d7255d1
17 changed files with 199 additions and 56 deletions

View file

@ -36,12 +36,20 @@
#define CONFIG_FILE "/furnace.cfg"
#endif
#ifdef IS_MOBILE
#ifdef HAVE_SDL2
#include <SDL.h>
#else
#error "Furnace mobile requires SDL2!"
#endif
#endif
void DivEngine::initConfDir() {
#ifdef _WIN32
// maybe move this function in here instead?
configPath=getWinConfigPath();
#elif defined (IS_MOBILE)
configPath=SDL_GetPrefPath();
#elif defined(IS_MOBILE)
configPath=SDL_GetPrefPath("tildearrow","furnace");
#else
#ifdef __HAIKU__
char userSettingsDir[PATH_MAX];

View file

@ -218,10 +218,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
break;
case DIV_SYSTEM_C64_6581:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
((DivPlatformC64*)dispatch)->setChipModel(true);
break;
case DIV_SYSTEM_C64_8580:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
((DivPlatformC64*)dispatch)->setChipModel(false);
break;
case DIV_SYSTEM_YM2151:

View file

@ -1141,7 +1141,7 @@ void DivEngine::swapChannels(int src, int dest) {
String prevChanName=curSubSong->chanName[src];
String prevChanShortName=curSubSong->chanShortName[src];
bool prevChanShow=curSubSong->chanShow[src];
bool prevChanCollapse=curSubSong->chanCollapse[src];
unsigned char prevChanCollapse=curSubSong->chanCollapse[src];
curSubSong->chanName[src]=curSubSong->chanName[dest];
curSubSong->chanShortName[src]=curSubSong->chanShortName[dest];
@ -1446,25 +1446,44 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
}
}
// swap channels
logV("swap list:");
for (int i=0; i<tchans; i++) {
logV("- %d -> %d",unswappedChannels[i],swappedChannels[i]);
}
// swap channels
bool allComplete=false;
while (!allComplete) {
logD("doing swap...");
allComplete=true;
for (int i=0; i<tchans; i++) {
if (unswappedChannels[i]!=swappedChannels[i]) {
swapChannels(i,swappedChannels[i]);
allComplete=false;
logD("> %d -> %d",unswappedChannels[i],unswappedChannels[swappedChannels[i]]);
unswappedChannels[i]^=unswappedChannels[swappedChannels[i]];
unswappedChannels[swappedChannels[i]]^=unswappedChannels[i];
unswappedChannels[i]^=unswappedChannels[swappedChannels[i]];
for (size_t i=0; i<song.subsong.size(); i++) {
DivOrders prevOrders=song.subsong[i]->orders;
DivPattern* prevPat[DIV_MAX_CHANS][256];
unsigned char prevEffectCols[DIV_MAX_CHANS];
String prevChanName[DIV_MAX_CHANS];
String prevChanShortName[DIV_MAX_CHANS];
bool prevChanShow[DIV_MAX_CHANS];
unsigned char prevChanCollapse[DIV_MAX_CHANS];
for (int j=0; j<tchans; j++) {
for (int k=0; k<256; k++) {
prevPat[j][k]=song.subsong[i]->pat[j].data[k];
}
prevEffectCols[j]=song.subsong[i]->pat[j].effectCols;
prevChanName[j]=song.subsong[i]->chanName[j];
prevChanShortName[j]=song.subsong[i]->chanShortName[j];
prevChanShow[j]=song.subsong[i]->chanShow[j];
prevChanCollapse[j]=song.subsong[i]->chanCollapse[j];
}
for (int j=0; j<tchans; j++) {
for (int k=0; k<256; k++) {
song.subsong[i]->orders.ord[j][k]=prevOrders.ord[swappedChannels[j]][k];
song.subsong[i]->pat[j].data[k]=prevPat[swappedChannels[j]][k];
}
song.subsong[i]->pat[j].effectCols=prevEffectCols[swappedChannels[j]];
song.subsong[i]->chanName[j]=prevChanName[swappedChannels[j]];
song.subsong[i]->chanShortName[j]=prevChanShortName[swappedChannels[j]];
song.subsong[i]->chanShow[j]=prevChanShow[swappedChannels[j]];
song.subsong[i]->chanCollapse[j]=prevChanCollapse[swappedChannels[j]];
}
}
}
@ -1648,7 +1667,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
}
}
int oldOrder=curOrder;
while (playing && curRow<goalRow) {
while (playing && (curRow<goalRow || ticks>1)) {
if (nextTick(preserveDrift)) {
skipping=false;
return;

View file

@ -4346,14 +4346,25 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
}
}
bool relWarning=false;
for (int i=0; i<getChannelCount(sys); i++) {
w->writeC(curPat[i].effectCols);
for (int j=0; j<curSubSong->ordersLen; j++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
for (int k=0; k<curSubSong->patLen; k++) {
w->writeS(pat->data[k][0]); // note
w->writeS(pat->data[k][1]); // octave
if ((pat->data[k][0]==101 || pat->data[k][0]==102) && pat->data[k][1]==0) {
w->writeS(100);
w->writeS(0);
if (!relWarning) {
relWarning=true;
addWarning("note/macro release will be converted to note off!");
}
} else {
w->writeS(pat->data[k][0]); // note
w->writeS(pat->data[k][1]); // octave
}
w->writeS(pat->data[k][3]); // volume
#ifdef TA_BIG_ENDIAN
for (int l=0; l<curPat[i].effectCols*2; l++) {

View file

@ -50,7 +50,7 @@ struct DivMacroStruct {
finished(false),
will(false),
linger(false),
began(false),
began(true),
mode(0) {}
};

View file

@ -69,6 +69,14 @@ const char* regCheatSheetAY8914[]={
NULL
};
// taken from ay8910.cpp
const int sunsoftVolTable[32]={
103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451,
18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796,
5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737,
1397, 1123, 925, 762, 578, 438, 332, 251
};
const char** DivPlatformAY8910::getRegisterSheet() {
return intellivision?regCheatSheetAY8914:regCheatSheetAY;
}
@ -134,27 +142,33 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
regPool[w.addr&0x0f]=w.val;
writes.pop();
}
ay->sound_stream_update(ayBuf,len);
if (sunsoft) {
for (size_t i=0; i<len; i++) {
bufL[i+start]=ayBuf[0][i];
ay->sound_stream_update(ayBuf,1);
bufL[i+start]=ayBuf[0][0];
bufR[i+start]=bufL[i+start];
}
} else if (stereo) {
for (size_t i=0; i<len; i++) {
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i];
bufR[i+start]=ayBuf[1][i]+ayBuf[2][i];
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]>>3;
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]>>3;
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]>>3;
}
} else {
for (size_t i=0; i<len; i++) {
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+ayBuf[2][i];
bufR[i+start]=bufL[i+start];
ay->sound_stream_update(ayBuf,len);
if (stereo) {
for (size_t i=0; i<len; i++) {
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i];
bufR[i+start]=ayBuf[1][i]+ayBuf[2][i];
}
} else {
for (size_t i=0; i<len; i++) {
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+ayBuf[2][i];
bufR[i+start]=bufL[i+start];
}
}
}
for (int ch=0; ch<3; ch++) {
for (size_t i=0; i<len; i++) {
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
for (int ch=0; ch<3; ch++) {
for (size_t i=0; i<len; i++) {
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
}
}
}
}

View file

@ -19,9 +19,10 @@
#include "c64.h"
#include "../engine.h"
#include "sound/c64_fp/siddefs-fp.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {sid.write(a,v); regPool[(a)&0x1f]=v; if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {if (isFP) {sid_fp.write(a,v);} else {sid.write(a,v);}; regPool[(a)&0x1f]=v; if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 524288
@ -63,15 +64,25 @@ const char** DivPlatformC64::getRegisterSheet() {
}
void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int dcOff=sid.get_dc(0);
int dcOff=isFP?0:sid.get_dc(0);
for (size_t i=start; i<start+len; i++) {
sid.clock();
bufL[i]=sid.output();
if (++writeOscBuf>=8) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
if (isFP) {
sid_fp.clock(4,&bufL[i]);
if (++writeOscBuf>=4) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
}
} else {
sid.clock();
bufL[i]=sid.output();
if (++writeOscBuf>=16) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
}
}
}
}
@ -405,7 +416,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
void DivPlatformC64::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
sid.set_is_muted(ch,mute);
if (isFP) {
sid_fp.mute(ch,mute);
} else {
sid.set_is_muted(ch,mute);
}
}
void DivPlatformC64::forceIns() {
@ -462,13 +477,21 @@ bool DivPlatformC64::getWantPreNote() {
return true;
}
float DivPlatformC64::getPostAmp() {
return isFP?3.0f:1.0f;
}
void DivPlatformC64::reset() {
for (int i=0; i<3; i++) {
chan[i]=DivPlatformC64::Channel();
chan[i].std.setEngine(parent);
}
sid.reset();
if (isFP) {
sid_fp.reset();
} else {
sid.reset();
}
memset(regPool,0,32);
rWrite(0x18,0x0f);
@ -490,12 +513,24 @@ void DivPlatformC64::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformC64::setChipModel(bool is6581) {
if (is6581) {
sid.set_chip_model(MOS6581);
if (isFP) {
sid_fp.setChipModel(reSIDfp::MOS6581);
} else {
sid.set_chip_model(MOS6581);
}
} else {
sid.set_chip_model(MOS8580);
if (isFP) {
sid_fp.setChipModel(reSIDfp::MOS8580);
} else {
sid.set_chip_model(MOS8580);
}
}
}
void DivPlatformC64::setFP(bool fp) {
isFP=fp;
}
void DivPlatformC64::setFlags(unsigned int flags) {
switch (flags&0xf) {
case 0x0: // NTSC C64
@ -513,6 +548,10 @@ void DivPlatformC64::setFlags(unsigned int flags) {
for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate/16;
}
if (isFP) {
rate/=4;
sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
}
}
int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View file

@ -23,6 +23,7 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include "sound/c64/sid.h"
#include "sound/c64_fp/SID.h"
class DivPlatformC64: public DivDispatch {
struct Channel {
@ -76,12 +77,17 @@ class DivPlatformC64: public DivDispatch {
unsigned char filtControl, filtRes, vol;
unsigned char writeOscBuf;
int filtCut, resetTime;
bool isFP;
SID sid;
reSIDfp::SID sid_fp;
unsigned char regPool[32];
friend void putDispatchChan(void*,int,int);
void acquire_classic(short* bufL, short* bufR, size_t start, size_t len);
void acquire_fp(short* bufL, short* bufR, size_t start, size_t len);
void updateFilter();
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
@ -98,6 +104,7 @@ class DivPlatformC64: public DivDispatch {
void notifyInsChange(int ins);
bool getDCOffRequired();
bool getWantPreNote();
float getPostAmp();
DivMacroInt* getChanMacroInt(int ch);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
@ -105,6 +112,7 @@ class DivPlatformC64: public DivDispatch {
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void setChipModel(bool is6581);
void setFP(bool fp);
void quit();
~DivPlatformC64();
};

View file

@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short* bufL, short* bufR, size_t start, size_
bufL[i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=nes->S1.output<<11;
oscBuf[1]->data[oscBuf[1]->needle++]=nes->S2.output<<11;
oscBuf[2]->data[oscBuf[2]->needle++]=nes->TR.output<<11;
oscBuf[3]->data[oscBuf[3]->needle++]=nes->NS.output<<11;
oscBuf[4]->data[oscBuf[4]->needle++]=nes->DMC.output<<8;
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11);
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11);
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<8);
}
}
}

View file

@ -924,6 +924,7 @@ float ay8910_device::mix_3D()
indx |= tone_mask | (m_vol_enabled[chan] ? tone_volume(tone) << (chan*5) : 0);
}
}
lastIndx=indx;
return m_vol3d_table[indx];
}
@ -1359,6 +1360,7 @@ unsigned char ay8910_device::ay8910_read_ym()
void ay8910_device::device_reset()
{
lastIndx=0;
ay8910_reset_ym();
}

View file

@ -146,6 +146,8 @@ public:
double m_Kn[32];
};
int lastIndx;
// internal interface for PSG component of YM device
// FIXME: these should be private, but vector06 accesses them directly

View file

@ -132,7 +132,7 @@ private:
*
* @return the output sample
*/
int output() const;
int output();
/**
* Calculate the numebr of cycles according to current parameters
@ -146,6 +146,8 @@ public:
SID();
~SID();
int lastChanOut[3];
/**
* Set chip model.
*
@ -312,12 +314,16 @@ void SID::ageBusValue(unsigned int n)
}
RESID_INLINE
int SID::output() const
int SID::output()
{
const int v1 = voice[0]->output(voice[2]->wave());
const int v2 = voice[1]->output(voice[0]->wave());
const int v3 = voice[2]->output(voice[1]->wave());
lastChanOut[0]=v1;
lastChanOut[1]=v2;
lastChanOut[2]=v3;
return externalFilter->clock(filter->clock(v1, v2, v3));
}

View file

@ -59,7 +59,8 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len
DivSample* s=parent->getSample(dacSample);
if (s->samples<=0) {
dacSample=-1;
continue;
dacPeriod=0;
break;
}
rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80);
if (s->isLoopable() && dacPos>=(unsigned int)s->loopEnd) {

View file

@ -1298,6 +1298,32 @@ void FurnaceGUI::doAction(int what) {
MARK_MODIFIED;
break;
}
case GUI_ACTION_SAMPLE_CREATE_WAVE: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
DivSample* sample=e->song.sample[curSample];
SAMPLE_OP_BEGIN;
if (end-start<1) {
showError("select at least one sample!");
} else if (end-start>256) {
showError("maximum size is 256 samples!");
} else {
curWave=e->addWave();
if (curWave==-1) {
showError("too many wavetables!");
} else {
DivWavetable* wave=e->song.wave[curWave];
wave->min=0;
wave->max=255;
wave->len=end-start;
for (unsigned int i=start; i<end; i++) {
wave->data[i-start]=(sample->data8[i]&0xff)^0x80;
}
nextWindow=GUI_WINDOW_WAVE_EDIT;
MARK_MODIFIED;
}
}
break;
}
case GUI_ACTION_ORDERS_UP:
if (curOrder>0) {

View file

@ -533,6 +533,7 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_ZOOM_AUTO,
GUI_ACTION_SAMPLE_MAKE_INS,
GUI_ACTION_SAMPLE_SET_LOOP,
GUI_ACTION_SAMPLE_CREATE_WAVE,
GUI_ACTION_SAMPLE_MAX,
GUI_ACTION_ORDERS_MIN,

View file

@ -655,6 +655,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("SAMPLE_ZOOM_AUTO", "Toggle auto-zoom", FURKMOD_CMD|SDLK_0),
D("SAMPLE_MAKE_INS", "Create instrument from sample", 0),
D("SAMPLE_SET_LOOP", "Set loop to selection", FURKMOD_CMD|SDLK_l),
D("SAMPLE_CREATE_WAVE", "Create wavetable from selection", FURKMOD_CMD|SDLK_w),
D("SAMPLE_MAX", "", NOT_AN_ACTION),
D("ORDERS_MIN", "---Orders", NOT_AN_ACTION),

View file

@ -1338,6 +1338,9 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::MenuItem("set loop to selection",BIND_FOR(GUI_ACTION_SAMPLE_SET_LOOP))) {
doAction(GUI_ACTION_SAMPLE_SET_LOOP);
}
if (ImGui::MenuItem("create wavetable from selection",BIND_FOR(GUI_ACTION_SAMPLE_CREATE_WAVE))) {
doAction(GUI_ACTION_SAMPLE_CREATE_WAVE);
}
ImGui::EndPopup();
}