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

View File

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

View File

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

View File

@ -4346,14 +4346,25 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
} }
} }
bool relWarning=false;
for (int i=0; i<getChannelCount(sys); i++) { for (int i=0; i<getChannelCount(sys); i++) {
w->writeC(curPat[i].effectCols); w->writeC(curPat[i].effectCols);
for (int j=0; j<curSubSong->ordersLen; j++) { for (int j=0; j<curSubSong->ordersLen; j++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false); DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
for (int k=0; k<curSubSong->patLen; k++) { for (int k=0; k<curSubSong->patLen; k++) {
w->writeS(pat->data[k][0]); // note if ((pat->data[k][0]==101 || pat->data[k][0]==102) && pat->data[k][1]==0) {
w->writeS(pat->data[k][1]); // octave 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 w->writeS(pat->data[k][3]); // volume
#ifdef TA_BIG_ENDIAN #ifdef TA_BIG_ENDIAN
for (int l=0; l<curPat[i].effectCols*2; l++) { for (int l=0; l<curPat[i].effectCols*2; l++) {

View File

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

View File

@ -69,6 +69,14 @@ const char* regCheatSheetAY8914[]={
NULL 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() { const char** DivPlatformAY8910::getRegisterSheet() {
return intellivision?regCheatSheetAY8914:regCheatSheetAY; 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; regPool[w.addr&0x0f]=w.val;
writes.pop(); writes.pop();
} }
ay->sound_stream_update(ayBuf,len);
if (sunsoft) { if (sunsoft) {
for (size_t i=0; i<len; i++) { 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]; bufR[i+start]=bufL[i+start];
}
} else if (stereo) { oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]>>3;
for (size_t i=0; i<len; i++) { oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]>>3;
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]; oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]>>3;
bufR[i+start]=ayBuf[1][i]+ayBuf[2][i];
} }
} else { } else {
for (size_t i=0; i<len; i++) { ay->sound_stream_update(ayBuf,len);
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+ayBuf[2][i]; if (stereo) {
bufR[i+start]=bufL[i+start]; 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++) {
for (int ch=0; ch<3; ch++) { oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
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 "c64.h"
#include "../engine.h" #include "../engine.h"
#include "sound/c64_fp/siddefs-fp.h"
#include <math.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 #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) { 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++) { for (size_t i=start; i<start+len; i++) {
sid.clock(); if (isFP) {
bufL[i]=sid.output(); sid_fp.clock(4,&bufL[i]);
if (++writeOscBuf>=8) { if (++writeOscBuf>=4) {
writeOscBuf=0; writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5; oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5; oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-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) { void DivPlatformC64::muteChannel(int ch, bool mute) {
isMuted[ch]=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() { void DivPlatformC64::forceIns() {
@ -462,13 +477,21 @@ bool DivPlatformC64::getWantPreNote() {
return true; return true;
} }
float DivPlatformC64::getPostAmp() {
return isFP?3.0f:1.0f;
}
void DivPlatformC64::reset() { void DivPlatformC64::reset() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformC64::Channel(); chan[i]=DivPlatformC64::Channel();
chan[i].std.setEngine(parent); chan[i].std.setEngine(parent);
} }
sid.reset(); if (isFP) {
sid_fp.reset();
} else {
sid.reset();
}
memset(regPool,0,32); memset(regPool,0,32);
rWrite(0x18,0x0f); rWrite(0x18,0x0f);
@ -490,12 +513,24 @@ void DivPlatformC64::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformC64::setChipModel(bool is6581) { void DivPlatformC64::setChipModel(bool is6581) {
if (is6581) { if (is6581) {
sid.set_chip_model(MOS6581); if (isFP) {
sid_fp.setChipModel(reSIDfp::MOS6581);
} else {
sid.set_chip_model(MOS6581);
}
} else { } 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) { void DivPlatformC64::setFlags(unsigned int flags) {
switch (flags&0xf) { switch (flags&0xf) {
case 0x0: // NTSC C64 case 0x0: // NTSC C64
@ -513,6 +548,10 @@ void DivPlatformC64::setFlags(unsigned int flags) {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate/16; 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) { int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View File

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

View File

@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short* bufL, short* bufR, size_t start, size_
bufL[i]=sample; bufL[i]=sample;
if (++writeOscBuf>=32) { if (++writeOscBuf>=32) {
writeOscBuf=0; writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=nes->S1.output<<11; oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11);
oscBuf[1]->data[oscBuf[1]->needle++]=nes->S2.output<<11; oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11);
oscBuf[2]->data[oscBuf[2]->needle++]=nes->TR.output<<11; oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11);
oscBuf[3]->data[oscBuf[3]->needle++]=nes->NS.output<<11; oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11);
oscBuf[4]->data[oscBuf[4]->needle++]=nes->DMC.output<<8; 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); indx |= tone_mask | (m_vol_enabled[chan] ? tone_volume(tone) << (chan*5) : 0);
} }
} }
lastIndx=indx;
return m_vol3d_table[indx]; return m_vol3d_table[indx];
} }
@ -1359,6 +1360,7 @@ unsigned char ay8910_device::ay8910_read_ym()
void ay8910_device::device_reset() void ay8910_device::device_reset()
{ {
lastIndx=0;
ay8910_reset_ym(); ay8910_reset_ym();
} }

View File

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

View File

@ -132,7 +132,7 @@ private:
* *
* @return the output sample * @return the output sample
*/ */
int output() const; int output();
/** /**
* Calculate the numebr of cycles according to current parameters * Calculate the numebr of cycles according to current parameters
@ -146,6 +146,8 @@ public:
SID(); SID();
~SID(); ~SID();
int lastChanOut[3];
/** /**
* Set chip model. * Set chip model.
* *
@ -312,12 +314,16 @@ void SID::ageBusValue(unsigned int n)
} }
RESID_INLINE RESID_INLINE
int SID::output() const int SID::output()
{ {
const int v1 = voice[0]->output(voice[2]->wave()); const int v1 = voice[0]->output(voice[2]->wave());
const int v2 = voice[1]->output(voice[0]->wave()); const int v2 = voice[1]->output(voice[0]->wave());
const int v3 = voice[2]->output(voice[1]->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)); 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); DivSample* s=parent->getSample(dacSample);
if (s->samples<=0) { if (s->samples<=0) {
dacSample=-1; dacSample=-1;
continue; dacPeriod=0;
break;
} }
rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80); rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80);
if (s->isLoopable() && dacPos>=(unsigned int)s->loopEnd) { if (s->isLoopable() && dacPos>=(unsigned int)s->loopEnd) {

View File

@ -1298,6 +1298,32 @@ void FurnaceGUI::doAction(int what) {
MARK_MODIFIED; MARK_MODIFIED;
break; 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: case GUI_ACTION_ORDERS_UP:
if (curOrder>0) { if (curOrder>0) {

View File

@ -533,6 +533,7 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_ZOOM_AUTO, GUI_ACTION_SAMPLE_ZOOM_AUTO,
GUI_ACTION_SAMPLE_MAKE_INS, GUI_ACTION_SAMPLE_MAKE_INS,
GUI_ACTION_SAMPLE_SET_LOOP, GUI_ACTION_SAMPLE_SET_LOOP,
GUI_ACTION_SAMPLE_CREATE_WAVE,
GUI_ACTION_SAMPLE_MAX, GUI_ACTION_SAMPLE_MAX,
GUI_ACTION_ORDERS_MIN, 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_ZOOM_AUTO", "Toggle auto-zoom", FURKMOD_CMD|SDLK_0),
D("SAMPLE_MAKE_INS", "Create instrument from sample", 0), D("SAMPLE_MAKE_INS", "Create instrument from sample", 0),
D("SAMPLE_SET_LOOP", "Set loop to selection", FURKMOD_CMD|SDLK_l), 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("SAMPLE_MAX", "", NOT_AN_ACTION),
D("ORDERS_MIN", "---Orders", 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))) { if (ImGui::MenuItem("set loop to selection",BIND_FOR(GUI_ACTION_SAMPLE_SET_LOOP))) {
doAction(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(); ImGui::EndPopup();
} }