Compare commits

...

10 commits

Author SHA1 Message Date
host12prog 8dd9ede0d0 add envelope channel to OPN SSG + rel. noise & stereo 2025-07-09 21:57:47 +07:00
tildearrow 896f0925b1 GUI: remove colon
i will make this dynamic
2025-07-06 05:11:36 -05:00
RevvoBolt 50101414d3 A small sv.po update 2025-07-05 23:40:15 -05:00
RevvoBolt 6d49ea5b3e An update sv.po
More additions and fixes for sv.po.
2025-07-05 23:40:15 -05:00
Electric Keet 1427fdac3b Doc update: image. 2025-07-05 23:39:34 -05:00
Electric Keet 23841bfaf5 Doc updates: text. 2025-07-05 23:39:34 -05:00
tildearrow 7414adfcc8 GUI: this is what happens when you copy and paste 2025-07-05 16:26:37 -05:00
tildearrow 57813d53c1 GUI: add input boxes for sample selection range
major sample editor additions coming soon
prepare
2025-07-05 05:06:15 -05:00
tildearrow aec1f80279 GUI: new pattern cursor logic, part 18
fix another thing
2025-07-05 04:46:25 -05:00
tildearrow 55e00cf538 GUI: new pattern cursor logic, part 17
fix some more annoyances
2025-07-05 04:32:28 -05:00
21 changed files with 607 additions and 263 deletions

View file

@ -84,7 +84,7 @@ the keys in the "Global hotkeys" section can be used in any window, although not
| Samples (Palette) | — | | Samples (Palette) | — |
| | | | | |
| **Note input** | | | **Note input** | |
| _see "note input" section after table_ | | | _see [settings](./settings.md#note-input)._ | |
| | | | | |
| **Pattern** | | | **Pattern** | |
| Transpose (+1) | `Ctrl-F2` | | Transpose (+1) | `Ctrl-F2` |

View file

@ -356,6 +356,10 @@ below all the binds, select a key from the dropdown list to add it. it will appe
- **No** - **No**
- **Yes** - **Yes**
- **Inverted** - **Inverted**
- **How many steps to move with each scroll wheel step?**: only appears when "Move cursor with scroll wheel" is set to "Yes" or "Inverted".
- **One**
- **Edit Step**
- **Coarse Step**
### Assets ### Assets

View file

@ -4,7 +4,7 @@ Bifurcator instrument editor consists of these macros:
- **Volume**: volume sequence. - **Volume**: volume sequence.
- **Arpeggio**: pitch sequence. - **Arpeggio**: pitch sequence.
- **Parametet**: set parameter of logistic map function. - **Parameter**: set parameter of logistic map function.
- **Panning (left)**: output level for left channel. - **Panning (left)**: output level for left channel.
- **Panning (right)**: output level for right channel. - **Panning (right)**: output level for right channel.
- **Pitch**: fine pitch. - **Pitch**: fine pitch.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 237 KiB

2
extern/SDL vendored

@ -1 +1 @@
Subproject commit 6371fd44c8a3cdfb3166b36d4798f5daeca2eeee Subproject commit 2359383fc187386204c3bb22de89655a494cd128

497
po/sv.po

File diff suppressed because it is too large Load diff

View file

@ -449,6 +449,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
} }
((DivPlatformYM2203Ext*)dispatch)->setCSM(1); ((DivPlatformYM2203Ext*)dispatch)->setCSM(1);
break; break;
case DIV_SYSTEM_YM2203_CSM_ENV:
dispatch = new DivPlatformYM2203Ext;
if (isRender) {
((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opn1CoreRender", 1));
}
else {
((DivPlatformYM2203Ext*)dispatch)->setCombo(eng->getConfInt("opn1Core", 1));
}
((DivPlatformYM2203Ext*)dispatch)->setCSM(1);
break;
case DIV_SYSTEM_YM2608: case DIV_SYSTEM_YM2608:
dispatch=new DivPlatformYM2608; dispatch=new DivPlatformYM2608;
if (isRender) { if (isRender) {

View file

@ -339,8 +339,8 @@ void DivPlatformAY8910::acquire_mame(blip_buffer_t** bb, size_t len) {
oscBuf[2]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767)); oscBuf[2]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767));
} else { } else {
if (stereo) { if (stereo) {
int out0=ayBuf[0]+ayBuf[1]+((ayBuf[2]*stereoSep)>>8); int out0=ayBuf[0]+((ayBuf[1]*centerVol+ayBuf[2]*sideVol)>>8);
int out1=((ayBuf[0]*stereoSep)>>8)+ayBuf[1]+ayBuf[2]; int out1=((ayBuf[0]*sideVol+ayBuf[1]*centerVol)>>8)+ayBuf[2];
if (lastOut[0]!=out0) { if (lastOut[0]!=out0) {
blip_add_delta(bb[0],i,out0-lastOut[0]); blip_add_delta(bb[0],i,out0-lastOut[0]);
lastOut[0]=out0; lastOut[0]=out0;
@ -388,8 +388,8 @@ void DivPlatformAY8910::acquire_atomic(short** buf, size_t len) {
SSG_Clock(&ay_atomic,1); SSG_Clock(&ay_atomic,1);
if (stereo) { if (stereo) {
buf[0][i]=ay_atomic.o_analog[0]+ay_atomic.o_analog[1]+((ay_atomic.o_analog[2]*stereoSep)>>8); buf[0][i]=ay_atomic.o_analog[0]+((ay_atomic.o_analog[1]*centerVol+ay_atomic.o_analog[2]*sideVol)>>8);
buf[1][i]=((ay_atomic.o_analog[0]*stereoSep)>>8)+ay_atomic.o_analog[1]+ay_atomic.o_analog[2]; buf[1][i]=((ay_atomic.o_analog[0]*sideVol+ay_atomic.o_analog[1]*centerVol)>>8)+ay_atomic.o_analog[2];
} else { } else {
buf[0][i]=ay_atomic.o_analog[0]+ay_atomic.o_analog[1]+ay_atomic.o_analog[2]; buf[0][i]=ay_atomic.o_analog[0]+ay_atomic.o_analog[1]+ay_atomic.o_analog[2];
buf[1][i]=buf[0][i]; buf[1][i]=buf[0][i];
@ -453,6 +453,7 @@ void DivPlatformAY8910::updateOutSel(bool immediate) {
} }
void DivPlatformAY8910::tick(bool sysTick) { void DivPlatformAY8910::tick(bool sysTick) {
signed char noiseAdd=-1;
// PSG // PSG
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i].std.next(); chan[i].std.next();
@ -478,7 +479,13 @@ void DivPlatformAY8910::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.duty.had) { if (chan[i].std.duty.had) {
rWrite(0x06,31-chan[i].std.duty.val); if (chan[i].std.duty.mode) {
noiseAdd=chan[i].std.duty.val;
}
else {
ayNoisePeriod=chan[i].std.duty.val;
noiseAdd=0;
}
} }
if (chan[i].std.wave.had) { if (chan[i].std.wave.had) {
if (!(chan[i].nextPSGMode.val&8)) { if (!(chan[i].nextPSGMode.val&8)) {
@ -547,10 +554,8 @@ void DivPlatformAY8910::tick(bool sysTick) {
chan[i].fixedFreq=chan[i].std.ex4.val; chan[i].fixedFreq=chan[i].std.ex4.val;
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.ex5.had) { if (chan[i].std.ex5.had && (!chan[3].active && extendedSsg)) {
ayEnvPeriod=chan[i].std.ex5.val; ayEnvPeriod=chan[i].std.ex5.val<<8;
immWrite(0x0b,ayEnvPeriod);
immWrite(0x0c,ayEnvPeriod>>8);
} }
if (chan[i].std.ex6.had) { if (chan[i].std.ex6.had) {
// 0 - disable timer // 0 - disable timer
@ -627,6 +632,9 @@ void DivPlatformAY8910::tick(bool sysTick) {
rWrite((i)<<1,chan[i].freq&0xff); rWrite((i)<<1,chan[i].freq&0xff);
rWrite(1+((i)<<1),chan[i].freq>>8); rWrite(1+((i)<<1),chan[i].freq>>8);
} }
if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0 && (!chan[3].active && extendedSsg)) {
ayEnvPeriod=(chan[i].freq*chan[i].autoEnvDen/chan[i].autoEnvNum)<<4;
}
if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) { if (chan[i].freqChanged && chan[i].autoEnvNum>0 && chan[i].autoEnvDen>0) {
@ -638,6 +646,48 @@ void DivPlatformAY8910::tick(bool sysTick) {
} }
} }
// Envelope
chan[3].std.next();
if (NEW_ARP_STRAT) {
chan[3].handleArp();
} else if (chan[3].std.arp.had) {
if (!chan[3].inPorta) {
int note=parent->calcArp(chan[3].note,chan[3].std.arp.val);
chan[3].baseFreq=round(parent->calcBaseFreq(chipClock*16,CHIP_DIVIDER,note,true));
}
chan[3].freqChanged=true;
}
if (chan[3].std.pitch.had) {
if (chan[3].std.pitch.mode) {
chan[3].pitch2+=chan[3].std.pitch.val;
CLAMP_VAR(chan[3].pitch2,-32768,32767);
} else {
chan[3].pitch2=chan[3].std.pitch.val;
}
chan[3].freqChanged=true;
}
if (chan[3].std.ex2.had) {
ayEnvMode=chan[3].std.ex2.val;
rWrite(0x0d,ayEnvMode);
}
if (chan[3].std.ex5.had && chan[3].active) {
ayEnvPeriod=chan[3].std.ex5.val<<8;
}
else if ((chan[3].freqChanged && chan[3].active) || chan[3].keyOn) {
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,chan[3].fixedArp?chan[3].baseNoteOverride:chan[3].arpOff,chan[3].fixedArp,true,0,chan[3].pitch2,chipClock*16,CHIP_DIVIDER);
if (chan[3].freq<0) chan[3].freq=0;
if (chan[3].freq>65535*256) chan[3].freq=65535*256;
ayEnvPeriod=chan[3].freq;
chan[3].freqChanged=false;
}
if (chan[3].keyOn) chan[3].keyOn=false;
if (chan[3].keyOff) chan[3].keyOff=false;
if (noiseAdd>=0) {
ayNoisePeriod=(ayNoisePeriod+noiseAdd)&31;
rWrite(0x06,31-ayNoisePeriod);
}
updateOutSel(); updateOutSel();
if (ayEnvSlide!=0) { if (ayEnvSlide!=0) {
@ -659,6 +709,13 @@ void DivPlatformAY8910::tick(bool sysTick) {
} }
} }
} }
unsigned int newPeriod=ayEnvPeriod>>8;
if (newPeriod!=ayOldEnvPeriod) {
immWrite(0x0b,newPeriod&0xff);
immWrite(0x0c,newPeriod>>8);
ayOldEnvPeriod=newPeriod;
}
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
if (pendingWrites[i]!=oldWrites[i]) { if (pendingWrites[i]!=oldWrites[i]) {
@ -760,7 +817,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
} }
if (!(chan[c.chan].nextPSGMode.val&8)) { if (c.chan<3 && !(chan[c.chan].nextPSGMode.val&8)) {
if (isMuted[c.chan]) { if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0); rWrite(0x08+c.chan,0);
} else if (intellivision && (chan[c.chan].nextPSGMode.getEnvelope())) { } else if (intellivision && (chan[c.chan].nextPSGMode.getEnvelope())) {
@ -788,7 +845,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) { if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
if (!(chan[c.chan].nextPSGMode.val&8)) { if (c.chan<3 && !(chan[c.chan].nextPSGMode.val&8)) {
if (isMuted[c.chan]) { if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0); rWrite(0x08+c.chan,0);
} else { } else {
@ -856,20 +913,23 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
if (chan[c.chan].active) { if (chan[c.chan].active) {
chan[c.chan].curPSGMode.val=chan[c.chan].nextPSGMode.val; chan[c.chan].curPSGMode.val=chan[c.chan].nextPSGMode.val;
} }
if (isMuted[c.chan]) { if (c.chan<3) {
rWrite(0x08+c.chan,0); if (isMuted[c.chan]) {
} else if (chan[c.chan].active) { rWrite(0x08+c.chan,0);
if (intellivision && (chan[c.chan].nextPSGMode.getEnvelope())) { } else if (chan[c.chan].active) {
rWrite(0x08+c.chan,(chan[c.chan].outVol&0xc)<<2); if (intellivision && (chan[c.chan].nextPSGMode.getEnvelope())) {
} else { rWrite(0x08+c.chan,(chan[c.chan].outVol&0xc)<<2);
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].nextPSGMode.getEnvelope())<<2)); } else {
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].nextPSGMode.getEnvelope())<<2));
}
} }
} }
} }
} }
break; break;
case DIV_CMD_STD_NOISE_FREQ: case DIV_CMD_STD_NOISE_FREQ:
rWrite(0x06,31-c.value); ayNoisePeriod=c.value;
rWrite(0x06,31-ayNoisePeriod);
break; break;
case DIV_CMD_AY_ENVELOPE_SET: case DIV_CMD_AY_ENVELOPE_SET:
ayEnvMode=c.value>>4; ayEnvMode=c.value>>4;
@ -891,16 +951,18 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_AY_ENVELOPE_LOW: case DIV_CMD_AY_ENVELOPE_LOW:
ayEnvPeriod&=0xff00; ayEnvPeriod&=0xff0000;
ayEnvPeriod|=c.value; ayEnvPeriod|=c.value<<8;
immWrite(0x0b,ayEnvPeriod); ayOldEnvPeriod=ayEnvPeriod>>8;
immWrite(0x0c,ayEnvPeriod>>8); immWrite(0x0b,ayOldEnvPeriod&0xff);
immWrite(0x0c,ayOldEnvPeriod>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_HIGH: case DIV_CMD_AY_ENVELOPE_HIGH:
ayEnvPeriod&=0xff; ayEnvPeriod&=0xff00;
ayEnvPeriod|=c.value<<8; ayEnvPeriod|=c.value<<16;
immWrite(0x0b,ayEnvPeriod); ayOldEnvPeriod=ayEnvPeriod>>8;
immWrite(0x0c,ayEnvPeriod>>8); immWrite(0x0b,ayOldEnvPeriod&0xff);
immWrite(0x0c,ayOldEnvPeriod>>8);
break; break;
case DIV_CMD_AY_ENVELOPE_SLIDE: case DIV_CMD_AY_ENVELOPE_SLIDE:
ayEnvSlide=c.value; ayEnvSlide=c.value;
@ -985,6 +1047,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
} }
void DivPlatformAY8910::muteChannel(int ch, bool mute) { void DivPlatformAY8910::muteChannel(int ch, bool mute) {
if (ch>=3) return;
isMuted[ch]=mute; isMuted[ch]=mute;
if (isMuted[ch]) { if (isMuted[ch]) {
rWrite(0x08+ch,0); rWrite(0x08+ch,0);
@ -1000,14 +1063,15 @@ void DivPlatformAY8910::muteChannel(int ch, bool mute) {
} }
void DivPlatformAY8910::forceIns() { void DivPlatformAY8910::forceIns() {
for (int i=0; i<3; i++) { for (int i=0; i<4; i++) {
chan[i].curPSGMode.val&=~8; chan[i].curPSGMode.val&=~8;
chan[i].nextPSGMode.val&=~8; chan[i].nextPSGMode.val&=~8;
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
immWrite(0x0b,ayEnvPeriod); ayOldEnvPeriod=ayEnvPeriod>>8;
immWrite(0x0c,ayEnvPeriod>>8); immWrite(0x0b,ayOldEnvPeriod&0xff);
immWrite(0x0c,ayOldEnvPeriod>>8);
immWrite(0x0d,ayEnvMode); immWrite(0x0d,ayEnvMode);
} }
@ -1029,7 +1093,7 @@ DivSamplePos DivPlatformAY8910::getSamplePos(int ch) {
} }
DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch]; return (ch<3)?oscBuf[ch]:NULL;
} }
int DivPlatformAY8910::mapVelocity(int ch, float vel) { int DivPlatformAY8910::mapVelocity(int ch, float vel) {
@ -1061,7 +1125,7 @@ void DivPlatformAY8910::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
ay->device_reset(); ay->device_reset();
memset(regPool,0,16); memset(regPool,0,16);
for (int i=0; i<3; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformAY8910::Channel(); chan[i]=DivPlatformAY8910::Channel();
chan[i].std.setEngine(parent); chan[i].std.setEngine(parent);
chan[i].vol=0x0f; chan[i].vol=0x0f;
@ -1082,7 +1146,9 @@ void DivPlatformAY8910::reset() {
} }
sampleBank=0; sampleBank=0;
ayNoisePeriod=0;
ayEnvPeriod=0; ayEnvPeriod=0;
ayOldEnvPeriod=0;
ayEnvMode=0; ayEnvMode=0;
ayEnvSlide=0; ayEnvSlide=0;
ayEnvSlideLow=0; ayEnvSlideLow=0;
@ -1114,7 +1180,7 @@ bool DivPlatformAY8910::getLegacyAlwaysSetVolume() {
} }
void DivPlatformAY8910::notifyInsDeletion(void* ins) { void DivPlatformAY8910::notifyInsDeletion(void* ins) {
for (int i=0; i<3; i++) { for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins); chan[i].std.notifyInsDeletion((DivInstrument*)ins);
} }
} }
@ -1250,6 +1316,21 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
stereo=flags.getBool("stereo",false); stereo=flags.getBool("stereo",false);
stereoSep=flags.getInt("stereoSep",0)&255; stereoSep=flags.getInt("stereoSep",0)&255;
extendedSsg=false;
switch (flags.getInt("panLaw",0)) {
case 1:
centerVol=sqrtf((stereoSep+256)/512.f)*256.f;
sideVol=sqrtf(stereoSep/256.f)*256.f;
break;
case 2:
centerVol=(stereoSep+256)/2;
sideVol=stereoSep;
break;
default:
centerVol=256;
sideVol=stereoSep;
break;
}
} }
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
@ -1263,7 +1344,11 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, const DivCo
ay=NULL; ay=NULL;
setFlags(flags); setFlags(flags);
reset(); reset();
return 3; return extendedSsg?4:3;
}
void DivPlatformAY8910::setExtended(bool extended) {
extendedSsg=extended;
} }
void DivPlatformAY8910::quit() { void DivPlatformAY8910::quit() {

View file

@ -106,7 +106,7 @@ class DivPlatformAY8910: public DivDispatch {
konCycles(0), konCycles(0),
fixedFreq(0) {} fixedFreq(0) {}
}; };
Channel chan[3]; Channel chan[4];
bool isMuted[3]; bool isMuted[3];
struct QueuedWrite { struct QueuedWrite {
unsigned short addr; unsigned short addr;
@ -123,6 +123,8 @@ class DivPlatformAY8910: public DivDispatch {
unsigned char sampleBank; unsigned char sampleBank;
unsigned char stereoSep; unsigned char stereoSep;
unsigned short sideVol;
unsigned short centerVol;
unsigned char selCore; unsigned char selCore;
ssg_t ay_atomic; ssg_t ay_atomic;
@ -130,6 +132,8 @@ class DivPlatformAY8910: public DivDispatch {
int delay; int delay;
int lastOut[2]; int lastOut[2];
bool extendedSsg;
bool extMode; bool extMode;
unsigned int extClock; unsigned int extClock;
int dacRate; int dacRate;
@ -142,7 +146,9 @@ class DivPlatformAY8910: public DivDispatch {
short oldWrites[16]; short oldWrites[16];
short pendingWrites[16]; short pendingWrites[16];
unsigned char ayNoisePeriod;
unsigned char ayEnvMode; unsigned char ayEnvMode;
unsigned short ayOldEnvPeriod;
unsigned short ayEnvPeriod; unsigned short ayEnvPeriod;
short ayEnvSlideLow; short ayEnvSlideLow;
short ayEnvSlide; short ayEnvSlide;
@ -189,6 +195,7 @@ class DivPlatformAY8910: public DivDispatch {
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void setExtended(bool extended);
void quit(); void quit();
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8, unsigned char ddiv=24): DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8, unsigned char ddiv=24):
DivDispatch(), DivDispatch(),

View file

@ -167,6 +167,7 @@ void DivPlatformYM2203::acquire(short** buf, size_t len) {
void DivPlatformYM2203::acquire_combo(short** buf, size_t len) { void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
thread_local int os; thread_local int os;
thread_local int os1, os2;
thread_local short ignored[2]; thread_local short ignored[2];
for (int i=0; i<7; i++) { for (int i=0; i<7; i++) {
@ -233,11 +234,23 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
// ymfm part // ymfm part
fm->generate(&fmout); fm->generate(&fmout);
os+=((fmout.data[1]+fmout.data[2]+fmout.data[3])*ssgVol)>>8; if (stereo) {
if (os<-32768) os=-32768; // so ugly
if (os>32767) os=32767; os1=((fmout.data[1]+((fmout.data[2]*centerVol+fmout.data[3]*sideVol)>>8))*ssgVol)>>8;
os2=((((fmout.data[1]*sideVol+fmout.data[2]*centerVol)>>8)+fmout.data[3])*ssgVol)>>8;
buf[0][h]=os; if (os1<-32768) os1=-32768;
if (os1>32767) os1=32767;
if (os2<-32768) os2=-32768;
if (os2>32767) os2=32767;
buf[0][h]=os+os1;
buf[1][h]=os+os2;
} else {
os+=((fmout.data[1]+fmout.data[2]+fmout.data[3])*ssgVol)>>8;
if (os<-32768) os=-32768;
if (os>32767) os=32767;
buf[0][h]=os;
if (extendedSSG) buf[1][h]=os;
}
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
oscBuf[i]->putSample(h,CLAMP(fm_nuked.ch_out[i]<<1,-32768,32767)); oscBuf[i]->putSample(h,CLAMP(fm_nuked.ch_out[i]<<1,-32768,32767));
@ -1241,7 +1254,7 @@ DivMacroInt* DivPlatformYM2203::getChanMacroInt(int ch) {
} }
DivDispatchOscBuffer* DivPlatformYM2203::getOscBuffer(int ch) { DivDispatchOscBuffer* DivPlatformYM2203::getOscBuffer(int ch) {
return oscBuf[ch]; return (ch<(3+2+4+isCSM))?oscBuf[ch]:NULL;
} }
unsigned char* DivPlatformYM2203::getRegisterPool() { unsigned char* DivPlatformYM2203::getRegisterPool() {
@ -1344,7 +1357,7 @@ void DivPlatformYM2203::reset() {
} }
int DivPlatformYM2203::getOutputCount() { int DivPlatformYM2203::getOutputCount() {
return 1; return extendedSSG?2:1;
} }
bool DivPlatformYM2203::keyOffAffectsArp(int ch) { bool DivPlatformYM2203::keyOffAffectsArp(int ch) {
@ -1423,6 +1436,25 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) {
fbAllOps=flags.getBool("fbAllOps",false); fbAllOps=flags.getBool("fbAllOps",false);
ssgVol=flags.getInt("ssgVol",128); ssgVol=flags.getInt("ssgVol",128);
fmVol=flags.getInt("fmVol",256); fmVol=flags.getInt("fmVol",256);
stereo=flags.getBool("stereo",false);
stereoSep=flags.getInt("stereoSep",0)&255;
switch (flags.getInt("panLaw",0)) {
case 1:
centerVol=sqrtf((stereoSep+256)/512.f)*256.f;
sideVol=sqrtf(stereoSep/256.f)*256.f;
break;
case 2:
centerVol=(stereoSep+256)/2;
sideVol=stereoSep;
break;
default:
centerVol=256;
sideVol=stereoSep;
break;
}
ayFlags.set("stereo",stereo);
ayFlags.set("stereoSep",stereoSep);
ayFlags.set("panLaw",flags.getInt("panLaw",0));
if (useCombo==2) { if (useCombo==2) {
rate=chipClock/(fmDivBase*2); rate=chipClock/(fmDivBase*2);
} else { } else {
@ -1451,7 +1483,7 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, const DivCo
// YM2149, 2MHz // YM2149, 2MHz
ay=new DivPlatformAY8910(true,chipClock,ayDiv); ay=new DivPlatformAY8910(true,chipClock,ayDiv);
ay->setCore(0); ay->setCore(0);
ay->init(p,3,sugRate,ayFlags); ay->init(p,extendedSSG?4:3,sugRate,ayFlags);
ay->toggleRegisterDump(true); ay->toggleRegisterDump(true);
setFlags(flags); setFlags(flags);

View file

@ -55,6 +55,11 @@ class DivPlatformYM2203: public DivPlatformOPN {
bool lastS; bool lastS;
DivPlatformAY8910* ay; DivPlatformAY8910* ay;
bool extendedSSG;
bool stereo;
unsigned char stereoSep;
unsigned short sideVol;
unsigned short centerVol;
unsigned char sampleBank; unsigned char sampleBank;
bool extMode, noExtMacros; bool extMode, noExtMacros;

View file

@ -739,6 +739,7 @@ DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) {
} }
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) { DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
if (ch>9) return NULL;
if (ch>=6) return oscBuf[ch-3]; if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch]; if (ch<3) return oscBuf[ch];
return NULL; return NULL;
@ -791,7 +792,7 @@ int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, con
isOpMuted[i]=false; isOpMuted[i]=false;
} }
extSys=true; extSys=true;
if (extendedSSG) ay->setExtended(1);
reset(); reset();
return 3+2+4+isCSM; // 3xPSG + 2xFM + 4xOP + optional CSM return 3+2+4+isCSM; // 3xPSG + 2xFM + 4xOP + optional CSM
} }
@ -809,5 +810,9 @@ void DivPlatformYM2203Ext::setCSM(bool isCSM) {
} }
} }
void DivPlatformYM2203Ext::setExtSSG(bool isExtended) {
extendedSSG=isExtended;
}
DivPlatformYM2203Ext::~DivPlatformYM2203Ext() { DivPlatformYM2203Ext::~DivPlatformYM2203Ext() {
} }

View file

@ -45,6 +45,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit(); void quit();
void setCSM(bool isCSM); void setCSM(bool isCSM);
void setExtSSG(bool isExtended);
~DivPlatformYM2203Ext(); ~DivPlatformYM2203Ext();
}; };

View file

@ -146,6 +146,7 @@ enum DivSystem {
DIV_SYSTEM_UPD1771C, DIV_SYSTEM_UPD1771C,
DIV_SYSTEM_SID3, DIV_SYSTEM_SID3,
DIV_SYSTEM_C64_PCM, DIV_SYSTEM_C64_PCM,
DIV_SYSTEM_YM2203_CSM_ENV,
DIV_SYSTEM_MAX DIV_SYSTEM_MAX
}; };

View file

@ -1252,6 +1252,20 @@ void DivEngine::registerSystems() {
fmExtChEffectHandlerMap fmExtChEffectHandlerMap
); );
sysDefs[DIV_SYSTEM_YM2203_CSM_ENV]=new DivSysDef(
_("Yamaha YM2203 (OPN) CSM + Envelope"), NULL, 0xfe, 0, 11, true, true, 0x151, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 0, 0,
_("cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)\nthis one is in Extended Channel mode, which turns the third FM channel into four operators with independent notes/frequencies."
"\nthis one includes CSM mode control for special effects on Channel 3."),
{_("FM 1"), _("FM 2"), _("FM 3 OP1"), _("FM 3 OP2"), _("FM 3 OP3"), _("FM 3 OP4"), _("CSM Timer"), _("PSG 1"), _("PSG 2"), _("PSG 3"), _("Envelope")},
{"F1", "F2", "O1", "O2", "O3", "O4", "CSM", "S1", "S2", "S3", "ENV"},
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE},
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY},
{},
{},
fmOPNPostEffectHandlerMap,
fmExtChEffectHandlerMap
);
sysDefs[DIV_SYSTEM_YM2608]=new DivSysDef( sysDefs[DIV_SYSTEM_YM2608]=new DivSysDef(
_("Yamaha YM2608 (OPNA)"), NULL, 0x8e, 0, 16, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT), 0, 0, _("Yamaha YM2608 (OPNA)"), NULL, 0x8e, 0, 16, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT), 0, 0,
_("OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels."), _("OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels."),

View file

@ -2195,6 +2195,10 @@ void FurnaceGUI::doUndo() {
} }
} }
if (shallReplay && e->isPlaying()) play(); if (shallReplay && e->isPlaying()) play();
if (cursor.order!=curOrder) {
e->setOrder(cursor.order);
}
if (curOrder>=e->curSubSong->ordersLen) { if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1; curOrder=e->curSubSong->ordersLen-1;
@ -2272,6 +2276,10 @@ void FurnaceGUI::doRedo() {
} }
if (shallReplay && e->isPlaying()) play(); if (shallReplay && e->isPlaying()) play();
if (cursor.order!=curOrder) {
e->setOrder(cursor.order);
}
if (curOrder>=e->curSubSong->ordersLen) { if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1; curOrder=e->curSubSong->ordersLen-1;
e->setOrder(curOrder); e->setOrder(curOrder);

View file

@ -8083,7 +8083,7 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
if (!ins->amiga.useSample) { if (!ins->amiga.useSample) {
macroList.push_back(FurnaceGUIMacroDesc(_("Noise Freq"),&ins->std.dutyMacro,0,31,160,uiColors[GUI_COLOR_MACRO_NOISE])); macroList.push_back(FurnaceGUIMacroDesc(_("Noise Freq"),&ins->std.dutyMacro,0,31,160,uiColors[GUI_COLOR_MACRO_NOISE],true,macroRelativeMode));
macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,3,48,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,true,ayShapeBits)); macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,3,48,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,true,ayShapeBits));
} }
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));

View file

@ -359,6 +359,13 @@ void FurnaceGUI::drawOrders() {
if (orderEditMode==0) { if (orderEditMode==0) {
handleUnimportant; handleUnimportant;
} }
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && cursor.order==selStart.order &&
cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y && cursor.order==selEnd.order) {
cursor.order=curOrder;
selStart=cursor;
selEnd=cursor;
}
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor();
for (int j=0; j<e->getTotalChannelCount(); j++) { for (int j=0; j<e->getTotalChannelCount(); j++) {
@ -398,6 +405,14 @@ void FurnaceGUI::drawOrders() {
orderCursor=j; orderCursor=j;
curNibble=false; curNibble=false;
} }
// i wonder whether this is necessary
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && cursor.order==selStart.order &&
cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y && cursor.order==selEnd.order) {
cursor.order=curOrder;
selStart=cursor;
selEnd=cursor;
}
} }
if (orderEditMode==0) { if (orderEditMode==0) {
@ -439,6 +454,13 @@ void FurnaceGUI::drawOrders() {
orderCursor=j; orderCursor=j;
curNibble=false; curNibble=false;
} }
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && cursor.order==selStart.order &&
cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y && cursor.order==selEnd.order) {
cursor.order=curOrder;
selStart=cursor;
selEnd=cursor;
}
} }
} }
} }

View file

@ -3242,6 +3242,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_YM2203_CSM, 1.0f, 0, "clockSel=3") CH(DIV_SYSTEM_YM2203_CSM, 1.0f, 0, "clockSel=3")
} }
); );
SUB_ENTRY(
_("Yamaha YM2203 (CSM) with Envelope"), {
CH(DIV_SYSTEM_YM2203_CSM_ENV, 1.0f, 0, "clockSel=3")
}
);
ENTRY( ENTRY(
_("Yamaha YM2608 (OPNA)"), { _("Yamaha YM2608 (OPNA)"), {
CH(DIV_SYSTEM_YM2608, 1.0f, 0, "") CH(DIV_SYSTEM_YM2608, 1.0f, 0, "")

View file

@ -1530,7 +1530,7 @@ void FurnaceGUI::drawSampleEdit() {
/*if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) { /*if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) {
avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f); avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f);
}*/ }*/
avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; avail.y-=ImGui::GetFrameHeightWithSpacing()+ImGui::GetStyle().ScrollbarSize;
if (avail.y<1.0) { // Prevents crash if (avail.y<1.0) { // Prevents crash
avail.y=1.0; avail.y=1.0;
} }
@ -1852,6 +1852,7 @@ void FurnaceGUI::drawSampleEdit() {
String statusBar=sampleDragMode?_("Draw"):_("Select"); String statusBar=sampleDragMode?_("Draw"):_("Select");
String statusBar2=""; String statusBar2="";
String statusBar3=fmt::sprintf(_("%d samples, %d bytes"),sample->samples,sample->getCurBufLen()); String statusBar3=fmt::sprintf(_("%d samples, %d bytes"),sample->samples,sample->getCurBufLen());
String statusBar4="";
bool drawSelection=false; bool drawSelection=false;
if (!sampleDragMode) { if (!sampleDragMode) {
@ -1863,10 +1864,8 @@ void FurnaceGUI::drawSampleEdit() {
end^=start; end^=start;
start^=end; start^=end;
} }
if (start==end) { if (start!=end) {
statusBar+=fmt::sprintf(" (%d)",start); statusBar4=fmt::sprintf(_("(%d samples)"),end-start);
} else {
statusBar+=fmt::sprintf(_(" (%d-%d: %d samples)"),start,end,end-start);
} }
drawSelection=true; drawSelection=true;
} }
@ -2070,27 +2069,55 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar.c_str()); ImGui::TextUnformatted(statusBar.c_str());
if (!sampleDragMode) {
ImGui::SameLine();
ImGui::SetNextItemWidth(140.0f*dpiScale);
if (ImGui::InputInt("##SESelStart",&sampleSelStart)) {
if (sampleSelStart<0) sampleSelStart=0;
if (sampleSelStart>(int)sample->samples) sampleSelStart=sample->samples;
if (sampleSelEnd<sampleSelStart) sampleSelEnd=sampleSelStart;
}
ImGui::SameLine();
ImGui::SetNextItemWidth(140.0f*dpiScale);
if (ImGui::InputInt("##SESelEnd",&sampleSelEnd)) {
if (sampleSelEnd<0) sampleSelEnd=0;
if (sampleSelEnd>(int)sample->samples) sampleSelEnd=sample->samples;
if (sampleSelEnd<sampleSelStart) sampleSelEnd=sampleSelStart;
}
if (!statusBar4.empty()) {
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar4.c_str());
}
}
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (!warnRate.empty()) { if (!warnRate.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar2.c_str()); ImGui::TextUnformatted(statusBar2.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",warnRate.c_str()); ImGui::SetTooltip("%s",warnRate.c_str());
} }
} else { } else {
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar2.c_str()); ImGui::TextUnformatted(statusBar2.c_str());
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (!warnLength.empty()) { if (!warnLength.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar3.c_str()); ImGui::TextUnformatted(statusBar3.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",warnLength.c_str()); ImGui::SetTooltip("%s",warnLength.c_str());
} }
} else { } else {
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(statusBar3.c_str()); ImGui::TextUnformatted(statusBar3.c_str());
} }

View file

@ -1343,6 +1343,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
} }
case DIV_SYSTEM_YM2203: case DIV_SYSTEM_YM2203:
case DIV_SYSTEM_YM2203_EXT: case DIV_SYSTEM_YM2203_EXT:
case DIV_SYSTEM_YM2203_CSM_ENV:
case DIV_SYSTEM_YM2203_CSM: { case DIV_SYSTEM_YM2203_CSM: {
int clockSel=flags.getInt("clockSel",0); int clockSel=flags.getInt("clockSel",0);
int prescale=flags.getInt("prescale",0); int prescale=flags.getInt("prescale",0);
@ -1350,6 +1351,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
bool fbAllOps=flags.getBool("fbAllOps",false); bool fbAllOps=flags.getBool("fbAllOps",false);
int ssgVol=flags.getInt("ssgVol",128); int ssgVol=flags.getInt("ssgVol",128);
int fmVol=flags.getInt("fmVol",256); int fmVol=flags.getInt("fmVol",256);
// these are for the SSG part
int panLaw=flags.getInt("panLaw",0);
bool stereo=flags.getBool("stereo",false);
int stereoSep=flags.getInt("stereoSep",0);
ImGui::Text(_("Clock rate:")); ImGui::Text(_("Clock rate:"));
ImGui::Indent(); ImGui::Indent();
@ -1415,6 +1420,39 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
} }
} }
if (type==DIV_SYSTEM_YM2203_CSM_ENV) {
if (ImGui::Checkbox(_("Stereo##_AY_STEREO"),&stereo)) {
altered=true;
}
if (stereo) {
int sep=256-(stereoSep&255);
if (CWSliderInt(_("Separation"),&sep,1,256)) {
if (sep<1) sep=1;
if (sep>256) sep=256;
stereoSep=256-sep;
altered=true;
}
ImGui::Text(_("Center level:"));
ImGui::Indent();
if (ImGui::RadioButton(_("-0 dB (VGMPlay)"),panLaw==0)) {
panLaw=0;
altered=true;
}
if (ImGui::RadioButton(_("-3 dB"),panLaw==1)) {
panLaw=1;
altered=true;
}
if (ImGui::RadioButton(_("-6 dB (most hardwares)"),panLaw==2)) {
panLaw=2;
altered=true;
}
ImGui::Unindent();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("note: not supported by the VGM format!"));
}
}
}
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel); flags.set("clockSel",clockSel);
@ -1423,6 +1461,9 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
flags.set("fbAllOps",fbAllOps); flags.set("fbAllOps",fbAllOps);
flags.set("ssgVol",ssgVol); flags.set("ssgVol",ssgVol);
flags.set("fmVol",fmVol); flags.set("fmVol",fmVol);
flags.set("stereo",stereo);
flags.set("stereoSep",stereoSep);
flags.set("panLaw",panLaw);
}); });
} }
break; break;