From 1e7acfe5ceb79e5a144afe61a51b2ca03baa57c5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 19 Jan 2024 13:32:56 -0500 Subject: [PATCH 001/127] ESFM: cherry-pick new documentation --- doc/4-instrument/fm-esfm.md | 93 ++++++++++++++++++++++++++++++------- doc/4-instrument/fm-opl.md | 2 +- doc/7-systems/esfm.md | 8 +++- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/doc/4-instrument/fm-esfm.md b/doc/4-instrument/fm-esfm.md index 50fdec3d8..2de2ca9b4 100644 --- a/doc/4-instrument/fm-esfm.md +++ b/doc/4-instrument/fm-esfm.md @@ -7,13 +7,13 @@ the ESFM editor is divided into 6 tabs: - **Macros (OP2)**: for macros controlling FM parameters of operator 2. - **Macros (OP3)**: for macros controlling FM parameters of operator 3. - **Macros (OP4)**: for macros controlling FM parameters of operator 4. -- **Macros**: for other macros (volume/arp/pitch/pan/noise mode). +- **Macros**: for other macros (volume/arp/pitch/pan/operator 4 noise mode). ## FM -ESFM is four-operator, but it is different from the rest of FM chips. +ESFM is four-operator, meaning it takes four oscillators to produce a single sound. -the concept of an algorithm does not exist in ESFM. instead, modulation routing is arbitrary, with each operator having output level and modulation input parameters. +unlike most four-operator FM synthesizers, however, ESFM does not have an algorithm selection. instead, it uses a fixed operator arrangement, but allows you to independently control the output and modulation input levels of each operator. this allows it to reproduce a few common four-operator algorithms, as well as unique combinations where operators act as modulators and carriers at the same time. these apply to the instrument as a whole: - **OP4 Noise Mode**: determines the mode used to produce noise in operator 4. @@ -21,57 +21,114 @@ these apply to the instrument as a whole: - Snare: takes the snare noise generation mode from OPL. square + noise. - HiHat: ring modulates with operator 3 and adds noise. - Top: ring modulates with operator 3 and double pitch modulation input. - - this mode is not emulated correctly. subject to change! + - these are normally used for the drum channels in ESFM's OPL3 compatibility mode. + - however, in ESFM, operator 4 can be modulated by operator 3, whereas in OPL3 drum mode the rhythm channels cannot be modulated. + - __note__: usage of noise mode "Top" is discouraged for now as it is not properly emulated yet, and results may change when the emulation gets fixed in the future. +- **operator routing preview**: shows how operators are connected with each other and with the audio output (at the bottom). + - left-click pops up a small "operators changes with volume?" dialog where each operator can be toggled to scale with volume level. + - right-click switches to a preview display of the waveform generated on a new note: + - left-click restarts the preview. + - middle-click pauses and unpauses the preview. + - right-click returns to algorithm view. these apply to each operator: - the crossed-arrows button can be dragged to rearrange operators. - **Amplitude Modulation (AM)**: makes the operator affected by LFO tremolo. -- **Sustain flag (SUS)**: when enabled, value of Sustain Level is in effect. -- **AM Depth (AMD)**: when enabled, LFO tremolo is deeper. +- **AM Depth (DAM/AMD)**: when enabled, LFO tremolo is deeper (1dB off; 4.8dB on). +- **Sustain flag (SUS)**: when enabled, the envelope pauses ("sustains") once it reaches the Sustain Level and does not proceed to the release phase until note off. +- **Envelope Delay (DL)**: determines the delay time before the envelope is triggered. the bigger the value, the longer the delay (0 to 7). + - a change of one unit doubles or halves the delay time. + - a value of 0 results in no delay. - **Attack Rate (AR)**: determines the rising time for the sound. the bigger the value, the faster the attack (0 to 15). - **Decay Rate (DR)**: determines the diminishing time for the sound. the higher the value, the shorter the decay. it's the initial amplitude decay rate (0 to 15). - **Sustain Level (SL)**: determines the point at which the sound ceases to decay and changes to a sound having a constant level. the sustain level is expressed as a fraction of the maximum level (0 to 15). - **Release Rate (RR)**: determines the rate at which the sound disappears after note off. the higher the value, the shorter the release (0 to 15). - - **Total Level (TL)**: represents the envelope’s highest amplitude, with 0 being the largest and 63 (decimal) the smallest. a change of one unit is about 0.75 dB. -- **Output Level (OL)**: determines the volume at which the operator will be output. -- **Modulation Input (MI)**: determines how much to take from the previous operator for modulation. - - this controls feedback level in the case of operator 1. - **Key Scale Level (KSL)**: also known as "Level Scale". determines the degree to which the amplitude decreases according to the pitch. ![FM ADSR chart](FM-ADSRchart.png) - **Key Scale Rate (KSR)**: also known as "Rate Scale". determines the degree to which the envelope execution speed increases according to the pitch. - **Frequency Multiplier (MULT)**: sets the coarse pitch offset in relation to the note (0 to 15). 0 is -1 octave, 1 is 0 octaves, 2 is 1 octave, 3 is 1 octave 7 semitones, and so on. - - note that values 11, 13 and 14 behave as 10, 12 and 12 respectively. + - note that values 11, 13 and 14 behave as 10, 12 and 15 respectively. +- **Tune (CT)**: sets the semitone offset in relation to the note (-24 to 24). + - this is a software effect. +- **Fine Detune (DT)**: shifts the pitch in fine steps (-128 to 127). 0 is the base pitch, -128 is -1 semitone, 127 is nearly +1 semitone. + - this is a software effect. +- **Left (L)**: toggles output to the left channel from the operator to the audio output. +- **Right (R)**: toggles output to the right channel from the operator to the audio output. - **Waveform Select (WS)**: changes the waveform of the operator (0 to 7). - **Vibrato (VIB)**: makes the operator affected by LFO vibrato. -- **FM Depth (FMD)**: when enabled, vibrato is deeper. +- **Vibrato Depth (DVB/FMD)**: when enabled, vibrato is deeper. -- **Tune**: sets the coarse tune of the operator. -- **Detune**: sets the fine tune of the operator. +### routing controls -- **Left (L)**: output on the left channel. -- **Right (R)**: output on the right channel. +- **Output Level (OL)**: sets the output level from this operator to the audio output (0 to 7). + - 7 is the loudest level and 1 is the softest, while 0 disables audio output. + - a change of one unit is about 6 dB. + - this output scaling factor is applied after TL and envelope scaling have been performed. +- **Modulation Input Level (MI)**: sets the modulation level from the previous operator to this operator (0 to 7). + - 7 is the strongest level and 1 is the weakest, while 0 disables modulation. + - a change of one unit is about 6 dB. + - for operator 1 this controls the **feedback level**. + - this modulation scaling factor is applied after the previous operator's TL and envelope scaling have been performed, but is unaffected by OL above. + +### common algorithms + +this table contains a list of modulation input/output level values which resemble common algorithms in Yamaha FM chips. + +__note__: MI1 is not included as it is the feedback level. + +| algorithm | OL1 | MI2 | OL2 | MI3 | OL3 | MI4 | OL4 | +|--------------------------------|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| OPN algorithm **0** | 0 | 7 | 0 | 7 | 0 | 7 | 7 | +| OPN algorithm **4** | 0 | 7 | 7 | 0 | 0 | 7 | 7 | +| OPN algorithm **6** | 0 | 7 | 7 | 0 | 7 | 0 | 7 | +| OPN algorithm **7** | 7 | 0 | 7 | 0 | 7 | 0 | 7 | +| OPL3 algorithm **1** | 7 | 0 | 0 | 7 | 0 | 7 | 7 | +| OPL3 algorithm **3** | 7 | 0 | 0 | 7 | 7 | 0 | 7 | +| OPL3 algorithm **1** (variant) | 0 | 7 | 0 | 7 | 7 | 0 | 7 | ### fixed frequency mode each operator has a Fixed Frequency mode. once enabled, the operator runs at the specified frequency regardless of the note. +when fixed frequency mode is enabled, the Tune and Fine Detune sliders will be replaced by **Block (Blk)** and **FreqNum (F)**, which allow you to input a raw frequency value into the operator. +the actual frequency is determined by the formula: `FreqNum*(2^Block)`. + +in other words, FreqNum defines the base frequency, while Block determines the scaling factor in octaves. + ## macros these macros allow you to control several parameters of FM per tick. ## OP1-OP4 Macros -all parameters are listed above. +most parameters are listed above. + +### envelope delay macro tricks + +due to a quirk in how the envelope delay feature works, the **Envelope Delay** macro can control the operator's key-on status for a limited amount of time after a note is played. a value of 0 represents key-on, while a value of 7 represents key-off. + +note that the macro cannot exceed 659.1 ms in length - anything beyond that will be treated as a value of 0. + +### operator arpeggio and pitch macros + +among the available macros are **Op. Arpeggio** and **Op. Pitch**. these work like the **Arpeggio** and **Pitch** macros featured below, but are applied to the individual operator, overriding the **Arpeggio**/**Pitch** macros respectively. + +the **Tune** and **Fine Detune** FM parameters are still respected when using these macros. + +### fixed frequency macros + +when fixed frequency is enabled for an operator, the **Op. Arpeggio** and **Op. Pitch** macros will be replaced by the **Block** and **FreqNum** macros. these can be used to change the operator's fixed frequency over time. ## Macros - **Volume**: volume sequence. - **Arpeggio**: pitch sequence. -- **OP4 Noise Mode**: noise mode sequence. +- **OP4 Noise Mode**: operator 4 noise mode sequence. - **Panning**: enables output on left/right channels. + - note that each operator also has its own pan controls, which get masked by this global pan control. - **Pitch**: fine pitch. - **Relative**: when enabled, pitch changes are relative to the current pitch. - **Phase Reset**: restarts all operators and resets the waveform to its start. diff --git a/doc/4-instrument/fm-opl.md b/doc/4-instrument/fm-opl.md index bfd979651..5879abcf8 100644 --- a/doc/4-instrument/fm-opl.md +++ b/doc/4-instrument/fm-opl.md @@ -29,7 +29,7 @@ these apply to the instrument as a whole: these apply to each operator: - the crossed-arrows button can be dragged to rearrange operators. - **Amplitude Modulation (AM)**: makes the operator affected by LFO tremolo. -- **Sustain flag (SUS)**: when enabled, value of Sustain Level is in effect. +- **Sustain flag (SUS)**: when enabled, the envelope pauses ("sustains") once it reaches the Sustain Level and does not proceed to the release phase until note off. - **Attack Rate (AR)**: determines the rising time for the sound. the bigger the value, the faster the attack (0 to 15). - **Decay Rate (DR)**: determines the diminishing time for the sound. the higher the value, the shorter the decay. it's the initial amplitude decay rate (0 to 15). - **Sustain Level (SL)**: determines the point at which the sound ceases to decay and changes to a sound having a constant level. the sustain level is expressed as a fraction of the maximum level (0 to 15). diff --git a/doc/7-systems/esfm.md b/doc/7-systems/esfm.md index bd53589a4..cdff9d860 100644 --- a/doc/7-systems/esfm.md +++ b/doc/7-systems/esfm.md @@ -1,8 +1,12 @@ # ESS ESFM -an enhanced version of Yamaha's OPL3, adding many features which weren't present on the original chip, such as 4-op on all channels, coarse/fine detune, per-op panning, envelope delay, noise generator and advanced modulation routing (no more algorithms!). +an FM synthesizer core included in a series of sound card chipsets made by ESS, which were mildly popular in the DOS days during the mid-late 90s. -the technology was present in many of ESS' sound cards (the ES1xxx series in particular). +at a cursory glance, it looks like just an [OPL3 clone](opl.md). but hidden under a veil of mystery is its exclusive "native mode", revealing an impressive superset of features, including 4-operator support on all 18 channels, semi-modular operator routing, per-operator pitch control, and even a few unique features. + +for a long time, not much was known about the inner workings of ESFM's native mode, since ESS did not release any documentation to developers on how to use it. this has thankfully changed in recent years thanks to reverse-engineering efforts from the community. + +thanks to ESS's decision to not release any documentation to developers and lock down usage of native mode behind a couple of General MIDI drivers shipping with rather lackluster patch sets, ESFM's native mode was unfortunately not very well used over its original lifespan. ## effects From 54c6a05710c47de95204fbc70339c03056d3a49b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 19 Jan 2024 16:23:27 -0500 Subject: [PATCH 002/127] GUI: tell the user when ins preview is happening --- src/gui/insEdit.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e96d1b64e..ad8b524fd 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2916,7 +2916,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); } if (ImGui::Begin("Instrument Editor",&insEditOpen,globalWinFlags|(settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking))) { - if (curIns<0 || curIns>=(int)e->song.ins.size()) { + if (curIns==-2) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)*0.5f); + CENTER_TEXT("waiting..."); + ImGui::Text("waiting..."); + } else if (curIns<0 || curIns>=(int)e->song.ins.size()) { ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()*(e->song.ins.empty()?2.0f:3.0f)+ImGui::GetStyle().ItemSpacing.y)*0.5f); CENTER_TEXT("no instrument selected"); ImGui::Text("no instrument selected"); From f31f9a454ded66134852bfee99cc08485f9478d9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 20 Jan 2024 02:34:21 -0500 Subject: [PATCH 003/127] fix instrument load preview --- src/engine/engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 1e13e3ccb..340e74c2e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -174,7 +174,7 @@ struct DivChannelState { struct DivNoteEvent { signed char channel; - unsigned char ins; + short ins; signed char note, volume; bool on, nop, insChange, fromMIDI; DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false): From f322316a32b657b67d45ceaaaeeff4656877f667 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 20 Jan 2024 02:51:20 -0500 Subject: [PATCH 004/127] GUI: fix paste overflow undo corruption, part 1 TODO: garbage collection --- src/gui/editing.cpp | 83 ++++++++++++++++++++++++++++++++++----------- src/gui/gui.cpp | 8 ----- src/gui/gui.h | 16 ++++++--- 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index fd2715bc8..891cc3d41 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -52,7 +52,16 @@ const char* FurnaceGUI::noteNameNormal(short note, short octave) { return noteNames[seek]; } -void FurnaceGUI::prepareUndo(ActionType action) { +void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { + if (region.begin.ord==-1) { + region.begin.ord=curOrder; + region.end.ord=curOrder; + region.begin.x=0; + region.end.x=e->getTotalChannelCount()-1; + region.begin.y=0; + region.end.y=e->curSubSong->patLen-1; + } + switch (action) { case GUI_UNDO_CHANGE_ORDER: memcpy(&oldOrders,e->curOrders,sizeof(DivOrders)); @@ -74,8 +83,21 @@ void FurnaceGUI::prepareUndo(ActionType action) { case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_EXPAND: case GUI_UNDO_PATTERN_DRAG: - for (int i=0; igetTotalChannelCount(); i++) { - e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]); + for (int h=region.begin.ord; h<=region.end.ord; h++) { + for (int i=region.begin.x; i<=region.end.x; i++) { + unsigned short id=h|(i<<8); + DivPattern* p=NULL; + + auto it=oldPatMap.find(id); + if (it==oldPatMap.end()) { + p=oldPatMap[id]=new DivPattern; + logV("oldPatMap: allocating for %.4x",id); + } else { + p=it->second; + } + + e->curPat[i].getPattern(e->curOrders->ord[i][h],false)->copyOn(p); + } } break; case GUI_UNDO_PATTERN_COLLAPSE_SONG: @@ -86,7 +108,7 @@ void FurnaceGUI::prepareUndo(ActionType action) { } } -void FurnaceGUI::makeUndo(ActionType action) { +void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { bool doPush=false; bool shallWalk=false; UndoStep s; @@ -99,6 +121,16 @@ void FurnaceGUI::makeUndo(ActionType action) { s.newOrdersLen=e->curSubSong->ordersLen; s.nibble=curNibble; size_t subSong=e->getCurrentSubSong(); + + if (region.begin.ord==-1) { + region.begin.ord=curOrder; + region.end.ord=curOrder; + region.begin.x=0; + region.end.x=e->getTotalChannelCount()-1; + region.begin.y=0; + region.end.y=e->curSubSong->patLen-1; + } + switch (action) { case GUI_UNDO_CHANGE_ORDER: for (int i=0; igetTotalChannelCount(); i++) { - DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false); - for (int j=0; jcurSubSong->patLen; j++) { - for (int k=0; kdata[j][k]!=oldPat[i]->data[j][k]) { - s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k])); + for (int h=region.begin.ord; h<=region.end.ord; h++) { + for (int i=region.begin.x; i<=region.end.x; i++) { + DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][h],false); + DivPattern* op=NULL; + unsigned short id=h|(i<<8); - if (k>=4) { - if (oldPat[i]->data[j][k&(~1)]==0x0b || - p->data[j][k&(~1)]==0x0b || - oldPat[i]->data[j][k&(~1)]==0x0d || - p->data[j][k&(~1)]==0x0d || - oldPat[i]->data[j][k&(~1)]==0xff || - p->data[j][k&(~1)]==0xff) { - shallWalk=true; + auto it=oldPatMap.find(id); + if (it==oldPatMap.end()) { + logW("no data in oldPatMap for channel %d!",i); + continue; + } else { + op=it->second; + } + + for (int j=region.begin.y; j<=region.end.y; j++) { + for (int k=0; kdata[j][k]!=op->data[j][k]) { + s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->data[j][k],p->data[j][k])); + + if (k>=4) { + if (op->data[j][k&(~1)]==0x0b || + p->data[j][k&(~1)]==0x0b || + op->data[j][k&(~1)]==0x0d || + p->data[j][k&(~1)]==0x0d || + op->data[j][k&(~1)]==0xff || + p->data[j][k&(~1)]==0xff) { + shallWalk=true; + } } - } + } } } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 02cb0beef..b06444252 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6880,10 +6880,6 @@ bool FurnaceGUI::init() { ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable; toggleMobileUI(mobileUI,true); - for (int i=0; i oldPatMap; FixedQueue undoHist; FixedQueue redoHist; @@ -2536,8 +2544,8 @@ class FurnaceGUI { void moveCursorTop(bool select); void moveCursorBottom(bool select); void editAdvance(); - void prepareUndo(ActionType action); - void makeUndo(ActionType action); + void prepareUndo(ActionType action, UndoRegion region=UndoRegion()); + void makeUndo(ActionType action, UndoRegion region=UndoRegion()); void doSelectAll(); void doDelete(); void doPullDelete(); From 6f7cb8ce06587ca043b465a3222300890a14e0cb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 20 Jan 2024 12:07:31 -0500 Subject: [PATCH 005/127] GUI: fix paste overflow undo corruption, part 2 --- src/gui/editing.cpp | 65 +++++++++++++++++++++++++++++++++++++++------ src/gui/gui.h | 4 +-- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 891cc3d41..abce533a9 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -60,6 +60,19 @@ void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { region.end.x=e->getTotalChannelCount()-1; region.begin.y=0; region.end.y=e->curSubSong->patLen-1; + } else { + if (region.begin.ord<0) region.begin.ord=0; + if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen; + if (region.end.ord<0) region.end.ord=0; + if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen; + if (region.begin.x<0) region.begin.x=0; + if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1; + if (region.end.x<0) region.end.x=0; + if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1; + if (region.begin.y<0) region.begin.y=0; + if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1; + if (region.end.y<0) region.end.y=0; + if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1; } switch (action) { @@ -129,6 +142,19 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { region.end.x=e->getTotalChannelCount()-1; region.begin.y=0; region.end.y=e->curSubSong->patLen-1; + } else { + if (region.begin.ord<0) region.begin.ord=0; + if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen; + if (region.end.ord<0) region.end.ord=0; + if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen; + if (region.begin.x<0) region.begin.x=0; + if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1; + if (region.end.x<0) region.end.x=0; + if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1; + if (region.begin.y<0) region.begin.y=0; + if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1; + if (region.end.y<0) region.end.y=0; + if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1; } switch (action) { @@ -177,7 +203,13 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { op=it->second; } - for (int j=region.begin.y; j<=region.end.y; j++) { + int jBegin=0; + int jEnd=e->curSubSong->patLen-1; + + if (h==region.begin.ord) jBegin=region.begin.y; + if (h==region.end.ord) jEnd=region.end.y; + + for (int j=jBegin; j<=jEnd; j++) { for (int k=0; kdata[j][k]!=op->data[j][k]) { s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->data[j][k],p->data[j][k])); @@ -505,7 +537,7 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s return clipb; } -void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData) +void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData, UndoRegion ur) { if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; if (startOff<0) return; @@ -647,7 +679,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str updateScroll(cursor.y); } - makeUndo(GUI_UNDO_PATTERN_PASTE); + makeUndo(GUI_UNDO_PATTERN_PASTE,ur); } } @@ -864,7 +896,7 @@ unsigned int convertEffectMPT_MPTM(unsigned char symbol, unsigned int val) { } // TODO: fix code style -void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat) +void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat, UndoRegion ur) { DETERMINE_LAST; @@ -1189,14 +1221,13 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String updateScroll(cursor.y); } - makeUndo(GUI_UNDO_PATTERN_PASTE); + makeUndo(GUI_UNDO_PATTERN_PASTE,ur); } } void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { if (readClipboard) { finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); char* clipText=SDL_GetClipboardText(); if (clipText!=NULL) { if (clipText[0]) { @@ -1243,10 +1274,28 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli if (!foundString) return; + UndoRegion ur; + if (mode==GUI_PASTE_MODE_OVERFLOW) { + int rows=cursor.y; + int firstPattern=curOrder; + int lastPattern=curOrder; + rows+=data.size(); + while (rows>=e->curSubSong->patLen) { + lastPattern++; + rows-=e->curSubSong->patLen; + } + + ur=UndoRegion(firstPattern,0,0,lastPattern,e->getTotalChannelCount()-1,e->curSubSong->patLen-1); + } + + if (readClipboard) { + prepareUndo(GUI_UNDO_PATTERN_PASTE,ur); + } + if (isFurnace) { - doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData); + doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData,ur); } else if (isModPlug) { - doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat); + doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat,ur); } } diff --git a/src/gui/gui.h b/src/gui/gui.h index c07820eb5..a41251f43 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2552,8 +2552,8 @@ class FurnaceGUI { void doInsert(); void doTranspose(int amount, OperationMask& mask); String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); - void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData); - void doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat); + void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData, UndoRegion ur); + void doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int mptFormat, UndoRegion ur); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate(); From a768583bffb06d6018b2630c5b1eed0d30afe2fc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 20 Jan 2024 12:14:22 -0500 Subject: [PATCH 006/127] GUI: fix paste overflow undo corruption, part 3 garbage collection --- src/gui/editing.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index abce533a9..183560191 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -104,7 +104,7 @@ void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { auto it=oldPatMap.find(id); if (it==oldPatMap.end()) { p=oldPatMap[id]=new DivPattern; - logV("oldPatMap: allocating for %.4x",id); + //logV("oldPatMap: allocating for %.4x",id); } else { p=it->second; } @@ -249,6 +249,12 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { if (shallWalk) { e->walkSong(loopOrder,loopRow,loopEnd); } + + // garbage collection + for (std::pair i: oldPatMap) { + delete i.second; + } + oldPatMap.clear(); } void FurnaceGUI::doSelectAll() { From c2d91a06d39c60c94a72e37d592cb901c5252550 Mon Sep 17 00:00:00 2001 From: Kagamiin~ Date: Sat, 20 Jan 2024 14:09:51 -0300 Subject: [PATCH 007/127] Fix ESFM operator order for KVS dialog --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index ad8b524fd..6ce4e9dfb 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1477,7 +1477,7 @@ void FurnaceGUI::kvsConfig(DivInstrument* ins, bool supportsKVS) { ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); for (int i=0; i<4; i++) { - int o=(opCount==4)?orderedOps[i]:i; + int o=(opCount==4 && ins->type!=DIV_INS_ESFM)?orderedOps[i]:i; if (!(i&1)) ImGui::TableNextRow(); const char* label="AUTO##OPKVS"; if (ins->fm.op[o].kvs==0) { From e19cd39e960c1e985315ce898b574bd3cf36ac32 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 20 Jan 2024 12:41:07 -0500 Subject: [PATCH 008/127] GUI: why is the input latch lowercase --- src/gui/gui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b06444252..359c27e51 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2760,7 +2760,7 @@ void FurnaceGUI::editOptions(bool topMenu) { } else if (latchIns==-1) { strcpy(id,"..##LatchIns"); } else { - snprintf(id,63,"%.2x##LatchIns",latchIns&0xff); + snprintf(id,63,"%.2X##LatchIns",latchIns&0xff); } if (ImGui::Selectable(id,latchTarget==1,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=1; @@ -2780,7 +2780,7 @@ void FurnaceGUI::editOptions(bool topMenu) { if (latchVol==-1) { strcpy(id,"..##LatchVol"); } else { - snprintf(id,63,"%.2x##LatchVol",latchVol&0xff); + snprintf(id,63,"%.2X##LatchVol",latchVol&0xff); } if (ImGui::Selectable(id,latchTarget==2,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=2; @@ -2796,7 +2796,7 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INACTIVE]); } else { const unsigned char data=latchEffect; - snprintf(id,63,"%.2x##LatchFX",data); + snprintf(id,63,"%.2X##LatchFX",data); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } @@ -2811,7 +2811,7 @@ void FurnaceGUI::editOptions(bool topMenu) { if (latchEffectVal==-1) { strcpy(id,"..##LatchFXV"); } else { - snprintf(id,63,"%.2x##LatchFXV",latchEffectVal&0xff); + snprintf(id,63,"%.2X##LatchFXV",latchEffectVal&0xff); } if (ImGui::Selectable(id,latchTarget==4,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=4; From 85997e55e705f361c136f82d49c4e2551d6363b3 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 06:44:29 -0500 Subject: [PATCH 009/127] add Power Noise support --- CMakeLists.txt | 3 +++ src/engine/dispatch.h | 3 +++ src/engine/dispatchContainer.cpp | 4 ++++ src/engine/instrument.cpp | 2 ++ src/engine/instrument.h | 1 + src/engine/playback.cpp | 3 +++ src/engine/song.h | 3 ++- src/engine/sysDef.cpp | 17 +++++++++++++++++ src/gui/gui.h | 1 + src/gui/guiConst.cpp | 4 ++++ src/gui/insEdit.cpp | 16 +++++++++++++++- 11 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a3f8e641..8a8f3805c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,6 +495,8 @@ extern/YMF276-LLE/fmopn2.c extern/ESFMu/esfm.c extern/ESFMu/esfm_registers.c +extern/pwrnoise/pwrnoise.c + src/pch.cpp src/engine/platform/sound/sn76496.cpp @@ -703,6 +705,7 @@ src/engine/platform/k053260.cpp src/engine/platform/ted.cpp src/engine/platform/c140.cpp src/engine/platform/esfm.cpp +src/engine/platform/powernoise.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c459cc3b0..8f9d2f05f 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -248,6 +248,9 @@ enum DivDispatchCmds { DIV_CMD_ESFM_MODIN, // (op, value) DIV_CMD_ESFM_ENV_DELAY, // (op, value) + DIV_CMD_POWER_NOISE_COUNTER_LOAD, // (which, val) + DIV_CMD_POWER_NOISE_IO_WRITE, // (port, value) + DIV_CMD_MACRO_RESTART, // (which) DIV_CMD_MAX diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index ffcbe91eb..d6be1ece8 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -83,6 +83,7 @@ #include "platform/c140.h" #include "platform/pcmdac.h" #include "platform/esfm.h" +#include "platform/powernoise.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -648,6 +649,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_ESFM: dispatch=new DivPlatformESFM; break; + case DIV_SYSTEM_POWER_NOISE: + dispatch=new DivPlatformPowerNoise; + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 71aaeb012..f7f3e26b7 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1043,6 +1043,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo featureFM=true; featureEF=true; break; + case DIV_INS_POWER_NOISE: + break; case DIV_INS_MAX: break; case DIV_INS_NULL: diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 4304d5526..3e7b98e62 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -86,6 +86,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_C140=53, DIV_INS_C219=54, DIV_INS_ESFM=55, + DIV_INS_POWER_NOISE=56, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3ff0560bc..4494736d8 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -247,6 +247,9 @@ const char* cmdName[]={ "ESFM_MODIN", "ESFM_ENV_DELAY", + "POWER_NOISE_COUNTER_LOAD", + "POWER_NOISE_IO_WRITE", + "MACRO_RESTART", }; diff --git a/src/engine/song.h b/src/engine/song.h index 7f29ddb69..e9e1f72e0 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -132,7 +132,8 @@ enum DivSystem { DIV_SYSTEM_TED, DIV_SYSTEM_C140, DIV_SYSTEM_C219, - DIV_SYSTEM_ESFM + DIV_SYSTEM_ESFM, + DIV_SYSTEM_POWER_NOISE, }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 0da85e855..375019671 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1984,6 +1984,23 @@ void DivEngine::registerSystems() { }, fmESFMPostEffectHandlerMap ); + + sysDefs[DIV_SYSTEM_POWER_NOISE]=new DivSysDef( + "BS-C-04xx series (Power Noise)", NULL, 0xd2, 0, 4, false, false, 0, false, 0, 0, 0, + "a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.", + {"Noise 1", "Noise 2", "Noise 3", "Slope"}, + {"N1", "N2", "N3", "SL"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PULSE}, + {DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE}, + {}, + { + {0x20, {DIV_CMD_POWER_NOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}}, + {0x21, {DIV_CMD_POWER_NOISE_COUNTER_LOAD, "21xx: Load high byte of noise channel LFSR (00 to FF)", constVal<1>, effectVal}}, + {0x22, {DIV_CMD_POWER_NOISE_IO_WRITE, "22xx: Write to I/O port A", constVal<0>, effectVal}}, + {0x23, {DIV_CMD_POWER_NOISE_IO_WRITE, "23xx: Write to I/O port B", constVal<1>, effectVal}}, + }, + {} + ); sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0, diff --git a/src/gui/gui.h b/src/gui/gui.h index a41251f43..a5da4d865 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -291,6 +291,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_C140, GUI_COLOR_INSTR_C219, GUI_COLOR_INSTR_ESFM, + GUI_COLOR_INSTR_POWER_NOISE, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index f48b8b906..43638329d 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -176,6 +176,7 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"C140",ICON_FA_VOLUME_UP,ICON_FUR_INS_C140}, {"C219",ICON_FA_VOLUME_UP,ICON_FUR_INS_C219}, {"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM}, + {"Power Noise",ICON_FUR_NOISE,ICON_FA_QUESTION}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; @@ -986,6 +987,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_C140,"",ImVec4(1.0f,1.0f,0.0f,1.0f)), D(GUI_COLOR_INSTR_C219,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_POWER_NOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1203,6 +1205,7 @@ const int availableSystems[]={ DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_ESFM, DIV_SYSTEM_PONG, + DIV_SYSTEM_POWER_NOISE, 0 // don't remove this last one! }; @@ -1292,6 +1295,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_VRC6, DIV_SYSTEM_MMC5, DIV_SYSTEM_SM8521, + DIV_SYSTEM_POWER_NOISE, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 6ce4e9dfb..bd8a3660a 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6936,6 +6936,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_C140) waveMax=0; if (ins->type==DIV_INS_C219) waveMax=0; + if (ins->type==DIV_INS_POWER_NOISE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -7074,6 +7075,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { panMax=4095; } + if (ins->type==DIV_INS_POWER_NOISE) { + panMax=15; + } if (volMax>0) { macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); @@ -7161,7 +7165,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_C140 || ins->type==DIV_INS_C219 || ins->type==DIV_INS_TED || - ins->type==DIV_INS_ESFM) { + ins->type==DIV_INS_ESFM || + ins->type==DIV_INS_POWER_NOISE) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -7254,6 +7259,15 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } + if (ins->type==DIV_INS_POWER_NOISE) { + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,8,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Offset",&ins->std.ex7Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + } drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); From 5d2dade0364b6646154a74465d0cb59333222eb4 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 06:47:38 -0500 Subject: [PATCH 010/127] Duty / Noise no longer appears in PN inst. config --- extern/pwrnoise/LICENSE | 19 ++ extern/pwrnoise/README.md | 4 + extern/pwrnoise/pwrnoise.c | 228 +++++++++++++ extern/pwrnoise/pwrnoise.h | 76 +++++ src/engine/platform/powernoise.cpp | 525 +++++++++++++++++++++++++++++ src/engine/platform/powernoise.h | 101 ++++++ src/gui/insEdit.cpp | 3 + 7 files changed, 956 insertions(+) create mode 100644 extern/pwrnoise/LICENSE create mode 100644 extern/pwrnoise/README.md create mode 100644 extern/pwrnoise/pwrnoise.c create mode 100644 extern/pwrnoise/pwrnoise.h create mode 100644 src/engine/platform/powernoise.cpp create mode 100644 src/engine/platform/powernoise.h diff --git a/extern/pwrnoise/LICENSE b/extern/pwrnoise/LICENSE new file mode 100644 index 000000000..3f7589ab1 --- /dev/null +++ b/extern/pwrnoise/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 scratchminer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extern/pwrnoise/README.md b/extern/pwrnoise/README.md new file mode 100644 index 000000000..0366f408a --- /dev/null +++ b/extern/pwrnoise/README.md @@ -0,0 +1,4 @@ +# pwrnoise +An emulator for the Power Noise fantasy sound chip, part of the [Hexheld](https://github.com/Hexheld/) fantasy console. + +Design by [jvsTSX](https://github.com/jvsTSX/), code by scratchminer. \ No newline at end of file diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c new file mode 100644 index 000000000..114848a7f --- /dev/null +++ b/extern/pwrnoise/pwrnoise.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include + +#include "pwrnoise.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { + switch (reg & 0x1f) { + case 1: + chan->enable = (val & 0x80) != 0; + chan->am = (val & 0x02) != 0; + chan->tapb_enable = (val & 0x01) != 0; + break; + case 2: + chan->period = (chan->period & 0xf00) | val; + break; + case 3: + chan->period = (chan->period & 0xff) | ((uint16_t)val << 8) & 0xf00; + chan->octave = val >> 4; + break; + case 4: + chan->lfsr = (chan->lfsr & 0xff00) | val; + break; + case 5: + chan->lfsr = (chan->lfsr & 0x00ff) | ((uint16_t)val << 8); + break; + case 6: + chan->tapa = val >> 4; + chan->tapb = val & 0x0f; + break; + case 7: + chan->vol = val; + break; + default: break; + } +} + +void pwrnoise_noise_step(noise_channel_t *chan) { + chan->octave_counter++; + if (chan->enable && !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { + if ((++chan->period_counter) == 4096) { + chan->prev = (uint8_t)(chan->lfsr >> 15); + uint16_t in = ((chan->lfsr >> chan->tapa) ^ (chan->tapb_enable ? (chan->lfsr >> chan->tapb) : 0)) & 0x0001; + chan->lfsr = (chan->lfsr << 1) | in; + chan->period_counter = chan->period; + } + } + + uint8_t out = chan->prev; + if (!chan->enable) out = 0; + else if (out != 0) out = chan->vol; + + chan->out_latch = out; +} + +void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { + switch (reg & 0x1f) { + case 0: + chan->accum = val & 0x7f; + break; + case 1: + chan->enable = (val & 0x80) != 0; + if ((val & 0x40) != 0) { + chan->a = 0; + chan->b = 0; + chan->portion = false; + } + chan->flags = val & 0x3f; + break; + case 2: + chan->period = (chan->period & 0xf00) | val; + break; + case 3: + chan->period = (chan->period & 0xff) | ((uint16_t)val << 8) & 0xf00; + chan->octave = val >> 4; + break; + case 4: + chan->alength = val; + break; + case 5: + chan->blength = val; + break; + case 6: + chan->aoffset = val >> 4; + chan->boffset = val & 0x0f; + break; + case 7: + chan->vol = val; + break; + default: break; + } +} + +void pwrnoise_slope_step(slope_channel_t *chan, bool force_zero) { + if (chan->enable && !((chan->octave_counter++ >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { + if ((++chan->period_counter) == 4096) { + if (!chan->portion) { + if ((chan->flags & 0x02) != 0) chan->accum -= chan->aoffset; + else chan->accum += chan->aoffset; + + if ((chan->flags & 0x20) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x02) ? 0x00 : 0x7f; + chan->accum &= 0x7f; + + if (++chan->a > chan->alength) { + if ((chan->flags & 0x04) != 0) chan->accum = (chan->flags & 0x02) ? 0x7f : 0x00; + chan->b = 0x00; + chan->portion = true; + } + } + else { + if ((chan->flags & 0x01) != 0) chan->accum -= chan->boffset; + else chan->accum += chan->boffset; + + if ((chan->flags & 0x10) != 0 && chan->accum > 0x7f) chan->accum = (chan->flags & 0x01) ? 0x00 : 0x7f; + chan->accum &= 0x7f; + + if (++chan->b > chan->blength) { + if ((chan->flags & 0x08) != 0) chan->accum = (chan->flags & 0x01) ? 0x7f : 0x00; + chan->a = 0x00; + chan->portion = false; + } + } + + chan->period_counter = chan->period; + } + } + + uint8_t left = chan->accum >> 3; + uint8_t right = chan->accum >> 3; + + switch (chan->vol >> 4) { + case 0: + case 1: + left >>= 1; + case 2: + case 3: + left >>= 1; + case 4: + case 5: + case 6: + case 7: + left >>= 1; + default: break; + } + switch (chan->vol & 0xf) { + case 0: + case 1: + right >>= 1; + case 2: + case 3: + right >>= 1; + case 4: + case 5: + case 6: + case 7: + right >>= 1; + default: break; + } + + left &= (chan->vol >> 4); + right &= (chan->vol & 0xf); + uint8_t out = (left << 4) | right; + + if (!chan->enable || force_zero) out = 0; + chan->out_latch = out; +} + +void pwrnoise_reset(power_noise_t *pn) { + memset(pn, 0, sizeof(power_noise_t)); +} + +void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val) { + reg &= 0x1f; + printf("%02x %02x\n", reg, val); + + if (reg == 0x00) { + pn->flags = val; + } + else if (reg == 0x08 && !(pn->flags & 0x20)) { + pn->gpioa = val; + } + else if (reg == 0x10 && !(pn->flags & 0x40)) { + pn->gpiob = val; + } + else if (reg < 0x08) { + pwrnoise_noise_write(&pn->n1, reg % 8, val); + } + else if (reg < 0x10) { + pwrnoise_noise_write(&pn->n2, reg % 8, val); + } + else if (reg < 0x18) { + pwrnoise_noise_write(&pn->n3, reg % 8, val); + } + else { + pwrnoise_slope_write(&pn->s, reg % 8, val); + } +} + +void pwrnoise_step(power_noise_t *pn, int16_t *left, int16_t *right) { + int32_t final_left, final_right; + + if ((pn->flags & 0x80) != 0) { + pwrnoise_noise_step(&pn->n1); + pwrnoise_noise_step(&pn->n2); + pwrnoise_noise_step(&pn->n3); + pwrnoise_slope_step(&pn->s, (pn->n1.am && !(pn->n1.prev)) || (pn->n2.am && !(pn->n2.prev)) || (pn->n3.am && !(pn->n3.prev))); + + final_left = (pn->n1.out_latch >> 4) + (pn->n2.out_latch >> 4) + (pn->n3.out_latch >> 4) + (pn->s.out_latch >> 4); + final_right = (pn->n1.out_latch & 0xf) + (pn->n2.out_latch & 0xf) + (pn->n3.out_latch & 0xf) + (pn->s.out_latch & 0xf); + } + else { + final_left = 0; + final_right = 0; + } + + *left = (int16_t)((final_left * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); + *right = (int16_t)((final_right * 65535 / 63 - 32768) * (pn->flags & 0x7) / 7); +} + +#ifdef __cplusplus +} +#endif diff --git a/extern/pwrnoise/pwrnoise.h b/extern/pwrnoise/pwrnoise.h new file mode 100644 index 000000000..c20da5b47 --- /dev/null +++ b/extern/pwrnoise/pwrnoise.h @@ -0,0 +1,76 @@ +#ifndef PWRNOISE_H +#define PWRNOISE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + bool enable; + bool am; + + uint16_t period; + uint16_t period_counter; + + uint8_t octave; + uint16_t octave_counter; + + uint8_t tapa; + uint8_t tapb; + bool tapb_enable; + + uint16_t lfsr; + uint8_t vol; + + uint8_t out_latch; + uint8_t prev; +} noise_channel_t; + +typedef struct { + bool enable; + uint8_t flags; + + uint16_t period; + uint16_t period_counter; + + uint8_t octave; + uint16_t octave_counter; + + uint8_t alength; + uint8_t blength; + uint8_t a; + uint8_t b; + bool portion; + + uint8_t aoffset; + uint8_t boffset; + + uint8_t accum; + uint8_t vol; + + uint8_t out_latch; +} slope_channel_t; + +typedef struct { + uint8_t flags; + uint8_t gpioa; + uint8_t gpiob; + + noise_channel_t n1; + noise_channel_t n2; + noise_channel_t n3; + slope_channel_t s; +} power_noise_t; + +void pwrnoise_reset(power_noise_t *pn); +void pwrnoise_step(power_noise_t *pn, int16_t *left, int16_t *right); +void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp new file mode 100644 index 000000000..ef14c06d3 --- /dev/null +++ b/src/engine/platform/powernoise.cpp @@ -0,0 +1,525 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "powernoise.h" +#include "../engine.h" +#include "furIcons.h" +#include + +#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = v; pwrnoise_write(&pn, (uint8_t)a, (uint8_t)v); if (dumpWrites) {addWrite(a,v);} } +#define cWrite(c,a,v) rWrite((c << 3) | (a + 1), v) +#define noiseCtl(enable, am, tapB) ((enable ? 0x80 : 0x00) | (am ? 0x02 : 0x00) | (tapB ? 0x01 : 0x00)) +#define slopeCtl(enable, rst, a, b) ((enable ? 0x80 : 0x00) | \ + (rst ? 0x40 : 0x00) | \ + (a.clip ? 0x20 : 0x00) | \ + (b.clip ? 0x10 : 0x00) | \ + (a.reset ? 0x08 : 0x00) | \ + (b.reset ? 0x04 : 0x00) | \ + (a.dir ? 0x02 : 0x00) | \ + (b.dir ? 0x01 : 0x00)) +#define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) +#define mapAmp(a) (((a) * 65535 / 15 - 32768) * (pn.flags & 0x7) / 7) + +const char* regCheatSheetPowerNoise[]={ + "ACTL", "00", + "N1CTL", "01", + "N1FL", "02", + "N1FH", "03", + "N1SRL", "04", + "N1SRH", "05", + "N1TAP", "06", + "N1V", "07", + "IOA", "08", + "N2CTL", "09", + "N2FL", "0A", + "N2FH", "0B", + "N2SRL", "0C", + "N2SRH", "0D", + "N2TAP", "0E", + "N2V", "0F", + "IOB", "10", + "N3CTL", "11", + "N3FL", "12", + "N3FH", "13", + "N3SRL", "14", + "N3SRH", "15", + "N3TAP", "16", + "N3V", "17", + "SLACC", "18", + "SLCTL", "19", + "SLFL", "1A", + "SLFH", "1B", + "SLPA", "1C", + "SLPB", "1D", + "SLPO", "1E", + "SLV", "1F", + NULL +}; + +const char** DivPlatformPowerNoise::getRegisterSheet() { + return regCheatSheetPowerNoise; +} + +void DivPlatformPowerNoise::acquire(short** buf, size_t len) { + int16_t left, right; + + for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp(pn.n1.out_latch); + oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(pn.n2.out_latch); + oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(pn.n3.out_latch); + oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(pn.s.out_latch); + + buf[0][h] = left; + buf[1][h] = right; + } +} + +/* macros: + * EX1 - control (0-63) + * EX2 - portion A length (0-255) - slope only + * EX3 - portion B length (0-255) - slope only + * EX4 - tap A location (0-15) - noise only + * EX5 - tap B location (0-15) - noise only + * EX6 - portion A offset (0-15) - slope only + * EX7 - portion B offset (0-15) - slope only +**/ + +void DivPlatformPowerNoise::tick(bool sysTick) { + for(int i = 0; i < 4; i++) { + chan[i].std.next(); + + if (chan[i].std.ex1.had) { + int val = chan[i].std.ex1.val; + + if (chan[i].slope) { + chan[i].slopeA.clip = ((val & 0x20) != 0); + chan[i].slopeB.clip = ((val & 0x10) != 0); + chan[i].slopeA.reset = ((val & 0x08) != 0); + chan[i].slopeB.reset = ((val & 0x04) != 0); + chan[i].slopeA.dir = ((val & 0x02) != 0); + chan[i].slopeB.dir = ((val & 0x01) != 0); + cWrite(i, 0x00, slopeCtl(chan[i].active, false, chan[i].slopeA, chan[i].slopeB)); + } + else { + chan[i].am = ((val & 0x02) != 0); + chan[i].tapBEnable = ((val & 0x01) != 0); + cWrite(i, 0x00, noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); + } + } + if (chan[i].std.ex2.had && chan[i].slope) { + cWrite(i, 0x03, chan[i].std.ex2.val) + } + if (chan[i].std.ex3.had && chan[i].slope) { + cWrite(i, 0x04, chan[i].std.ex3.val) + } + if ((chan[i].std.ex4.had || chan[i].std.ex5.had) && !chan[i].slope) { + cWrite(i, 0x05, (chan[i].std.ex4.val << 4) | chan[i].std.ex5.val) + } + if ((chan[i].std.ex6.had || chan[i].std.ex7.had) && chan[i].slope) { + cWrite(i, 0x05, (chan[i].std.ex6.val << 4) | chan[i].std.ex7.val) + } + + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); + } + chan[i].handleArp(); + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had || chan[i].std.vol.had) { + cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + if (chan[i].slope && chan[i].active) { + cWrite(i, 0x00, slopeCtl(true, true, chan[i].slopeA, chan[i].slopeB)); + chan[i].keyOn=true; + } + else if (chan[i].active) { + cWrite(i, 0x03, 0x01); + cWrite(i, 0x04, 0x00); + chan[i].keyOn=true; + } + } + + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,chan[i].octave); + + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>4095) chan[i].freq=4095; + + cWrite(i,0x01,(4095-chan[i].freq)&0xff); + cWrite(i,0x02,((4095-chan[i].freq)>>8) | (chan[i].octave<<4)); + + if (chan[i].keyOn) { + if(chan[i].slope) { + cWrite(i, 0x00, slopeCtl(true, false, chan[i].slopeA, chan[i].slopeB)); + } + else { + cWrite(i, 0x00, noiseCtl(true, chan[i].am, chan[i].tapBEnable)); + cWrite(i, 0x03, 0x01); + cWrite(i, 0x04, 0x00); + } + } + if (chan[i].keyOff) { + if(chan[i].slope) { + cWrite(i, 0x00, slopeCtl(false, false, chan[i].slopeA, chan[i].slopeB)); + } + else { + cWrite(i, 0x00, noiseCtl(false, chan[i].am, chan[i].tapBEnable)); + } + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + + if (chan[i].slope) { + uint8_t counter = pn.s.accum; + regPool[0x18] = counter; + } + else { + uint16_t lfsr; + if (i == 0) lfsr = pn.n1.lfsr; + else if (i == 1) lfsr = pn.n2.lfsr; + else lfsr = pn.n3.lfsr; + regPool[(i << 3) + 0x4] = lfsr & 0xff; + regPool[(i << 3) + 0x5] = lfsr >> 8; + } + } +} + +int DivPlatformPowerNoise::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWER_NOISE); + if (ins->type==DIV_INS_POWER_NOISE) { + if (skipRegisterWrites) break; + if (c.value!=DIV_NOTE_NULL) { + int baseFreq, divider; + for (divider = 0; divider < 16; divider++) { + baseFreq = round(parent->calcBaseFreq(chipClock,2<song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].keyOn=true; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq, divider; + for (divider = 0; divider < 16; divider++) { + destFreq = round(parent->calcBaseFreq(chipClock,2<chan[c.chan].baseFreq || divider 4095 && chan[c.chan].octave > 0) { + chan[c.chan].octave--; + chan[c.chan].baseFreq %= 4096; + } + if (chan[c.chan].baseFreq>=destFreq || chan[c.chan].octavedivider) { + chan[c.chan].baseFreq=destFreq; + chan[c.chan].octave=divider; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_PANNING: { + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); + cWrite(c.chan,0x06,isMuted[c.chan]?0:volPan(chan[c.chan].outVol, chan[c.chan].pan)); + break; + } + case DIV_CMD_LEGATO: { + int whatAMess = c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); + + int baseFreq, divider; + for (divider = 0; divider < 16; divider++) { + baseFreq = round(parent->calcBaseFreq(chipClock,2<song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWER_NOISE)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) { + int baseFreq, divider; + for (divider = 0; divider < 16; divider++) { + baseFreq = round(parent->calcBaseFreq(chipClock,2<rate=rate; + } +} + +void DivPlatformPowerNoise::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformPowerNoise::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + + for (int i=0; i<4; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 4; +} + +void DivPlatformPowerNoise::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } +} + +DivPlatformPowerNoise::~DivPlatformPowerNoise() { +} diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h new file mode 100644 index 000000000..cc0e1d741 --- /dev/null +++ b/src/engine/platform/powernoise.h @@ -0,0 +1,101 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 _POWER_NOISE_H +#define _POWER_NOISE_H + +#include "../dispatch.h" +#include "../../fixedQueue.h" +#include "../../../extern/pwrnoise/pwrnoise.h" + +class DivPlatformPowerNoise: public DivDispatch { + struct SlopePortion { + unsigned char len, offset; + bool clip, reset, dir; + + SlopePortion(): + len(0), + offset(0), + clip(false), + reset(false), + dir(false) {} + }; + + struct Channel: public SharedChannel { + unsigned char octave, pan, tapA, tapB; + bool slope, am, tapBEnable, keyOn, keyOff; + SlopePortion slopeA, slopeB; + + Channel(): + SharedChannel(15), + octave(0), + pan(255), + tapA(0), + tapB(0), + slope(false), + am(false), + tapBEnable(false), + keyOn(false), + keyOff(false), + slopeA(), + slopeB() {} + }; + + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + unsigned char regPool[32]; + + FixedQueue queueLeft; + FixedQueue queueRight; + + power_noise_t pn; + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); + DivChannelModeHints getModeHints(int chan); + DivSamplePos getSamplePos(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformPowerNoise(); +}; + +#endif diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index bd8a3660a..301b7ac58 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6902,6 +6902,9 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="OP4 Noise Mode"; dutyMax=3; } + if (ins->type==DIV_INS_POWER_NOISE) { + dutyMax=0; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); From cc70471b9ae45a71fdb1cd3e7602f39100c98083 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 06:58:00 -0500 Subject: [PATCH 011/127] remove debugging printf --- extern/pwrnoise/pwrnoise.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 114848a7f..631e54616 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -177,7 +176,6 @@ void pwrnoise_reset(power_noise_t *pn) { void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val) { reg &= 0x1f; - printf("%02x %02x\n", reg, val); if (reg == 0x00) { pn->flags = val; From d29eb42d077a341ae6b057fbe9053a9ab5630f5d Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 07:19:15 -0500 Subject: [PATCH 012/127] change the config from 8 to 6 bits: the upper two aren't used --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 301b7ac58..99282d974 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7263,7 +7263,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWER_NOISE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,8,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); From 00676e785ad2e8f353d7d202a6b9f8afd77c5c8a Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 11:55:49 -0500 Subject: [PATCH 013/127] Fix some bugs with sound output --- extern/pwrnoise/README.md | 2 +- src/engine/platform/powernoise.cpp | 114 ++++++++++++++++++----------- src/engine/sysDef.cpp | 2 +- src/gui/guiConst.cpp | 2 +- src/gui/presets.cpp | 5 ++ 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/extern/pwrnoise/README.md b/extern/pwrnoise/README.md index 0366f408a..b6d9e86d1 100644 --- a/extern/pwrnoise/README.md +++ b/extern/pwrnoise/README.md @@ -1,4 +1,4 @@ # pwrnoise -An emulator for the Power Noise fantasy sound chip, part of the [Hexheld](https://github.com/Hexheld/) fantasy console. +An emulator for the PowerNoise fantasy sound chip, part of the [Hexheld](https://github.com/Hexheld/) fantasy console. Design by [jvsTSX](https://github.com/jvsTSX/), code by scratchminer. \ No newline at end of file diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index ef14c06d3..f7393e264 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -35,6 +35,7 @@ (b.dir ? 0x01 : 0x00)) #define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) #define mapAmp(a) (((a) * 65535 / 15 - 32768) * (pn.flags & 0x7) / 7) +#define CHIP_DIVIDER 2 const char* regCheatSheetPowerNoise[]={ "ACTL", "00", @@ -174,13 +175,63 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,chan[i].octave); + chan[i].freq = parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; - if (chan[i].freq>4095) chan[i].freq=4095; + if (chan[i].freq>0xfffffff) chan[i].freq=0xfffffff; + if(chan[i].freq >= 0x8000000) { + chan[i].octave = 0; + } + else if(chan[i].freq >= 0x4000000) { + chan[i].octave = 1; + } + else if(chan[i].freq >= 0x2000000) { + chan[i].octave = 2; + } + else if(chan[i].freq >= 0x1000000) { + chan[i].octave = 3; + } + if(chan[i].freq >= 0x800000) { + chan[i].octave = 4; + } + else if(chan[i].freq >= 0x400000) { + chan[i].octave = 5; + } + else if(chan[i].freq >= 0x200000) { + chan[i].octave = 6; + } + else if(chan[i].freq >= 0x100000) { + chan[i].octave = 7; + } + if(chan[i].freq >= 0x800000) { + chan[i].octave = 8; + } + else if(chan[i].freq >= 0x400000) { + chan[i].octave = 9; + } + else if(chan[i].freq >= 0x200000) { + chan[i].octave = 10; + } + else if(chan[i].freq >= 0x100000) { + chan[i].octave = 11; + } + if(chan[i].freq >= 0x8000) { + chan[i].octave = 12; + } + else if(chan[i].freq >= 0x4000) { + chan[i].octave = 13; + } + else if(chan[i].freq >= 0x2000) { + chan[i].octave = 14; + } + else { + chan[i].octave = 15; + } + chan[i].freq = 0xfff-(chan[i].freq>>chan[i].octave); + chan[i].octave = 15 - chan[i].octave; - cWrite(i,0x01,(4095-chan[i].freq)&0xff); - cWrite(i,0x02,((4095-chan[i].freq)>>8) | (chan[i].octave<<4)); + cWrite(i,0x01,chan[i].freq&0xff); + cWrite(i,0x02,(chan[i].freq>>8) | (chan[i].octave<<4)); if (chan[i].keyOn) { if(chan[i].slope) { @@ -227,14 +278,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { if (ins->type==DIV_INS_POWER_NOISE) { if (skipRegisterWrites) break; if (c.value!=DIV_NOTE_NULL) { - int baseFreq, divider; - for (divider = 0; divider < 16; divider++) { - baseFreq = round(parent->calcBaseFreq(chipClock,2<calcBaseFreq(chipClock,2<chan[c.chan].baseFreq || dividerchan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; - if (chan[c.chan].baseFreq > 4095 && chan[c.chan].octave > 0) { - chan[c.chan].octave--; - chan[c.chan].baseFreq %= 4096; + if (chan[c.chan].baseFreq > 0xfffffff) { + chan[c.chan].baseFreq = 0xfffffff; } - if (chan[c.chan].baseFreq>=destFreq || chan[c.chan].octave=destFreq) { chan[c.chan].baseFreq=destFreq; - chan[c.chan].octave=divider; return2=true; } } else { chan[c.chan].baseFreq-=c.value; - if (chan[c.chan].baseFreq < 0 && chan[c.chan].octave < 15) { - chan[c.chan].octave++; - chan[c.chan].baseFreq = (chan[c.chan].baseFreq + 4096) % 4096; + if (chan[c.chan].baseFreq < 0) { + chan[c.chan].baseFreq = 0; } - if (chan[c.chan].baseFreq<=destFreq || chan[c.chan].octave>divider) { + if (chan[c.chan].baseFreq<=destFreq) { chan[c.chan].baseFreq=destFreq; - chan[c.chan].octave=divider; return2=true; } } @@ -327,13 +363,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { case DIV_CMD_LEGATO: { int whatAMess = c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); - int baseFreq, divider; - for (divider = 0; divider < 16; divider++) { - baseFreq = round(parent->calcBaseFreq(chipClock,2<song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWER_NOISE)); } if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) { - int baseFreq, divider; - for (divider = 0; divider < 16; divider++) { - baseFreq = round(parent->calcBaseFreq(chipClock,2< Date: Sun, 21 Jan 2024 12:22:23 -0500 Subject: [PATCH 014/127] Add a 'set LFSR' macro and remove the LFSR reset when a note is turned on --- src/engine/platform/powernoise.cpp | 21 ++++++++++----------- src/gui/insEdit.cpp | 13 +++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index f7393e264..52ee9e99f 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -101,6 +101,7 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { * EX5 - tap B location (0-15) - noise only * EX6 - portion A offset (0-15) - slope only * EX7 - portion B offset (0-15) - slope only + * EX8 - load LFSR (0-65535) - noise only **/ void DivPlatformPowerNoise::tick(bool sysTick) { @@ -126,16 +127,21 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } } if (chan[i].std.ex2.had && chan[i].slope) { - cWrite(i, 0x03, chan[i].std.ex2.val) + cWrite(i, 0x03, chan[i].std.ex2.val); } if (chan[i].std.ex3.had && chan[i].slope) { - cWrite(i, 0x04, chan[i].std.ex3.val) + cWrite(i, 0x04, chan[i].std.ex3.val); } if ((chan[i].std.ex4.had || chan[i].std.ex5.had) && !chan[i].slope) { - cWrite(i, 0x05, (chan[i].std.ex4.val << 4) | chan[i].std.ex5.val) + cWrite(i, 0x05, (chan[i].std.ex4.val << 4) | chan[i].std.ex5.val); } if ((chan[i].std.ex6.had || chan[i].std.ex7.had) && chan[i].slope) { - cWrite(i, 0x05, (chan[i].std.ex6.val << 4) | chan[i].std.ex7.val) + cWrite(i, 0x05, (chan[i].std.ex6.val << 4) | chan[i].std.ex7.val); + } + if (chan[i].std.ex8.had && !chan[i].slope && chan[i].active && chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + cWrite(i, 0x03, chan[i].std.ex8.val & 0xff); + cWrite(i, 0x04, chan[i].std.ex8.val >> 8); + chan[i].keyOn=true; } if (chan[i].std.vol.had) { @@ -167,11 +173,6 @@ void DivPlatformPowerNoise::tick(bool sysTick) { cWrite(i, 0x00, slopeCtl(true, true, chan[i].slopeA, chan[i].slopeB)); chan[i].keyOn=true; } - else if (chan[i].active) { - cWrite(i, 0x03, 0x01); - cWrite(i, 0x04, 0x00); - chan[i].keyOn=true; - } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { @@ -239,8 +240,6 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } else { cWrite(i, 0x00, noiseCtl(true, chan[i].am, chan[i].tapBEnable)); - cWrite(i, 0x03, 0x01); - cWrite(i, 0x04, 0x00); } } if (chan[i].keyOff) { diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 99282d974..839f122b0 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7264,12 +7264,13 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_POWER_NOISE) { macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); - macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Portion B Offset",&ins->std.ex7Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Offset",&ins->std.ex7Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } drawMacros(macroList,macroEditStateMacros); From 38c2121fc04b2ef8621d211086ed8fd34edc859e Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 19:39:52 -0500 Subject: [PATCH 015/127] fix the freq calculator (again) --- src/engine/platform/powernoise.cpp | 67 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 52ee9e99f..f2ec265f3 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -35,7 +35,7 @@ (b.dir ? 0x01 : 0x00)) #define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) #define mapAmp(a) (((a) * 65535 / 15 - 32768) * (pn.flags & 0x7) / 7) -#define CHIP_DIVIDER 2 +#define CHIP_DIVIDER 128 const char* regCheatSheetPowerNoise[]={ "ACTL", "00", @@ -179,57 +179,58 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].freq = parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; - if (chan[i].freq>0xfffffff) chan[i].freq=0xfffffff; - if(chan[i].freq >= 0x8000000) { - chan[i].octave = 0; - } - else if(chan[i].freq >= 0x4000000) { - chan[i].octave = 1; + if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; + + if(chan[i].freq >= 0x4000000) { + chan[i].octave = 15; } else if(chan[i].freq >= 0x2000000) { - chan[i].octave = 2; + chan[i].octave = 14; } else if(chan[i].freq >= 0x1000000) { - chan[i].octave = 3; + chan[i].octave = 13; } - if(chan[i].freq >= 0x800000) { - chan[i].octave = 4; + else if(chan[i].freq >= 0x800000) { + chan[i].octave = 12; } else if(chan[i].freq >= 0x400000) { - chan[i].octave = 5; - } - else if(chan[i].freq >= 0x200000) { - chan[i].octave = 6; - } - else if(chan[i].freq >= 0x100000) { - chan[i].octave = 7; - } - if(chan[i].freq >= 0x800000) { - chan[i].octave = 8; - } - else if(chan[i].freq >= 0x400000) { - chan[i].octave = 9; + chan[i].octave = 11; } else if(chan[i].freq >= 0x200000) { chan[i].octave = 10; } else if(chan[i].freq >= 0x100000) { - chan[i].octave = 11; + chan[i].octave = 9; } - if(chan[i].freq >= 0x8000) { - chan[i].octave = 12; + else if(chan[i].freq >= 0x80000) { + chan[i].octave = 8; + } + else if(chan[i].freq >= 0x40000) { + chan[i].octave = 7; + } + else if(chan[i].freq >= 0x20000) { + chan[i].octave = 6; + } + else if(chan[i].freq >= 0x10000) { + chan[i].octave = 5; + } + else if(chan[i].freq >= 0x8000) { + chan[i].octave = 4; } else if(chan[i].freq >= 0x4000) { - chan[i].octave = 13; + chan[i].octave = 3; } else if(chan[i].freq >= 0x2000) { - chan[i].octave = 14; + chan[i].octave = 2; + } + else if(chan[i].freq >= 0x1000) { + chan[i].octave = 1; } else { - chan[i].octave = 15; + chan[i].octave = 0; } + chan[i].freq = 0xfff-(chan[i].freq>>chan[i].octave); - chan[i].octave = 15 - chan[i].octave; cWrite(i,0x01,chan[i].freq&0xff); cWrite(i,0x02,(chan[i].freq>>8) | (chan[i].octave<<4)); @@ -330,8 +331,8 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; - if (chan[c.chan].baseFreq > 0xfffffff) { - chan[c.chan].baseFreq = 0xfffffff; + if (chan[c.chan].baseFreq > 0x7ffffff) { + chan[c.chan].baseFreq = 0x7ffffff; } if (chan[c.chan].baseFreq>=destFreq) { chan[c.chan].baseFreq=destFreq; From 8f8ccdc272862100386d1265c758d7ecd50a54cf Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 20:37:49 -0500 Subject: [PATCH 016/127] fix a bug with the volume macro --- src/engine/platform/powernoise.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index f2ec265f3..7ab2c48a2 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -312,6 +312,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; + cWrite(c.chan,0x06,isMuted[c.chan]?0:volPan(chan[c.chan].outVol, chan[c.chan].pan)); } } break; From bc3a12ef4ac494749d5688b55f130cdf708cb1af Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 21:01:40 -0500 Subject: [PATCH 017/127] fix volume macro again --- src/engine/platform/powernoise.cpp | 32 ++++++++++++++++++++---------- src/engine/platform/powernoise.h | 1 - 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 7ab2c48a2..ab80dc3b8 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -146,18 +146,29 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); + if (chan[i].outVol<0) chan[i].outVol=0; + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; } - chan[i].handleArp(); if (chan[i].std.panL.had) { chan[i].pan&=0x0f; chan[i].pan|=(chan[i].std.panL.val&15)<<4; + cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } if (chan[i].std.panR.had) { chan[i].pan&=0xf0; chan[i].pan|=chan[i].std.panR.val&15; + cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } - if (chan[i].std.panL.had || chan[i].std.panR.had || chan[i].std.vol.had) { - cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); + + if(chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { + cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -312,7 +323,9 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; - cWrite(c.chan,0x06,isMuted[c.chan]?0:volPan(chan[c.chan].outVol, chan[c.chan].pan)); + if (chan[c.chan].active) { + cWrite(c.chan,0x06,volPan(chan[c.chan].outVol, chan[c.chan].pan)); + } } } break; @@ -358,7 +371,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } case DIV_CMD_PANNING: { chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); - cWrite(c.chan,0x06,isMuted[c.chan]?0:volPan(chan[c.chan].outVol, chan[c.chan].pan)); + cWrite(c.chan,0x06,volPan(chan[c.chan].outVol, chan[c.chan].pan)); break; } case DIV_CMD_LEGATO: { @@ -410,15 +423,15 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } void DivPlatformPowerNoise::muteChannel(int ch, bool mute) { - isMuted[ch]=mute; - cWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol, chan[ch].pan)); + if(mute) chan[ch].keyOff = true; + else chan[ch].keyOn = true; } void DivPlatformPowerNoise::forceIns() { for (int i=0; i<6; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); + cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } } @@ -487,7 +500,7 @@ void DivPlatformPowerNoise::reset() { rWrite(0,0x87); // set per-channel panning for (int i=0; i<4; i++) { - cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); + cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } addWrite(0xffffffff, 0); } @@ -534,7 +547,6 @@ int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const D skipRegisterWrites=false; for (int i=0; i<4; i++) { - isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index cc0e1d741..bf25bb631 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -59,7 +59,6 @@ class DivPlatformPowerNoise: public DivDispatch { Channel chan[4]; DivDispatchOscBuffer* oscBuf[4]; - bool isMuted[4]; unsigned char regPool[32]; FixedQueue queueLeft; From 98bb0ea175cf457bb490b9de2ff73146dac46e93 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Sun, 21 Jan 2024 23:49:02 -0500 Subject: [PATCH 018/127] forgot to change the number of channels --- src/engine/platform/powernoise.cpp | 34 ++++++++++++------------------ src/engine/platform/powernoise.h | 1 + 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index ab80dc3b8..63a473bc0 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -24,8 +24,8 @@ #define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = v; pwrnoise_write(&pn, (uint8_t)a, (uint8_t)v); if (dumpWrites) {addWrite(a,v);} } #define cWrite(c,a,v) rWrite((c << 3) | (a + 1), v) -#define noiseCtl(enable, am, tapB) ((enable ? 0x80 : 0x00) | (am ? 0x02 : 0x00) | (tapB ? 0x01 : 0x00)) -#define slopeCtl(enable, rst, a, b) ((enable ? 0x80 : 0x00) | \ +#define noiseCtl(enable, am, tapB) (((enable) ? 0x80 : 0x00) | ((am) ? 0x02 : 0x00) | ((tapB) ? 0x01 : 0x00)) +#define slopeCtl(enable, rst, a, b) (((enable) ? 0x80 : 0x00) | \ (rst ? 0x40 : 0x00) | \ (a.clip ? 0x20 : 0x00) | \ (b.clip ? 0x10 : 0x00) | \ @@ -34,7 +34,7 @@ (a.dir ? 0x02 : 0x00) | \ (b.dir ? 0x01 : 0x00)) #define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) -#define mapAmp(a) (((a) * 65535 / 15 - 32768) * (pn.flags & 0x7) / 7) +#define mapAmp(a) (((a) * 65535 / 63 - 32768) * (pn.flags & 0x7) / 7) #define CHIP_DIVIDER 128 const char* regCheatSheetPowerNoise[]={ @@ -159,16 +159,14 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].std.panL.had) { chan[i].pan&=0x0f; chan[i].pan|=(chan[i].std.panL.val&15)<<4; - cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } if (chan[i].std.panR.had) { chan[i].pan&=0xf0; chan[i].pan|=chan[i].std.panR.val&15; - cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } if(chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { - cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); + cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -180,8 +178,8 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { - if (chan[i].slope && chan[i].active) { - cWrite(i, 0x00, slopeCtl(true, true, chan[i].slopeA, chan[i].slopeB)); + if (chan[i].slope) { + cWrite(i, 0x00, slopeCtl(chan[i].active, true, chan[i].slopeA, chan[i].slopeB)); chan[i].keyOn=true; } } @@ -262,6 +260,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { cWrite(i, 0x00, noiseCtl(false, chan[i].am, chan[i].tapBEnable)); } } + if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; @@ -323,9 +322,6 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; - if (chan[c.chan].active) { - cWrite(c.chan,0x06,volPan(chan[c.chan].outVol, chan[c.chan].pan)); - } } } break; @@ -369,11 +365,9 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } break; } - case DIV_CMD_PANNING: { + case DIV_CMD_PANNING: chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); - cWrite(c.chan,0x06,volPan(chan[c.chan].outVol, chan[c.chan].pan)); break; - } case DIV_CMD_LEGATO: { int whatAMess = c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); @@ -423,15 +417,14 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } void DivPlatformPowerNoise::muteChannel(int ch, bool mute) { - if(mute) chan[ch].keyOff = true; - else chan[ch].keyOn = true; + isMuted[ch] = mute; + cWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol, chan[ch].pan)); } void DivPlatformPowerNoise::forceIns() { - for (int i=0; i<6; i++) { + for (int i=0; i<4; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); } } @@ -489,9 +482,7 @@ void DivPlatformPowerNoise::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformPowerNoise::Channel(); chan[i].std.setEngine(parent); - if(i == 3) { - chan[i].slope = true; - } + chan[i].slope = i == 3; } pwrnoise_reset(&pn); @@ -548,6 +539,7 @@ int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const D for (int i=0; i<4; i++) { oscBuf[i]=new DivDispatchOscBuffer; + isMuted[i]=false; } setFlags(flags); reset(); diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index bf25bb631..cc0e1d741 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -59,6 +59,7 @@ class DivPlatformPowerNoise: public DivDispatch { Channel chan[4]; DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; unsigned char regPool[32]; FixedQueue queueLeft; From 41076c5ba6e52855c3d74f22f4e442a06c55ac84 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 21 Jan 2024 23:58:12 -0500 Subject: [PATCH 019/127] allocate chip ID for PowerNoise --- papers/format.md | 1 + 1 file changed, 1 insertion(+) diff --git a/papers/format.md b/papers/format.md index 1a5a466a0..b48ade8cd 100644 --- a/papers/format.md +++ b/papers/format.md @@ -235,6 +235,7 @@ size | description | - 0xcf: Namco C219 - 16 channels | - 0xd0: Namco C352 - 32 channels (UNAVAILABLE) | - 0xd1: ESFM - 18 channels (UNAVAILABLE) + | - 0xd4: PowerNoise - 4 channels (UNAVAILABLE) | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel From 606ef1e410f7e1afe305cc61e7b9ec5302448047 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Mon, 22 Jan 2024 00:45:54 -0500 Subject: [PATCH 020/127] make required changes, and fix per-channel osc --- src/engine/platform/powernoise.cpp | 61 +++++++++++++----------------- src/engine/platform/powernoise.h | 4 -- src/engine/sysDef.cpp | 2 +- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 63a473bc0..f23b9015d 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -34,7 +34,7 @@ (a.dir ? 0x02 : 0x00) | \ (b.dir ? 0x01 : 0x00)) #define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) -#define mapAmp(a) (((a) * 65535 / 63 - 32768) * (pn.flags & 0x7) / 7) +#define mapAmp(a) ((((a) * 65535 / 63 - 32768) * (pn.flags & 0x7) / 7) >> 1) #define CHIP_DIVIDER 128 const char* regCheatSheetPowerNoise[]={ @@ -81,7 +81,9 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { int16_t left, right; for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp(pn.n1.out_latch); oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(pn.n2.out_latch); @@ -145,7 +147,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].std.vol.had) { - chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; } if (NEW_ARP_STRAT) { @@ -165,7 +167,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].pan|=chan[i].std.panR.val&15; } - if(chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { + if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); } if (chan[i].std.pitch.had) { @@ -190,49 +192,49 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; - if(chan[i].freq >= 0x4000000) { + if (chan[i].freq >= 0x4000000) { chan[i].octave = 15; } - else if(chan[i].freq >= 0x2000000) { + else if (chan[i].freq >= 0x2000000) { chan[i].octave = 14; } - else if(chan[i].freq >= 0x1000000) { + else if (chan[i].freq >= 0x1000000) { chan[i].octave = 13; } - else if(chan[i].freq >= 0x800000) { + else if (chan[i].freq >= 0x800000) { chan[i].octave = 12; } - else if(chan[i].freq >= 0x400000) { + else if (chan[i].freq >= 0x400000) { chan[i].octave = 11; } - else if(chan[i].freq >= 0x200000) { + else if (chan[i].freq >= 0x200000) { chan[i].octave = 10; } - else if(chan[i].freq >= 0x100000) { + else if (chan[i].freq >= 0x100000) { chan[i].octave = 9; } - else if(chan[i].freq >= 0x80000) { + else if (chan[i].freq >= 0x80000) { chan[i].octave = 8; } - else if(chan[i].freq >= 0x40000) { + else if (chan[i].freq >= 0x40000) { chan[i].octave = 7; } - else if(chan[i].freq >= 0x20000) { + else if (chan[i].freq >= 0x20000) { chan[i].octave = 6; } - else if(chan[i].freq >= 0x10000) { + else if (chan[i].freq >= 0x10000) { chan[i].octave = 5; } - else if(chan[i].freq >= 0x8000) { + else if (chan[i].freq >= 0x8000) { chan[i].octave = 4; } - else if(chan[i].freq >= 0x4000) { + else if (chan[i].freq >= 0x4000) { chan[i].octave = 3; } - else if(chan[i].freq >= 0x2000) { + else if (chan[i].freq >= 0x2000) { chan[i].octave = 2; } - else if(chan[i].freq >= 0x1000) { + else if (chan[i].freq >= 0x1000) { chan[i].octave = 1; } else { @@ -245,7 +247,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { cWrite(i,0x02,(chan[i].freq>>8) | (chan[i].octave<<4)); if (chan[i].keyOn) { - if(chan[i].slope) { + if (chan[i].slope) { cWrite(i, 0x00, slopeCtl(true, false, chan[i].slopeA, chan[i].slopeB)); } else { @@ -253,7 +255,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } } if (chan[i].keyOff) { - if(chan[i].slope) { + if (chan[i].slope) { cWrite(i, 0x00, slopeCtl(false, false, chan[i].slopeA, chan[i].slopeB)); } else { @@ -442,18 +444,7 @@ unsigned short DivPlatformPowerNoise::getPan(int ch) { DivChannelModeHints DivPlatformPowerNoise::getModeHints(int ch) { DivChannelModeHints ret; - - ret.count=1; - - if (ch==3) { - ret.hint[0]=ICON_FUR_SAW; - ret.type[0]=5; - } - else { - ret.hint[0]=ICON_FUR_NOISE; - ret.type[0]=4; - } - + ret.count=0; return ret; } @@ -514,10 +505,10 @@ void DivPlatformPowerNoise::notifyInsDeletion(void* ins) { } void DivPlatformPowerNoise::setFlags(const DivConfig& flags) { - chipClock=16777216; + chipClock=16000000; CHECK_CUSTOM_CLOCK; - rate=chipClock; + rate=chipClock/32; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index cc0e1d741..c458d9fcc 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -21,7 +21,6 @@ #define _POWER_NOISE_H #include "../dispatch.h" -#include "../../fixedQueue.h" #include "../../../extern/pwrnoise/pwrnoise.h" class DivPlatformPowerNoise: public DivDispatch { @@ -62,9 +61,6 @@ class DivPlatformPowerNoise: public DivDispatch { bool isMuted[4]; unsigned char regPool[32]; - FixedQueue queueLeft; - FixedQueue queueRight; - power_noise_t pn; friend void putDispatchChip(void*,int); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 0d82a2210..1e70e9f94 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1986,7 +1986,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_POWER_NOISE]=new DivSysDef( - "PowerNoise", NULL, 0xd2, 0, 4, false, false, 0, false, 0, 0, 0, + "PowerNoise", NULL, 0xd4, 0, 4, false, false, 0, false, 0, 0, 0, "a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.", {"Noise 1", "Noise 2", "Noise 3", "Slope"}, {"N1", "N2", "N3", "SL"}, From b499be656863fd8b263af8f10a114e92ff7b9bd7 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Mon, 22 Jan 2024 12:10:54 -0500 Subject: [PATCH 021/127] fix the bounds on the oscilloscope again --- src/engine/platform/powernoise.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index f23b9015d..79d2ca32f 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -85,10 +85,10 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { pwrnoise_step(&pn, &left, &right); } - oscBuf[0]->data[oscBuf[0]->needle++]=mapAmp(pn.n1.out_latch); - oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(pn.n2.out_latch); - oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(pn.n3.out_latch); - oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(pn.s.out_latch); + oscBuf[0]->data[oscBuf[0]->needle++]=mapAmp(((pn.n1.out_latch>>1)&0x7)+(pn.n1.out_latch>>5)); + oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(((pn.n2.out_latch>>1)&0x7)+(pn.n2.out_latch>>5)); + oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(((pn.n3.out_latch>>1)&0x7)+(pn.n3.out_latch>>5)); + oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(((pn.s.out_latch>>1)&0x7)+(pn.s.out_latch>>5)); buf[0][h] = left; buf[1][h] = right; From 86981a39dbb86bf33cccf61801421c02d6e71426 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 22 Jan 2024 14:43:49 -0500 Subject: [PATCH 022/127] separate old instrument format --- papers/format.md | 643 +--------------------------------------------- papers/oldIns.md | 651 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 652 insertions(+), 642 deletions(-) create mode 100644 papers/oldIns.md diff --git a/papers/format.md b/papers/format.md index b48ade8cd..3e1d847d8 100644 --- a/papers/format.md +++ b/papers/format.md @@ -504,628 +504,7 @@ see [newIns.md](newIns.md) for more information. # old instrument (<127) -notes: - -- the entire instrument is stored, regardless of instrument type. -- the macro range varies depending on the instrument type. -- "macro open" indicates whether the macro is collapsed or not in the instrument editor. - - as of format version 120, bit 1-2 indicates macro mode: - - 0: sequence (normal) - - 1: ADSR - - 2: LFO - - see sub-section for information on how to interpret parameters. -- FM operator order is: - - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op - - 1/2/?/? (? = unused) for OPL 2-op and OPLL -- meaning of extended macros varies depending on instrument type. -- meaning of panning macros varies depending on instrument type: - - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) - - otherwise both left and right panning macros are used - -``` -size | description ------|------------------------------------ - 4 | "INST" block ID - 4 | size of this block - 2 | format version (see header) - 1 | instrument type - | - 0: SN76489/standard - | - 1: FM (OPN) - | - 2: Game Boy - | - 3: C64 - | - 4: Amiga/sample - | - 5: PC Engine - | - 6: AY-3-8910 - | - 7: AY8930 - | - 8: TIA - | - 9: SAA1099 - | - 10: VIC - | - 11: PET - | - 12: VRC6 - | - 13: OPLL - | - 14: OPL - | - 15: FDS - | - 16: Virtual Boy - | - 17: Namco 163 - | - 18: SCC - | - 19: OPZ - | - 20: POKEY - | - 21: PC Speaker - | - 22: WonderSwan - | - 23: Lynx - | - 24: VERA - | - 25: X1-010 - | - 26: VRC6 (saw) - | - 27: ES5506 - | - 28: MultiPCM - | - 29: SNES - | - 30: Sound Unit - | - 31: Namco WSG - | - 32: OPL (drums) - | - 33: FM (OPM) - | - 34: NES - | - 35: MSM6258 - | - 36: MSM6295 - | - 37: ADPCM-A - | - 38: ADPCM-B - | - 39: SegaPCM - | - 40: QSound - | - 41: YMZ280B - | - 42: RF5C68 - | - 43: MSM5232 - | - 44: T6W28 - 1 | reserved - STR | instrument name - --- | **FM instrument data** - 1 | alg (SUS on OPLL) - 1 | feedback - 1 | fms (DC on OPLL) - 1 | ams (DM on OPLL) - 1 | operator count - | - this is either 2 or 4, and is ignored on non-OPL systems. - | - always read 4 ops regardless of this value. - 1 | OPLL preset (>=60) or reserved - | - 0: custom - | - 1-15: pre-defined patches - | - 16: drums (compatibility only!) - 2 | reserved - --- | **FM operator data** × 4 - 1 | am - 1 | ar - 1 | dr - 1 | mult - 1 | rr - 1 | sl - 1 | tl - 1 | dt2 - 1 | rs - 1 | dt - 1 | d2r - 1 | ssgEnv - | - bit 4: on (EG-S on OPLL) - | - bit 0-3: envelope type - 1 | dam (for YMU759 compat; REV on OPZ) - 1 | dvb (for YMU759 compat; FINE on OPZ) - 1 | egt (for YMU759 compat; FixedFreq on OPZ) - 1 | ksl (EGShift on OPZ) - 1 | sus - 1 | vib - 1 | ws - 1 | ksr - 1 | operator enabled (>=114) or reserved - 1 | KVS mode (>=115) or reserved - | - 0: off - | - 1: on - | - 2: auto (depending on alg) - 10 | reserved - --- | **Game Boy instrument data** - 1 | volume - 1 | direction - 1 | length - 1 | sound length - --- | **C64 instrument data** - 1 | triangle - 1 | saw - 1 | pulse - 1 | noise - 1 | attack - 1 | decay - 1 | sustain - 1 | release - 2 | duty - 1 | ring mod - 1 | osc sync - 1 | to filter - 1 | init filter - 1 | vol macro is cutoff (<187) or reserved - | - from version 187 onwards, volume and cutoff macros are separate. - | - if this is on and the version is less than 187, move the volume macro into the ALG one. - 1 | resonance - 1 | low pass - 1 | band pass - 1 | high pass - 1 | channel 3 off - 2 | cutoff - 1 | duty macro is absolute - 1 | filter macro is absolute - --- | **Amiga instrument data** - 2 | initial sample - 1 | mode (>=82) or reserved - | - 0: sample - | - 1: wavetable - 1 | wavetable length (-1) (>=82) or reserved - 12 | reserved - --- | **standard instrument data** - 4 | volume macro length - 4 | arp macro length - 4 | duty macro length - 4 | wave macro length - 4 | pitch macro length (>=17) - 4 | extra 1 macro length (>=17) - 4 | extra 2 macro length (>=17) - 4 | extra 3 macro length (>=17) - 4 | volume macro loop - 4 | arp macro loop - 4 | duty macro loop - 4 | wave macro loop - 4 | pitch macro loop (>=17) - 4 | extra 1 macro loop (>=17) - 4 | extra 2 macro loop (>=17) - 4 | extra 3 macro loop (>=17) - 1 | arp macro mode (<112) or reserved - | - treat this value in a special way. - | - before version 112, this byte indicates whether the arp macro mode is fixed or not. - | - from that version onwards, the fixed mode is part of the macro values. - | - to convert a <112 macro mode to a modern one, do the following: - | - is the macro mode set to fixed? - | - if yes, then: - | - set bit 30 of all arp macro values (this is the fixed mode bit) - | - does the macro loop? - | - if yes, then do nothing else - | - if no, then add one to the macro length, and set the last macro value to 0 - | - if no, then do nothing - 1 | reserved (>=17) or volume macro height (>=15) or reserved - 1 | reserved (>=17) or duty macro height (>=15) or reserved - 1 | reserved (>=17) or wave macro height (>=15) or reserved - 4?? | volume macro - | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. - 4?? | arp macro - | - before version 31, this macro's values were stored offset by 12. - | - from version 112 onward, bit 30 of a value indicates fixed mode. - 4?? | duty macro - | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. - 4?? | wave macro - 4?? | pitch macro (>=17) - 4?? | extra 1 macro (>=17) - 4?? | extra 2 macro (>=17) - 4?? | extra 3 macro (>=17) - 4 | alg macro length (>=29) - 4 | fb macro length (>=29) - 4 | fms macro length (>=29) - 4 | ams macro length (>=29) - 4 | alg macro loop (>=29) - 4 | fb macro loop (>=29) - 4 | fms macro loop (>=29) - 4 | ams macro loop (>=29) - 1 | volume macro open (>=29) - 1 | arp macro open (>=29) - 1 | duty macro open (>=29) - 1 | wave macro open (>=29) - 1 | pitch macro open (>=29) - 1 | extra 1 macro open (>=29) - 1 | extra 2 macro open (>=29) - 1 | extra 3 macro open (>=29) - 1 | alg macro open (>=29) - 1 | fb macro open (>=29) - 1 | fms macro open (>=29) - 1 | ams macro open (>=29) - 4?? | alg macro (>=29) - 4?? | fb macro (>=29) - 4?? | fms macro (>=29) - 4?? | ams macro (>=29) - --- | **operator macro headers** × 4 (>=29) - 4 | AM macro length - 4 | AR macro length - 4 | DR macro length - 4 | MULT macro length - 4 | RR macro length - 4 | SL macro length - 4 | TL macro length - 4 | DT2 macro length - 4 | RS macro length - 4 | DT macro length - 4 | D2R macro length - 4 | SSG-EG macro length - 4 | AM macro loop - 4 | AR macro loop - 4 | DR macro loop - 4 | MULT macro loop - 4 | RR macro loop - 4 | SL macro loop - 4 | TL macro loop - 4 | DT2 macro loop - 4 | RS macro loop - 4 | DT macro loop - 4 | D2R macro loop - 4 | SSG-EG macro loop - 1 | AM macro open - 1 | AR macro open - 1 | DR macro open - 1 | MULT macro open - 1 | RR macro open - 1 | SL macro open - 1 | TL macro open - 1 | DT2 macro open - 1 | RS macro open - 1 | DT macro open - 1 | D2R macro open - 1 | SSG-EG macro open - --- | **operator macros** × 4 (>=29) - 1?? | AM macro - 1?? | AR macro - 1?? | DR macro - 1?? | MULT macro - 1?? | RR macro - 1?? | SL macro - 1?? | TL macro - 1?? | DT2 macro - 1?? | RS macro - 1?? | DT macro - 1?? | D2R macro - 1?? | SSG-EG macro - --- | **release points** (>=44) - 4 | volume macro release - 4 | arp macro release - 4 | duty macro release - 4 | wave macro release - 4 | pitch macro release - 4 | extra 1 macro release - 4 | extra 2 macro release - 4 | extra 3 macro release - 4 | alg macro release - 4 | fb macro release - 4 | fms macro release - 4 | ams macro release - --- | **operator release points** × 4 (>=44) - 4 | AM macro release - 4 | AR macro release - 4 | DR macro release - 4 | MULT macro release - 4 | RR macro release - 4 | SL macro release - 4 | TL macro release - 4 | DT2 macro release - 4 | RS macro release - 4 | DT macro release - 4 | D2R macro release - 4 | SSG-EG macro release - --- | **extended op macro headers** × 4 (>=61) - 4 | DAM macro length - 4 | DVB macro length - 4 | EGT macro length - 4 | KSL macro length - 4 | SUS macro length - 4 | VIB macro length - 4 | WS macro length - 4 | KSR macro length - 4 | DAM macro loop - 4 | DVB macro loop - 4 | EGT macro loop - 4 | KSL macro loop - 4 | SUS macro loop - 4 | VIB macro loop - 4 | WS macro loop - 4 | KSR macro loop - 4 | DAM macro release - 4 | DVB macro release - 4 | EGT macro release - 4 | KSL macro release - 4 | SUS macro release - 4 | VIB macro release - 4 | WS macro release - 4 | KSR macro release - 1 | DAM macro open - 1 | DVB macro open - 1 | EGT macro open - 1 | KSL macro open - 1 | SUS macro open - 1 | VIB macro open - 1 | WS macro open - 1 | KSR macro open - --- | **extended op macros** × 4 (>=61) - 1?? | DAM macro - 1?? | DVB macro - 1?? | EGT macro - 1?? | KSL macro - 1?? | SUS macro - 1?? | VIB macro - 1?? | WS macro - 1?? | KSR macro - --- | **OPL drums mode data** (>=63) - 1 | fixed frequency mode - 1 | reserved - 2 | kick frequency - 2 | snare/hi-hat frequency - 2 | tom/top frequency - --- | **Sample instrument extra data** (>=67) - 1 | use note map - | - only read the following two data structures if this is true! - 4?? | note frequency × 120 - | - 480 bytes - 2?? | note sample × 120 - | - 240 bytes - --- | **Namco 163 data** (>=73) - 4 | initial waveform - 1 | wave position - 1 | wave length - 1 | wave mode: - | - bit 1: update on change - | - bit 0: load on playback - 1 | reserved - --- | **even more macros** (>=76) - 4 | left panning macro length - 4 | right panning macro length - 4 | phase reset macro length - 4 | extra 4 macro length - 4 | extra 5 macro length - 4 | extra 6 macro length - 4 | extra 7 macro length - 4 | extra 8 macro length - 4 | left panning macro loop - 4 | right panning macro loop - 4 | phase reset macro loop - 4 | extra 4 macro loop - 4 | extra 5 macro loop - 4 | extra 6 macro loop - 4 | extra 7 macro loop - 4 | extra 8 macro loop - 4 | left panning macro release - 4 | right panning macro release - 4 | phase reset macro release - 4 | extra 4 macro release - 4 | extra 5 macro release - 4 | extra 6 macro release - 4 | extra 7 macro release - 4 | extra 8 macro release - 1 | left panning macro open - 1 | right panning macro open - 1 | phase reset macro open - 1 | extra 4 macro open - 1 | extra 5 macro open - 1 | extra 6 macro open - 1 | extra 7 macro open - 1 | extra 8 macro open - --- | **even more macro data** (>=76) - 4?? | left panning macro - 4?? | right panning macro - 4?? | phase reset macro - 4?? | extra 4 macro - 4?? | extra 5 macro - 4?? | extra 6 macro - 4?? | extra 7 macro - 4?? | extra 8 macro - --- | **FDS instrument data** (>=76) - 4 | modulation speed - 4 | modulation depth - 1 | init modulation table with first wave - 3 | reserved - 32 | modulation table - --- | **OPZ instrument extra data** (>=77) - 1 | fms2 - 1 | ams2 - --- | **wavetable synth data** (>=79) - 4 | first wave - 4 | second wave - 1 | rate divider - 1 | effect - | - bit 7: single or dual effect - 1 | enabled - 1 | global - 1 | speed (+1) - 1 | parameter 1 - 1 | parameter 2 - 1 | parameter 3 - 1 | parameter 4 - --- | **additional macro mode flags** (>=84) - 1 | volume macro mode - 1 | duty macro mode - 1 | wave macro mode - 1 | pitch macro mode - 1 | extra 1 macro mode - 1 | extra 2 macro mode - 1 | extra 3 macro mode - 1 | alg macro mode - 1 | fb macro mode - 1 | fms macro mode - 1 | ams macro mode - 1 | left panning macro mode - 1 | right panning macro mode - 1 | phase reset macro mode - 1 | extra 4 macro mode - 1 | extra 5 macro mode - 1 | extra 6 macro mode - 1 | extra 7 macro mode - 1 | extra 8 macro mode - --- | **extra C64 data** (>=89) - 1 | don't test/gate before new note - --- | **MultiPCM data** (>=93) - 1 | attack rate - 1 | decay 1 rate - 1 | decay level - 1 | decay 2 rate - 1 | release rate - 1 | rate correction - 1 | lfo rate - 1 | vib depth - 1 | am depth - 23 | reserved - --- | **Sound Unit data** (>=104) - 1 | use sample - 1 | switch roles of phase reset timer and frequency - --- | **Game Boy envelope sequence** (>=105) - 1 | length - ??? | hardware sequence data - | size is length*3: - | 1 byte: command - | - 0: set envelope - | - 1: set sweep - | - 2: wait - | - 3: wait for release - | - 4: loop - | - 5: loop until release - | 2 bytes: data - | - for set envelope: - | - 1 byte: parameter - | - bit 4-7: volume - | - bit 3: direction - | - bit 0-2: length - | - 1 byte: sound length - | - for set sweep: - | - 1 byte: parameter - | - bit 4-6: length - | - bit 3: direction - | - bit 0-2: shift - | - 1 byte: nothing - | - for wait: - | - 1 byte: length (in ticks) - | - 1 byte: nothing - | - for wait for release: - | - 2 bytes: nothing - | - for loop/loop until release: - | - 2 bytes: position - --- | **Game Boy extra flags** (>=106) - 1 | use software envelope - 1 | always init hard env on new note - --- | **ES5506 data** (>=107) - 1 | filter mode - | - 0: HPK2_HPK2 - | - 1: HPK2_LPK1 - | - 2: LPK2_LPK2 - | - 3: LPK2_LPK1 - 2 | K1 - 2 | K2 - 2 | envelope count - 1 | left volume ramp - 1 | right volume ramp - 1 | K1 ramp - 1 | K2 ramp - 1 | K1 slow - 1 | K2 slow - --- | **SNES data** (>=109) - 1 | use envelope - 1 | gain mode - 1 | gain - 1 | attack - 1 | decay - 1 | sustain - | - bit 3: sustain mode (>=118) - 1 | release - --- | **macro speeds/delays** (>=111) - 1 | volume macro speed - 1 | arp macro speed - 1 | duty macro speed - 1 | wave macro speed - 1 | pitch macro speed - 1 | extra 1 macro speed - 1 | extra 2 macro speed - 1 | extra 3 macro speed - 1 | alg macro speed - 1 | fb macro speed - 1 | fms macro speed - 1 | ams macro speed - 1 | left panning macro speed - 1 | right panning macro speed - 1 | phase reset macro speed - 1 | extra 4 macro speed - 1 | extra 5 macro speed - 1 | extra 6 macro speed - 1 | extra 7 macro speed - 1 | extra 8 macro speed - 1 | volume macro delay - 1 | arp macro delay - 1 | duty macro delay - 1 | wave macro delay - 1 | pitch macro delay - 1 | extra 1 macro delay - 1 | extra 2 macro delay - 1 | extra 3 macro delay - 1 | alg macro delay - 1 | fb macro delay - 1 | fms macro delay - 1 | ams macro delay - 1 | left panning macro delay - 1 | right panning macro delay - 1 | phase reset macro delay - 1 | extra 4 macro delay - 1 | extra 5 macro delay - 1 | extra 6 macro delay - 1 | extra 7 macro delay - 1 | extra 8 macro delay - --- | **operator macro speeds/delay** × 4 (>=111) - 1 | AM macro speed - 1 | AR macro speed - 1 | DR macro speed - 1 | MULT macro speed - 1 | RR macro speed - 1 | SL macro speed - 1 | TL macro speed - 1 | DT2 macro speed - 1 | RS macro speed - 1 | DT macro speed - 1 | D2R macro speed - 1 | SSG-EG macro speed - 1 | DAM macro speed - 1 | DVB macro speed - 1 | EGT macro speed - 1 | KSL macro speed - 1 | SUS macro speed - 1 | VIB macro speed - 1 | WS macro speed - 1 | KSR macro speed - 1 | AM macro delay - 1 | AR macro delay - 1 | DR macro delay - 1 | MULT macro delay - 1 | RR macro delay - 1 | SL macro delay - 1 | TL macro delay - 1 | DT2 macro delay - 1 | RS macro delay - 1 | DT macro delay - 1 | D2R macro delay - 1 | SSG-EG macro delay - 1 | DAM macro delay - 1 | DVB macro delay - 1 | EGT macro delay - 1 | KSL macro delay - 1 | SUS macro delay - 1 | VIB macro delay - 1 | WS macro delay - 1 | KSR macro delay -``` - -## interpreting macro mode values - -- sequence (normal): I think this is obvious... -- ADSR: - - `val[0]`: bottom - - `val[1]`: top - - `val[2]`: attack - - `val[3]`: hold time - - `val[4]`: decay - - `val[5]`: sustain level - - `val[6]`: sustain hold time - - `val[7]`: decay 2 - - `val[8]`: release -- LFO: - - `val[11]`: speed - - `val[12]`: waveform - - 0: triangle - - 1: saw - - 2: pulse - - `val[13]`: phase - - `val[14]`: loop - - `val[15]`: global (not sure how will I implement this) +instruments in older versions of Furnace used a different format. see [oldIns.md](oldIns.md) for more information. ## C64 compatibility note (>=187) @@ -1333,26 +712,6 @@ size | description STR | pattern name (>=51) ``` -# the Furnace instrument format (.fui) - -the instrument format is pretty similar to the file format, but it also stores wavetables and samples used by the instrument. - -``` -size | description ------|------------------------------------ - 16 | "-Furnace instr.-" format magic - 2 | format version - 2 | reserved - 4 | pointer to instrument data - 2 | wavetable count - 2 | sample count - 4 | reserved - 4?? | pointers to wavetables - 4?? | pointers to samples -``` - -instrument data follows. - # the Furnace wavetable format (.fuw) similar to the instrument format... diff --git a/papers/oldIns.md b/papers/oldIns.md new file mode 100644 index 000000000..9d9ef5e58 --- /dev/null +++ b/papers/oldIns.md @@ -0,0 +1,651 @@ +# old instrument format (<127) + +this format is used in older versions of Furnace. + +# header + +.fui files use the following header: + +``` +size | description +-----|------------------------------------ + 16 | "-Furnace instr.-" format magic + 2 | format version + 2 | reserved + 4 | pointer to instrument data + 2 | wavetable count + 2 | sample count + 4 | reserved + 4?? | pointers to wavetables + 4?? | pointers to samples +``` + +instrument data follows. + +this header is not present on instruments inside a .fur file. + +# data + +notes: + +- the entire instrument is stored, regardless of instrument type. +- the macro range varies depending on the instrument type. +- "macro open" indicates whether the macro is collapsed or not in the instrument editor. + - as of format version 120, bit 1-2 indicates macro mode: + - 0: sequence (normal) + - 1: ADSR + - 2: LFO + - see sub-section for information on how to interpret parameters. +- FM operator order is: + - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op + - 1/2/?/? (? = unused) for OPL 2-op and OPLL +- meaning of extended macros varies depending on instrument type. +- meaning of panning macros varies depending on instrument type: + - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) + - otherwise both left and right panning macros are used + +``` +size | description +-----|------------------------------------ + 4 | "INST" block ID + 4 | size of this block + 2 | format version (see header) + 1 | instrument type + | - 0: SN76489/standard + | - 1: FM (OPN) + | - 2: Game Boy + | - 3: C64 + | - 4: Amiga/sample + | - 5: PC Engine + | - 6: AY-3-8910 + | - 7: AY8930 + | - 8: TIA + | - 9: SAA1099 + | - 10: VIC + | - 11: PET + | - 12: VRC6 + | - 13: OPLL + | - 14: OPL + | - 15: FDS + | - 16: Virtual Boy + | - 17: Namco 163 + | - 18: SCC + | - 19: OPZ + | - 20: POKEY + | - 21: PC Speaker + | - 22: WonderSwan + | - 23: Lynx + | - 24: VERA + | - 25: X1-010 + | - 26: VRC6 (saw) + | - 27: ES5506 + | - 28: MultiPCM + | - 29: SNES + | - 30: Sound Unit + | - 31: Namco WSG + | - 32: OPL (drums) + | - 33: FM (OPM) + | - 34: NES + | - 35: MSM6258 + | - 36: MSM6295 + | - 37: ADPCM-A + | - 38: ADPCM-B + | - 39: SegaPCM + | - 40: QSound + | - 41: YMZ280B + | - 42: RF5C68 + | - 43: MSM5232 + | - 44: T6W28 + 1 | reserved + STR | instrument name + --- | **FM instrument data** + 1 | alg (SUS on OPLL) + 1 | feedback + 1 | fms (DC on OPLL) + 1 | ams (DM on OPLL) + 1 | operator count + | - this is either 2 or 4, and is ignored on non-OPL systems. + | - always read 4 ops regardless of this value. + 1 | OPLL preset (>=60) or reserved + | - 0: custom + | - 1-15: pre-defined patches + | - 16: drums (compatibility only!) + 2 | reserved + --- | **FM operator data** × 4 + 1 | am + 1 | ar + 1 | dr + 1 | mult + 1 | rr + 1 | sl + 1 | tl + 1 | dt2 + 1 | rs + 1 | dt + 1 | d2r + 1 | ssgEnv + | - bit 4: on (EG-S on OPLL) + | - bit 0-3: envelope type + 1 | dam (for YMU759 compat; REV on OPZ) + 1 | dvb (for YMU759 compat; FINE on OPZ) + 1 | egt (for YMU759 compat; FixedFreq on OPZ) + 1 | ksl (EGShift on OPZ) + 1 | sus + 1 | vib + 1 | ws + 1 | ksr + 1 | operator enabled (>=114) or reserved + 1 | KVS mode (>=115) or reserved + | - 0: off + | - 1: on + | - 2: auto (depending on alg) + 10 | reserved + --- | **Game Boy instrument data** + 1 | volume + 1 | direction + 1 | length + 1 | sound length + --- | **C64 instrument data** + 1 | triangle + 1 | saw + 1 | pulse + 1 | noise + 1 | attack + 1 | decay + 1 | sustain + 1 | release + 2 | duty + 1 | ring mod + 1 | osc sync + 1 | to filter + 1 | init filter + 1 | vol macro is cutoff (<187) or reserved + | - from version 187 onwards, volume and cutoff macros are separate. + | - if this is on and the version is less than 187, move the volume macro into the ALG one. + 1 | resonance + 1 | low pass + 1 | band pass + 1 | high pass + 1 | channel 3 off + 2 | cutoff + 1 | duty macro is absolute + 1 | filter macro is absolute + --- | **Amiga instrument data** + 2 | initial sample + 1 | mode (>=82) or reserved + | - 0: sample + | - 1: wavetable + 1 | wavetable length (-1) (>=82) or reserved + 12 | reserved + --- | **standard instrument data** + 4 | volume macro length + 4 | arp macro length + 4 | duty macro length + 4 | wave macro length + 4 | pitch macro length (>=17) + 4 | extra 1 macro length (>=17) + 4 | extra 2 macro length (>=17) + 4 | extra 3 macro length (>=17) + 4 | volume macro loop + 4 | arp macro loop + 4 | duty macro loop + 4 | wave macro loop + 4 | pitch macro loop (>=17) + 4 | extra 1 macro loop (>=17) + 4 | extra 2 macro loop (>=17) + 4 | extra 3 macro loop (>=17) + 1 | arp macro mode (<112) or reserved + | - treat this value in a special way. + | - before version 112, this byte indicates whether the arp macro mode is fixed or not. + | - from that version onwards, the fixed mode is part of the macro values. + | - to convert a <112 macro mode to a modern one, do the following: + | - is the macro mode set to fixed? + | - if yes, then: + | - set bit 30 of all arp macro values (this is the fixed mode bit) + | - does the macro loop? + | - if yes, then do nothing else + | - if no, then add one to the macro length, and set the last macro value to 0 + | - if no, then do nothing + 1 | reserved (>=17) or volume macro height (>=15) or reserved + 1 | reserved (>=17) or duty macro height (>=15) or reserved + 1 | reserved (>=17) or wave macro height (>=15) or reserved + 4?? | volume macro + | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. + 4?? | arp macro + | - before version 31, this macro's values were stored offset by 12. + | - from version 112 onward, bit 30 of a value indicates fixed mode. + 4?? | duty macro + | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. + 4?? | wave macro + 4?? | pitch macro (>=17) + 4?? | extra 1 macro (>=17) + 4?? | extra 2 macro (>=17) + 4?? | extra 3 macro (>=17) + 4 | alg macro length (>=29) + 4 | fb macro length (>=29) + 4 | fms macro length (>=29) + 4 | ams macro length (>=29) + 4 | alg macro loop (>=29) + 4 | fb macro loop (>=29) + 4 | fms macro loop (>=29) + 4 | ams macro loop (>=29) + 1 | volume macro open (>=29) + 1 | arp macro open (>=29) + 1 | duty macro open (>=29) + 1 | wave macro open (>=29) + 1 | pitch macro open (>=29) + 1 | extra 1 macro open (>=29) + 1 | extra 2 macro open (>=29) + 1 | extra 3 macro open (>=29) + 1 | alg macro open (>=29) + 1 | fb macro open (>=29) + 1 | fms macro open (>=29) + 1 | ams macro open (>=29) + 4?? | alg macro (>=29) + 4?? | fb macro (>=29) + 4?? | fms macro (>=29) + 4?? | ams macro (>=29) + --- | **operator macro headers** × 4 (>=29) + 4 | AM macro length + 4 | AR macro length + 4 | DR macro length + 4 | MULT macro length + 4 | RR macro length + 4 | SL macro length + 4 | TL macro length + 4 | DT2 macro length + 4 | RS macro length + 4 | DT macro length + 4 | D2R macro length + 4 | SSG-EG macro length + 4 | AM macro loop + 4 | AR macro loop + 4 | DR macro loop + 4 | MULT macro loop + 4 | RR macro loop + 4 | SL macro loop + 4 | TL macro loop + 4 | DT2 macro loop + 4 | RS macro loop + 4 | DT macro loop + 4 | D2R macro loop + 4 | SSG-EG macro loop + 1 | AM macro open + 1 | AR macro open + 1 | DR macro open + 1 | MULT macro open + 1 | RR macro open + 1 | SL macro open + 1 | TL macro open + 1 | DT2 macro open + 1 | RS macro open + 1 | DT macro open + 1 | D2R macro open + 1 | SSG-EG macro open + --- | **operator macros** × 4 (>=29) + 1?? | AM macro + 1?? | AR macro + 1?? | DR macro + 1?? | MULT macro + 1?? | RR macro + 1?? | SL macro + 1?? | TL macro + 1?? | DT2 macro + 1?? | RS macro + 1?? | DT macro + 1?? | D2R macro + 1?? | SSG-EG macro + --- | **release points** (>=44) + 4 | volume macro release + 4 | arp macro release + 4 | duty macro release + 4 | wave macro release + 4 | pitch macro release + 4 | extra 1 macro release + 4 | extra 2 macro release + 4 | extra 3 macro release + 4 | alg macro release + 4 | fb macro release + 4 | fms macro release + 4 | ams macro release + --- | **operator release points** × 4 (>=44) + 4 | AM macro release + 4 | AR macro release + 4 | DR macro release + 4 | MULT macro release + 4 | RR macro release + 4 | SL macro release + 4 | TL macro release + 4 | DT2 macro release + 4 | RS macro release + 4 | DT macro release + 4 | D2R macro release + 4 | SSG-EG macro release + --- | **extended op macro headers** × 4 (>=61) + 4 | DAM macro length + 4 | DVB macro length + 4 | EGT macro length + 4 | KSL macro length + 4 | SUS macro length + 4 | VIB macro length + 4 | WS macro length + 4 | KSR macro length + 4 | DAM macro loop + 4 | DVB macro loop + 4 | EGT macro loop + 4 | KSL macro loop + 4 | SUS macro loop + 4 | VIB macro loop + 4 | WS macro loop + 4 | KSR macro loop + 4 | DAM macro release + 4 | DVB macro release + 4 | EGT macro release + 4 | KSL macro release + 4 | SUS macro release + 4 | VIB macro release + 4 | WS macro release + 4 | KSR macro release + 1 | DAM macro open + 1 | DVB macro open + 1 | EGT macro open + 1 | KSL macro open + 1 | SUS macro open + 1 | VIB macro open + 1 | WS macro open + 1 | KSR macro open + --- | **extended op macros** × 4 (>=61) + 1?? | DAM macro + 1?? | DVB macro + 1?? | EGT macro + 1?? | KSL macro + 1?? | SUS macro + 1?? | VIB macro + 1?? | WS macro + 1?? | KSR macro + --- | **OPL drums mode data** (>=63) + 1 | fixed frequency mode + 1 | reserved + 2 | kick frequency + 2 | snare/hi-hat frequency + 2 | tom/top frequency + --- | **Sample instrument extra data** (>=67) + 1 | use note map + | - only read the following two data structures if this is true! + 4?? | note frequency × 120 + | - 480 bytes + 2?? | note sample × 120 + | - 240 bytes + --- | **Namco 163 data** (>=73) + 4 | initial waveform + 1 | wave position + 1 | wave length + 1 | wave mode: + | - bit 1: update on change + | - bit 0: load on playback + 1 | reserved + --- | **even more macros** (>=76) + 4 | left panning macro length + 4 | right panning macro length + 4 | phase reset macro length + 4 | extra 4 macro length + 4 | extra 5 macro length + 4 | extra 6 macro length + 4 | extra 7 macro length + 4 | extra 8 macro length + 4 | left panning macro loop + 4 | right panning macro loop + 4 | phase reset macro loop + 4 | extra 4 macro loop + 4 | extra 5 macro loop + 4 | extra 6 macro loop + 4 | extra 7 macro loop + 4 | extra 8 macro loop + 4 | left panning macro release + 4 | right panning macro release + 4 | phase reset macro release + 4 | extra 4 macro release + 4 | extra 5 macro release + 4 | extra 6 macro release + 4 | extra 7 macro release + 4 | extra 8 macro release + 1 | left panning macro open + 1 | right panning macro open + 1 | phase reset macro open + 1 | extra 4 macro open + 1 | extra 5 macro open + 1 | extra 6 macro open + 1 | extra 7 macro open + 1 | extra 8 macro open + --- | **even more macro data** (>=76) + 4?? | left panning macro + 4?? | right panning macro + 4?? | phase reset macro + 4?? | extra 4 macro + 4?? | extra 5 macro + 4?? | extra 6 macro + 4?? | extra 7 macro + 4?? | extra 8 macro + --- | **FDS instrument data** (>=76) + 4 | modulation speed + 4 | modulation depth + 1 | init modulation table with first wave + 3 | reserved + 32 | modulation table + --- | **OPZ instrument extra data** (>=77) + 1 | fms2 + 1 | ams2 + --- | **wavetable synth data** (>=79) + 4 | first wave + 4 | second wave + 1 | rate divider + 1 | effect + | - bit 7: single or dual effect + 1 | enabled + 1 | global + 1 | speed (+1) + 1 | parameter 1 + 1 | parameter 2 + 1 | parameter 3 + 1 | parameter 4 + --- | **additional macro mode flags** (>=84) + 1 | volume macro mode + 1 | duty macro mode + 1 | wave macro mode + 1 | pitch macro mode + 1 | extra 1 macro mode + 1 | extra 2 macro mode + 1 | extra 3 macro mode + 1 | alg macro mode + 1 | fb macro mode + 1 | fms macro mode + 1 | ams macro mode + 1 | left panning macro mode + 1 | right panning macro mode + 1 | phase reset macro mode + 1 | extra 4 macro mode + 1 | extra 5 macro mode + 1 | extra 6 macro mode + 1 | extra 7 macro mode + 1 | extra 8 macro mode + --- | **extra C64 data** (>=89) + 1 | don't test/gate before new note + --- | **MultiPCM data** (>=93) + 1 | attack rate + 1 | decay 1 rate + 1 | decay level + 1 | decay 2 rate + 1 | release rate + 1 | rate correction + 1 | lfo rate + 1 | vib depth + 1 | am depth + 23 | reserved + --- | **Sound Unit data** (>=104) + 1 | use sample + 1 | switch roles of phase reset timer and frequency + --- | **Game Boy envelope sequence** (>=105) + 1 | length + ??? | hardware sequence data + | size is length*3: + | 1 byte: command + | - 0: set envelope + | - 1: set sweep + | - 2: wait + | - 3: wait for release + | - 4: loop + | - 5: loop until release + | 2 bytes: data + | - for set envelope: + | - 1 byte: parameter + | - bit 4-7: volume + | - bit 3: direction + | - bit 0-2: length + | - 1 byte: sound length + | - for set sweep: + | - 1 byte: parameter + | - bit 4-6: length + | - bit 3: direction + | - bit 0-2: shift + | - 1 byte: nothing + | - for wait: + | - 1 byte: length (in ticks) + | - 1 byte: nothing + | - for wait for release: + | - 2 bytes: nothing + | - for loop/loop until release: + | - 2 bytes: position + --- | **Game Boy extra flags** (>=106) + 1 | use software envelope + 1 | always init hard env on new note + --- | **ES5506 data** (>=107) + 1 | filter mode + | - 0: HPK2_HPK2 + | - 1: HPK2_LPK1 + | - 2: LPK2_LPK2 + | - 3: LPK2_LPK1 + 2 | K1 + 2 | K2 + 2 | envelope count + 1 | left volume ramp + 1 | right volume ramp + 1 | K1 ramp + 1 | K2 ramp + 1 | K1 slow + 1 | K2 slow + --- | **SNES data** (>=109) + 1 | use envelope + 1 | gain mode + 1 | gain + 1 | attack + 1 | decay + 1 | sustain + | - bit 3: sustain mode (>=118) + 1 | release + --- | **macro speeds/delays** (>=111) + 1 | volume macro speed + 1 | arp macro speed + 1 | duty macro speed + 1 | wave macro speed + 1 | pitch macro speed + 1 | extra 1 macro speed + 1 | extra 2 macro speed + 1 | extra 3 macro speed + 1 | alg macro speed + 1 | fb macro speed + 1 | fms macro speed + 1 | ams macro speed + 1 | left panning macro speed + 1 | right panning macro speed + 1 | phase reset macro speed + 1 | extra 4 macro speed + 1 | extra 5 macro speed + 1 | extra 6 macro speed + 1 | extra 7 macro speed + 1 | extra 8 macro speed + 1 | volume macro delay + 1 | arp macro delay + 1 | duty macro delay + 1 | wave macro delay + 1 | pitch macro delay + 1 | extra 1 macro delay + 1 | extra 2 macro delay + 1 | extra 3 macro delay + 1 | alg macro delay + 1 | fb macro delay + 1 | fms macro delay + 1 | ams macro delay + 1 | left panning macro delay + 1 | right panning macro delay + 1 | phase reset macro delay + 1 | extra 4 macro delay + 1 | extra 5 macro delay + 1 | extra 6 macro delay + 1 | extra 7 macro delay + 1 | extra 8 macro delay + --- | **operator macro speeds/delay** × 4 (>=111) + 1 | AM macro speed + 1 | AR macro speed + 1 | DR macro speed + 1 | MULT macro speed + 1 | RR macro speed + 1 | SL macro speed + 1 | TL macro speed + 1 | DT2 macro speed + 1 | RS macro speed + 1 | DT macro speed + 1 | D2R macro speed + 1 | SSG-EG macro speed + 1 | DAM macro speed + 1 | DVB macro speed + 1 | EGT macro speed + 1 | KSL macro speed + 1 | SUS macro speed + 1 | VIB macro speed + 1 | WS macro speed + 1 | KSR macro speed + 1 | AM macro delay + 1 | AR macro delay + 1 | DR macro delay + 1 | MULT macro delay + 1 | RR macro delay + 1 | SL macro delay + 1 | TL macro delay + 1 | DT2 macro delay + 1 | RS macro delay + 1 | DT macro delay + 1 | D2R macro delay + 1 | SSG-EG macro delay + 1 | DAM macro delay + 1 | DVB macro delay + 1 | EGT macro delay + 1 | KSL macro delay + 1 | SUS macro delay + 1 | VIB macro delay + 1 | WS macro delay + 1 | KSR macro delay +``` + +## interpreting macro mode values + +- sequence (normal): I think this is obvious... +- ADSR: + - `val[0]`: bottom + - `val[1]`: top + - `val[2]`: attack + - `val[3]`: hold time + - `val[4]`: decay + - `val[5]`: sustain level + - `val[6]`: sustain hold time + - `val[7]`: decay 2 + - `val[8]`: release +- LFO: + - `val[11]`: speed + - `val[12]`: waveform + - 0: triangle + - 1: saw + - 2: pulse + - `val[13]`: phase + - `val[14]`: loop + - `val[15]`: global (not sure how will I implement this) + From 4e2ec442d33ce9390713c6efd17f3be75ae587bd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 22 Jan 2024 14:46:22 -0500 Subject: [PATCH 023/127] allocate ES5503 chip ID --- papers/format.md | 1 + 1 file changed, 1 insertion(+) diff --git a/papers/format.md b/papers/format.md index 3e1d847d8..b0f721624 100644 --- a/papers/format.md +++ b/papers/format.md @@ -235,6 +235,7 @@ size | description | - 0xcf: Namco C219 - 16 channels | - 0xd0: Namco C352 - 32 channels (UNAVAILABLE) | - 0xd1: ESFM - 18 channels (UNAVAILABLE) + | - 0xd2: Ensoniq ES5503 (hard pan) - 32 channels (UNAVAILABLE) | - 0xd4: PowerNoise - 4 channels (UNAVAILABLE) | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels From 8ca65585820a7543250410a75a480a09933134e5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 22 Jan 2024 14:52:05 -0500 Subject: [PATCH 024/127] update format.md once again --- papers/format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/format.md b/papers/format.md index b0f721624..dca4c0143 100644 --- a/papers/format.md +++ b/papers/format.md @@ -234,7 +234,7 @@ size | description | - 0xce: Namco C140 - 24 channels | - 0xcf: Namco C219 - 16 channels | - 0xd0: Namco C352 - 32 channels (UNAVAILABLE) - | - 0xd1: ESFM - 18 channels (UNAVAILABLE) + | - 0xd1: ESFM - 18 channels | - 0xd2: Ensoniq ES5503 (hard pan) - 32 channels (UNAVAILABLE) | - 0xd4: PowerNoise - 4 channels (UNAVAILABLE) | - 0xde: YM2610B extended - 19 channels From ae8d45b1ff746d696210b6b4b531ec524e0a6ae7 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Mon, 22 Jan 2024 14:54:27 -0500 Subject: [PATCH 025/127] move the two PowerNoise commands to the end --- src/engine/dispatch.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 8f9d2f05f..a71d87c69 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -248,11 +248,11 @@ enum DivDispatchCmds { DIV_CMD_ESFM_MODIN, // (op, value) DIV_CMD_ESFM_ENV_DELAY, // (op, value) + DIV_CMD_MACRO_RESTART, // (which) + DIV_CMD_POWER_NOISE_COUNTER_LOAD, // (which, val) DIV_CMD_POWER_NOISE_IO_WRITE, // (port, value) - DIV_CMD_MACRO_RESTART, // (which) - DIV_CMD_MAX }; From 17f4cab32dc94bfe6c94a4acee2319633f9fda96 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Mon, 22 Jan 2024 15:02:51 -0500 Subject: [PATCH 026/127] fix the MSVC complaint --- src/engine/platform/powernoise.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 79d2ca32f..0e770940f 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -22,8 +22,8 @@ #include "furIcons.h" #include -#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = v; pwrnoise_write(&pn, (uint8_t)a, (uint8_t)v); if (dumpWrites) {addWrite(a,v);} } -#define cWrite(c,a,v) rWrite((c << 3) | (a + 1), v) +#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = (v); pwrnoise_write(&pn, (uint8_t)(a), (uint8_t)(v)); if (dumpWrites) {addWrite(a,v);} } +#define cWrite(c,a,v) rWrite((c << 3) | ((a) + 1), (v)) #define noiseCtl(enable, am, tapB) (((enable) ? 0x80 : 0x00) | ((am) ? 0x02 : 0x00) | ((tapB) ? 0x01 : 0x00)) #define slopeCtl(enable, rst, a, b) (((enable) ? 0x80 : 0x00) | \ (rst ? 0x40 : 0x00) | \ From 509d20c215e17b9bff709767b2eb0e12f2053e62 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Tue, 23 Jan 2024 01:41:20 -0500 Subject: [PATCH 027/127] reduce CPU usage massively --- extern/pwrnoise/pwrnoise.c | 123 ++++++++++++++++------------- extern/pwrnoise/pwrnoise.h | 3 +- src/engine/platform/powernoise.cpp | 5 +- 3 files changed, 70 insertions(+), 61 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 631e54616..1c3351d0e 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -40,22 +40,23 @@ void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { } } -void pwrnoise_noise_step(noise_channel_t *chan) { - chan->octave_counter++; - if (chan->enable && !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { - if ((++chan->period_counter) == 4096) { +void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { + if (!chan->enable) chan->out_latch = 0; + + chan->octave_counter += cycles; + if (chan->enable && (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + chan->period_counter += (cycles >> (chan->octave + 1)); + if (cycles == 1) ++chan->period_counter; + + if (chan->period_counter >= 4096) { chan->prev = (uint8_t)(chan->lfsr >> 15); uint16_t in = ((chan->lfsr >> chan->tapa) ^ (chan->tapb_enable ? (chan->lfsr >> chan->tapb) : 0)) & 0x0001; chan->lfsr = (chan->lfsr << 1) | in; - chan->period_counter = chan->period; + chan->period_counter = chan->period + (chan->period_counter - 4096); } } - uint8_t out = chan->prev; - if (!chan->enable) out = 0; - else if (out != 0) out = chan->vol; - - chan->out_latch = out; + chan->out_latch = (chan->prev != 0) ? chan->vol : 0; } void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { @@ -96,9 +97,18 @@ void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { } } -void pwrnoise_slope_step(slope_channel_t *chan, bool force_zero) { - if (chan->enable && !((chan->octave_counter++ >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { - if ((++chan->period_counter) == 4096) { +void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero) { + if (!chan->enable) { + chan->out_latch = 0; + return; + } + + chan->octave_counter += cycles; + if (chan->enable && (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + chan->period_counter += (cycles >> (chan->octave + 1)); + if (cycles == 1) ++chan->period_counter; + + if (chan->period_counter >= 4096) { if (!chan->portion) { if ((chan->flags & 0x02) != 0) chan->accum -= chan->aoffset; else chan->accum += chan->aoffset; @@ -126,48 +136,47 @@ void pwrnoise_slope_step(slope_channel_t *chan, bool force_zero) { } } - chan->period_counter = chan->period; + chan->period_counter = chan->period + (chan->period_counter - 4096); + + uint8_t left = chan->accum >> 3; + uint8_t right = chan->accum >> 3; + + switch (chan->vol >> 4) { + case 0: + case 1: + left >>= 1; + case 2: + case 3: + left >>= 1; + case 4: + case 5: + case 6: + case 7: + left >>= 1; + default: break; + } + switch (chan->vol & 0xf) { + case 0: + case 1: + right >>= 1; + case 2: + case 3: + right >>= 1; + case 4: + case 5: + case 6: + case 7: + right >>= 1; + default: break; + } + + left &= (chan->vol >> 4); + right &= (chan->vol & 0xf); + chan->prev = (left << 4) | right; } } - uint8_t left = chan->accum >> 3; - uint8_t right = chan->accum >> 3; - - switch (chan->vol >> 4) { - case 0: - case 1: - left >>= 1; - case 2: - case 3: - left >>= 1; - case 4: - case 5: - case 6: - case 7: - left >>= 1; - default: break; - } - switch (chan->vol & 0xf) { - case 0: - case 1: - right >>= 1; - case 2: - case 3: - right >>= 1; - case 4: - case 5: - case 6: - case 7: - right >>= 1; - default: break; - } - - left &= (chan->vol >> 4); - right &= (chan->vol & 0xf); - uint8_t out = (left << 4) | right; - - if (!chan->enable || force_zero) out = 0; - chan->out_latch = out; + chan->out_latch = force_zero ? 0 : chan->prev; } void pwrnoise_reset(power_noise_t *pn) { @@ -200,14 +209,14 @@ void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val) { } } -void pwrnoise_step(power_noise_t *pn, int16_t *left, int16_t *right) { +void pwrnoise_step(power_noise_t *pn, uint16_t cycles, int16_t *left, int16_t *right) { int32_t final_left, final_right; if ((pn->flags & 0x80) != 0) { - pwrnoise_noise_step(&pn->n1); - pwrnoise_noise_step(&pn->n2); - pwrnoise_noise_step(&pn->n3); - pwrnoise_slope_step(&pn->s, (pn->n1.am && !(pn->n1.prev)) || (pn->n2.am && !(pn->n2.prev)) || (pn->n3.am && !(pn->n3.prev))); + pwrnoise_noise_step(&pn->n1, cycles); + pwrnoise_noise_step(&pn->n2, cycles); + pwrnoise_noise_step(&pn->n3, cycles); + pwrnoise_slope_step(&pn->s, cycles, (pn->n1.am && !(pn->n1.prev)) || (pn->n2.am && !(pn->n2.prev)) || (pn->n3.am && !(pn->n3.prev))); final_left = (pn->n1.out_latch >> 4) + (pn->n2.out_latch >> 4) + (pn->n3.out_latch >> 4) + (pn->s.out_latch >> 4); final_right = (pn->n1.out_latch & 0xf) + (pn->n2.out_latch & 0xf) + (pn->n3.out_latch & 0xf) + (pn->s.out_latch & 0xf); diff --git a/extern/pwrnoise/pwrnoise.h b/extern/pwrnoise/pwrnoise.h index c20da5b47..0da3978b6 100644 --- a/extern/pwrnoise/pwrnoise.h +++ b/extern/pwrnoise/pwrnoise.h @@ -52,6 +52,7 @@ typedef struct { uint8_t vol; uint8_t out_latch; + uint8_t prev; } slope_channel_t; typedef struct { @@ -66,7 +67,7 @@ typedef struct { } power_noise_t; void pwrnoise_reset(power_noise_t *pn); -void pwrnoise_step(power_noise_t *pn, int16_t *left, int16_t *right); +void pwrnoise_step(power_noise_t *pn, uint16_t cycles, int16_t *left, int16_t *right); void pwrnoise_write(power_noise_t *pn, uint8_t reg, uint8_t val); #ifdef __cplusplus diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 0e770940f..d0dbfc97d 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -19,6 +19,7 @@ #include "powernoise.h" #include "../engine.h" +#include "../../ta-log.h" #include "furIcons.h" #include @@ -81,9 +82,7 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { int16_t left, right; for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp(((pn.n1.out_latch>>1)&0x7)+(pn.n1.out_latch>>5)); oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(((pn.n2.out_latch>>1)&0x7)+(pn.n2.out_latch>>5)); From 5cd93f4ecb39c854f6905dbe83a1856c80277b0d Mon Sep 17 00:00:00 2001 From: scratchminer Date: Tue, 23 Jan 2024 01:56:46 -0500 Subject: [PATCH 028/127] more optimization --- extern/pwrnoise/pwrnoise.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 1c3351d0e..787976dfb 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -44,7 +44,7 @@ void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { if (!chan->enable) chan->out_latch = 0; chan->octave_counter += cycles; - if (chan->enable && (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + if (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { chan->period_counter += (cycles >> (chan->octave + 1)); if (cycles == 1) ++chan->period_counter; @@ -56,7 +56,7 @@ void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { } } - chan->out_latch = (chan->prev != 0) ? chan->vol : 0; + chan->out_latch = chan->prev ? chan->vol : 0; } void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { @@ -104,7 +104,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero } chan->octave_counter += cycles; - if (chan->enable && (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + if (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { chan->period_counter += (cycles >> (chan->octave + 1)); if (cycles == 1) ++chan->period_counter; From 9df3247ba32b15324bb007ab2b2ca7febce98106 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Tue, 23 Jan 2024 09:33:24 -0500 Subject: [PATCH 029/127] dumb mistake fixed --- extern/pwrnoise/pwrnoise.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 787976dfb..fd7e98996 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -41,7 +41,10 @@ void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { } void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { - if (!chan->enable) chan->out_latch = 0; + if (!chan->enable) { + chan->out_latch = 0; + return; + } chan->octave_counter += cycles; if (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { From 4145a8b034a73f7af5b20c161c805df964450dc0 Mon Sep 17 00:00:00 2001 From: scratchminer Date: Tue, 23 Jan 2024 11:05:50 -0500 Subject: [PATCH 030/127] Add descriptions for the instrument control bits --- src/gui/insEdit.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 839f122b0..dcda086c8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -328,6 +328,10 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; +const char* powernoiseControlBits[7]={ + "tap B / down B", "slope AM / down A", "reset B", "reset A", "clip B", "clip A", NULL +}; + const char* panBits[5]={ "right", "left", "rear right", "rear left", NULL }; @@ -7263,7 +7267,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWER_NOISE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); From 821553f8b535d7d000c7925fa884931e84645a70 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 02:48:34 -0500 Subject: [PATCH 031/127] PowerNoise: fix load LFSR macro --- src/engine/platform/powernoise.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index d0dbfc97d..c76386303 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -139,10 +139,9 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if ((chan[i].std.ex6.had || chan[i].std.ex7.had) && chan[i].slope) { cWrite(i, 0x05, (chan[i].std.ex6.val << 4) | chan[i].std.ex7.val); } - if (chan[i].std.ex8.had && !chan[i].slope && chan[i].active && chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + if (chan[i].std.ex8.had && !chan[i].slope) { cWrite(i, 0x03, chan[i].std.ex8.val & 0xff); cWrite(i, 0x04, chan[i].std.ex8.val >> 8); - chan[i].keyOn=true; } if (chan[i].std.vol.had) { From 05524f5d5967ed67d9993e12c683c9a21e0c5258 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 02:55:29 -0500 Subject: [PATCH 032/127] PowerNoise: type changes --- src/engine/platform/powernoise.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index c76386303..de020eaa5 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -23,7 +23,7 @@ #include "furIcons.h" #include -#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = (v); pwrnoise_write(&pn, (uint8_t)(a), (uint8_t)(v)); if (dumpWrites) {addWrite(a,v);} } +#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = (v); pwrnoise_write(&pn, (unsigned char)(a), (unsigned char)(v)); if (dumpWrites) {addWrite(a,v);} } #define cWrite(c,a,v) rWrite((c << 3) | ((a) + 1), (v)) #define noiseCtl(enable, am, tapB) (((enable) ? 0x80 : 0x00) | ((am) ? 0x02 : 0x00) | ((tapB) ? 0x01 : 0x00)) #define slopeCtl(enable, rst, a, b) (((enable) ? 0x80 : 0x00) | \ @@ -79,7 +79,7 @@ const char** DivPlatformPowerNoise::getRegisterSheet() { } void DivPlatformPowerNoise::acquire(short** buf, size_t len) { - int16_t left, right; + short left, right; for (size_t h=0; h Date: Wed, 24 Jan 2024 03:04:40 -0500 Subject: [PATCH 033/127] PowerNoise: icon TODO: letter icon in U+E15A --- src/gui/guiConst.cpp | 2 +- src/icon/furIcons.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index f2aeb311e..3254808d7 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -176,7 +176,7 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"C140",ICON_FA_VOLUME_UP,ICON_FUR_INS_C140}, {"C219",ICON_FA_VOLUME_UP,ICON_FUR_INS_C219}, {"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM}, - {"PowerNoise",ICON_FUR_NOISE,ICON_FA_QUESTION}, + {"PowerNoise",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index 5d5cec4cb..3932f5213 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -1,7 +1,7 @@ // not auto-generated. update every time you change icons.ttf! #define ICON_MIN_FUR 0xe0f0 -#define ICON_MAX_FUR 0xe159 +#define ICON_MAX_FUR 0xe15a // test #define ICON_FUR_TEST0 u8"\ue0f0" @@ -67,6 +67,7 @@ #define ICON_FUR_INS_C140 u8"\ue135" #define ICON_FUR_INS_C219 u8"\ue142" #define ICON_FUR_INS_ESFM u8"\ue143" +#define ICON_FUR_INS_POWERNOISE u8"\ue15a" // sample editor #define ICON_FUR_SAMPLE_APPLY_SILENCE u8"\ue136" From c098e0bcd181b3fe4c59463f21619f247d8a2420 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 03:15:41 -0500 Subject: [PATCH 034/127] PowerNoise: POWER_NOISE -> POWERNOISE --- src/engine/dispatch.h | 4 ++-- src/engine/dispatchContainer.cpp | 2 +- src/engine/instrument.cpp | 2 +- src/engine/instrument.h | 2 +- src/engine/platform/powernoise.cpp | 10 +++++----- src/engine/platform/powernoise.h | 4 ++-- src/engine/playback.cpp | 4 ++-- src/engine/song.h | 2 +- src/engine/sysDef.cpp | 12 ++++++------ src/gui/gui.h | 2 +- src/gui/guiConst.cpp | 6 +++--- src/gui/insEdit.cpp | 10 +++++----- src/gui/presets.cpp | 2 +- 13 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index a71d87c69..d736fcfe0 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -250,8 +250,8 @@ enum DivDispatchCmds { DIV_CMD_MACRO_RESTART, // (which) - DIV_CMD_POWER_NOISE_COUNTER_LOAD, // (which, val) - DIV_CMD_POWER_NOISE_IO_WRITE, // (port, value) + DIV_CMD_POWERNOISE_COUNTER_LOAD, // (which, val) + DIV_CMD_POWERNOISE_IO_WRITE, // (port, value) DIV_CMD_MAX }; diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index d6be1ece8..06049a69f 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -649,7 +649,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_ESFM: dispatch=new DivPlatformESFM; break; - case DIV_SYSTEM_POWER_NOISE: + case DIV_SYSTEM_POWERNOISE: dispatch=new DivPlatformPowerNoise; break; case DIV_SYSTEM_DUMMY: diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index f7f3e26b7..cbaa10f5d 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1043,7 +1043,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo featureFM=true; featureEF=true; break; - case DIV_INS_POWER_NOISE: + case DIV_INS_POWERNOISE: break; case DIV_INS_MAX: break; diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 3e7b98e62..6c78ad2f3 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -86,7 +86,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_C140=53, DIV_INS_C219=54, DIV_INS_ESFM=55, - DIV_INS_POWER_NOISE=56, + DIV_INS_POWERNOISE=56, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index de020eaa5..422ef73e5 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -284,8 +284,8 @@ void DivPlatformPowerNoise::tick(bool sysTick) { int DivPlatformPowerNoise::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWER_NOISE); - if (ins->type==DIV_INS_POWER_NOISE) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE); + if (ins->type==DIV_INS_POWERNOISE) { if (skipRegisterWrites) break; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); @@ -378,7 +378,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWER_NOISE)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE)); } if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); @@ -389,7 +389,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { case DIV_CMD_GET_VOLMAX: return 15; break; - case DIV_CMD_POWER_NOISE_COUNTER_LOAD: { + case DIV_CMD_POWERNOISE_COUNTER_LOAD: { if (chan[c.chan].slope && c.value == 0) { rWrite(0x18, c.value2 & 0x7f); } @@ -398,7 +398,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } break; } - case DIV_CMD_POWER_NOISE_IO_WRITE: + case DIV_CMD_POWERNOISE_IO_WRITE: rWrite(0x08 + (c.value << 3), c.value2); break; case DIV_CMD_MACRO_OFF: diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index c458d9fcc..8df12af32 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -17,8 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef _POWER_NOISE_H -#define _POWER_NOISE_H +#ifndef _POWERNOISE_H +#define _POWERNOISE_H #include "../dispatch.h" #include "../../../extern/pwrnoise/pwrnoise.h" diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 4494736d8..be8bc6de4 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -247,8 +247,8 @@ const char* cmdName[]={ "ESFM_MODIN", "ESFM_ENV_DELAY", - "POWER_NOISE_COUNTER_LOAD", - "POWER_NOISE_IO_WRITE", + "POWERNOISE_COUNTER_LOAD", + "POWERNOISE_IO_WRITE", "MACRO_RESTART", }; diff --git a/src/engine/song.h b/src/engine/song.h index e9e1f72e0..a4b60238a 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -133,7 +133,7 @@ enum DivSystem { DIV_SYSTEM_C140, DIV_SYSTEM_C219, DIV_SYSTEM_ESFM, - DIV_SYSTEM_POWER_NOISE, + DIV_SYSTEM_POWERNOISE, }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 1e70e9f94..5739a2312 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1985,19 +1985,19 @@ void DivEngine::registerSystems() { fmESFMPostEffectHandlerMap ); - sysDefs[DIV_SYSTEM_POWER_NOISE]=new DivSysDef( + sysDefs[DIV_SYSTEM_POWERNOISE]=new DivSysDef( "PowerNoise", NULL, 0xd4, 0, 4, false, false, 0, false, 0, 0, 0, "a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.", {"Noise 1", "Noise 2", "Noise 3", "Slope"}, {"N1", "N2", "N3", "SL"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PULSE}, - {DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE, DIV_INS_POWER_NOISE}, + {DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE}, {}, { - {0x20, {DIV_CMD_POWER_NOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}}, - {0x21, {DIV_CMD_POWER_NOISE_COUNTER_LOAD, "21xx: Load high byte of noise channel LFSR (00 to FF)", constVal<1>, effectVal}}, - {0x22, {DIV_CMD_POWER_NOISE_IO_WRITE, "22xx: Write to I/O port A", constVal<0>, effectVal}}, - {0x23, {DIV_CMD_POWER_NOISE_IO_WRITE, "23xx: Write to I/O port B", constVal<1>, effectVal}}, + {0x20, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}}, + {0x21, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "21xx: Load high byte of noise channel LFSR (00 to FF)", constVal<1>, effectVal}}, + {0x22, {DIV_CMD_POWERNOISE_IO_WRITE, "22xx: Write to I/O port A", constVal<0>, effectVal}}, + {0x23, {DIV_CMD_POWERNOISE_IO_WRITE, "23xx: Write to I/O port B", constVal<1>, effectVal}}, }, {} ); diff --git a/src/gui/gui.h b/src/gui/gui.h index a5da4d865..fc0a495b4 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -291,7 +291,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_C140, GUI_COLOR_INSTR_C219, GUI_COLOR_INSTR_ESFM, - GUI_COLOR_INSTR_POWER_NOISE, + GUI_COLOR_INSTR_POWERNOISE, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 3254808d7..5b58c3a99 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -987,7 +987,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_C140,"",ImVec4(1.0f,1.0f,0.0f,1.0f)), D(GUI_COLOR_INSTR_C219,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)), - D(GUI_COLOR_INSTR_POWER_NOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), + D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1205,7 +1205,7 @@ const int availableSystems[]={ DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_ESFM, DIV_SYSTEM_PONG, - DIV_SYSTEM_POWER_NOISE, + DIV_SYSTEM_POWERNOISE, 0 // don't remove this last one! }; @@ -1295,7 +1295,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_VRC6, DIV_SYSTEM_MMC5, DIV_SYSTEM_SM8521, - DIV_SYSTEM_POWER_NOISE, + DIV_SYSTEM_POWERNOISE, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index dcda086c8..7f64ad565 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6906,7 +6906,7 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="OP4 Noise Mode"; dutyMax=3; } - if (ins->type==DIV_INS_POWER_NOISE) { + if (ins->type==DIV_INS_POWERNOISE) { dutyMax=0; } @@ -6943,7 +6943,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_C140) waveMax=0; if (ins->type==DIV_INS_C219) waveMax=0; - if (ins->type==DIV_INS_POWER_NOISE) waveMax=0; + if (ins->type==DIV_INS_POWERNOISE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -7082,7 +7082,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_ES5506) { panMax=4095; } - if (ins->type==DIV_INS_POWER_NOISE) { + if (ins->type==DIV_INS_POWERNOISE) { panMax=15; } @@ -7173,7 +7173,7 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_C219 || ins->type==DIV_INS_TED || ins->type==DIV_INS_ESFM || - ins->type==DIV_INS_POWER_NOISE) { + ins->type==DIV_INS_POWERNOISE) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -7266,7 +7266,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } - if (ins->type==DIV_INS_POWER_NOISE) { + if (ins->type==DIV_INS_POWERNOISE) { macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 162e6cd3c..b54137dba 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -2747,7 +2747,7 @@ void FurnaceGUI::initSystemPresets() { ); ENTRY( "PowerNoise", { - CH(DIV_SYSTEM_POWER_NOISE, 1.0f, 0, "") + CH(DIV_SYSTEM_POWERNOISE, 1.0f, 0, "") } ); CATEGORY_END; From 4f7dff1feb0bab7f8d33604a607dbef98ce8ce9a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 03:23:07 -0500 Subject: [PATCH 035/127] PowerNoise: prepare to split ins type into two --- src/engine/instrument.cpp | 2 ++ src/engine/instrument.h | 1 + src/engine/sysDef.cpp | 4 ++-- src/gui/guiConst.cpp | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index cbaa10f5d..78e509046 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1045,6 +1045,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo break; case DIV_INS_POWERNOISE: break; + case DIV_INS_POWERNOISE_SLOPE: + break; case DIV_INS_MAX: break; case DIV_INS_NULL: diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 6c78ad2f3..d5d62c142 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -87,6 +87,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_C219=54, DIV_INS_ESFM=55, DIV_INS_POWERNOISE=56, + DIV_INS_POWERNOISE_SLOPE=57, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 5739a2312..082aa9f9d 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1990,8 +1990,8 @@ void DivEngine::registerSystems() { "a fantasy sound chip designed by jvsTSX and The Beesh-Spweesh!\nused in the Hexheld fantasy console.", {"Noise 1", "Noise 2", "Noise 3", "Slope"}, {"N1", "N2", "N3", "SL"}, - {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PULSE}, - {DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_WAVE}, + {DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE, DIV_INS_POWERNOISE_SLOPE}, {}, { {0x20, {DIV_CMD_POWERNOISE_COUNTER_LOAD, "20xx: Load low byte of noise channel LFSR (00 to FF) or slope channel accumulator (00 to 7F)", constVal<0>, effectVal}}, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 5b58c3a99..7991ac187 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -176,7 +176,8 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"C140",ICON_FA_VOLUME_UP,ICON_FUR_INS_C140}, {"C219",ICON_FA_VOLUME_UP,ICON_FUR_INS_C219}, {"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM}, - {"PowerNoise",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE}, + {"PowerNoise (noise)",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE}, + {"PowerNoise (slope)",ICON_FUR_SAW,ICON_FUR_INS_POWERNOISE}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; From 8fb554af5ffa99a9f3bf0de00c5111ba8a66fb35 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 03:50:26 -0500 Subject: [PATCH 036/127] PowerNoise: fix warnings --- extern/pwrnoise/pwrnoise.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index fd7e98996..a284faf21 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -20,7 +20,7 @@ void pwrnoise_noise_write(noise_channel_t *chan, uint8_t reg, uint8_t val) { chan->period = (chan->period & 0xf00) | val; break; case 3: - chan->period = (chan->period & 0xff) | ((uint16_t)val << 8) & 0xf00; + chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); chan->octave = val >> 4; break; case 4: @@ -47,7 +47,7 @@ void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { } chan->octave_counter += cycles; - if (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { + if ((cycles > 2) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { chan->period_counter += (cycles >> (chan->octave + 1)); if (cycles == 1) ++chan->period_counter; @@ -80,7 +80,7 @@ void pwrnoise_slope_write(slope_channel_t *chan, uint8_t reg, uint8_t val) { chan->period = (chan->period & 0xf00) | val; break; case 3: - chan->period = (chan->period & 0xff) | ((uint16_t)val << 8) & 0xf00; + chan->period = (chan->period & 0xff) | (((uint16_t)val << 8) & 0xf00); chan->octave = val >> 4; break; case 4: @@ -107,7 +107,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero } chan->octave_counter += cycles; - if (cycles > 2 || !(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001)) { + if ((cycles > 2) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { chan->period_counter += (cycles >> (chan->octave + 1)); if (cycles == 1) ++chan->period_counter; From efad61c7d0522623c1aab8d016f982c06fee603e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 04:35:36 -0500 Subject: [PATCH 037/127] PowerNoise: split ins type --- src/engine/platform/powernoise.cpp | 52 +++++++++++++++--------------- src/gui/gui.h | 1 + src/gui/guiConst.cpp | 1 + src/gui/insEdit.cpp | 19 ++++++++--- src/gui/settings.cpp | 4 +++ 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 422ef73e5..c6aa92a29 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -23,19 +23,20 @@ #include "furIcons.h" #include -#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a] = (v); pwrnoise_write(&pn, (unsigned char)(a), (unsigned char)(v)); if (dumpWrites) {addWrite(a,v);} } -#define cWrite(c,a,v) rWrite((c << 3) | ((a) + 1), (v)) -#define noiseCtl(enable, am, tapB) (((enable) ? 0x80 : 0x00) | ((am) ? 0x02 : 0x00) | ((tapB) ? 0x01 : 0x00)) -#define slopeCtl(enable, rst, a, b) (((enable) ? 0x80 : 0x00) | \ - (rst ? 0x40 : 0x00) | \ - (a.clip ? 0x20 : 0x00) | \ - (b.clip ? 0x10 : 0x00) | \ - (a.reset ? 0x08 : 0x00) | \ - (b.reset ? 0x04 : 0x00) | \ - (a.dir ? 0x02 : 0x00) | \ - (b.dir ? 0x01 : 0x00)) -#define volPan(v, p) (((v * (p >> 4) / 15) << 4) | ((v * (p & 0xf) / 15) & 0xf)) -#define mapAmp(a) ((((a) * 65535 / 63 - 32768) * (pn.flags & 0x7) / 7) >> 1) +#define rWrite(a,v) if (!skipRegisterWrites) {regPool[a]=(v); pwrnoise_write(&pn,(unsigned char)(a),(unsigned char)(v)); if (dumpWrites) {addWrite(a,v);}} +#define cWrite(c,a,v) rWrite((c<<3)|((a)+1),(v)) +#define noiseCtl(enable,am,tapB) (((enable)?0x80:0x00)|((am)?0x02:0x00)|((tapB)?0x01:0x00)) +#define slopeCtl(enable,rst,a,b) (((enable)?0x80:0x00)| \ + (rst?0x40:0x00)| \ + (a.clip?0x20:0x00)| \ + (b.clip?0x10:0x00)| \ + (a.reset?0x08:0x00)| \ + (b.reset?0x04:0x00)| \ + (a.dir?0x02:0x00)| \ + (b.dir?0x01:0x00)) +#define volPan(v,p) (((v*(p>>4)/15)<<4)|((v*(p&0xf)/15)&0xf)) +// TODO: optimize! +#define mapAmp(a) ((((a)*65535/63-32768)*(pn.flags&0x7)/7)>>1) #define CHIP_DIVIDER 128 const char* regCheatSheetPowerNoise[]={ @@ -285,20 +286,19 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE); - if (ins->type==DIV_INS_POWERNOISE) { - if (skipRegisterWrites) break; - if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); - chan[c.chan].freqChanged=true; - chan[c.chan].note=c.value; - } - chan[c.chan].active=true; - chan[c.chan].macroInit(ins); - if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { - chan[c.chan].outVol=chan[c.chan].vol; - } - chan[c.chan].keyOn=true; + // ?????? + if (skipRegisterWrites) break; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; } + chan[c.chan].active=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].keyOn=true; chan[c.chan].insChanged=false; break; } diff --git a/src/gui/gui.h b/src/gui/gui.h index fc0a495b4..132032bc9 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -292,6 +292,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_C219, GUI_COLOR_INSTR_ESFM, GUI_COLOR_INSTR_POWERNOISE, + GUI_COLOR_INSTR_POWERNOISE_SLOPE, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 7991ac187..ff92b38f7 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -989,6 +989,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_C219,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), + D(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"",ImVec4(1.0f,0.6f,0.3f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7f64ad565..5907b600c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6909,6 +6909,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POWERNOISE) { dutyMax=0; } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + dutyMax=0; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); @@ -6944,6 +6947,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_C140) waveMax=0; if (ins->type==DIV_INS_C219) waveMax=0; if (ins->type==DIV_INS_POWERNOISE) waveMax=0; + if (ins->type==DIV_INS_POWERNOISE_SLOPE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -7085,6 +7089,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POWERNOISE) { panMax=15; } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + panMax=15; + } if (volMax>0) { macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); @@ -7173,7 +7180,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_C219 || ins->type==DIV_INS_TED || ins->type==DIV_INS_ESFM || - ins->type==DIV_INS_POWERNOISE) { + ins->type==DIV_INS_POWERNOISE || + ins->type==DIV_INS_POWERNOISE_SLOPE) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -7268,13 +7276,16 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_POWERNOISE) { macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); - macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + } + if (ins->type==DIV_INS_POWERNOISE_SLOPE) { + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion B Offset",&ins->std.ex7Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); - macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } drawMacros(macroList,macroEditStateMacros); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2ec4a0fd2..049044cc4 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3493,6 +3493,10 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_C140,"C140"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_C219,"C219"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ESFM,"ESFM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE,"PowerNoise (noise)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"PowerNoise (slope)"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } From 74e46dfc2c555d378d73daedab16e5e7a0229e41 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 04:46:37 -0500 Subject: [PATCH 038/127] update papers --- papers/format.md | 2 +- papers/newIns.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/papers/format.md b/papers/format.md index dca4c0143..fc442e895 100644 --- a/papers/format.md +++ b/papers/format.md @@ -236,7 +236,7 @@ size | description | - 0xd0: Namco C352 - 32 channels (UNAVAILABLE) | - 0xd1: ESFM - 18 channels | - 0xd2: Ensoniq ES5503 (hard pan) - 32 channels (UNAVAILABLE) - | - 0xd4: PowerNoise - 4 channels (UNAVAILABLE) + | - 0xd4: PowerNoise - 4 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel diff --git a/papers/newIns.md b/papers/newIns.md index 8111ac4a4..341956c32 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -121,6 +121,9 @@ the following instrument types are available: - 52: TED - 53: C140 - 54: C219 +- 55: ESFM +- 56: PowerNoise (noise) +- 57: PowerNoise (slope) the following feature codes are recognized: @@ -146,6 +149,7 @@ the following feature codes are recognized: - `ES`: ES5506 ins data - `X1`: X1-010 ins data - `NE`: NES DPCM sample map data +- `EF`: ESFM ins data - `EN`: end of features - if you find this feature code, stop reading the instrument. - it will usually appear only when there sample/wave lists. From a2c252522fce9c554142c633644211b69fcb0b28 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 04:52:27 -0500 Subject: [PATCH 039/127] GUI: separate control bits for PowerNoise ins --- src/gui/insEdit.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 5907b600c..251386489 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -328,8 +328,12 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; -const char* powernoiseControlBits[7]={ - "tap B / down B", "slope AM / down A", "reset B", "reset A", "clip B", "clip A", NULL +const char* powerNoiseControlBits[7]={ + "tap B", "slope AM", "reset B", "reset A", "clip B", "clip A", NULL +}; + +const char* powerNoiseSlopeControlBits[7]={ + "down B", "down A", "reset B", "reset A", "clip B", "clip A", NULL }; const char* panBits[5]={ @@ -7275,13 +7279,13 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWERNOISE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWERNOISE_SLOPE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powernoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseSlopeControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Portion A Length",&ins->std.ex2Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion B Length",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Portion A Offset",&ins->std.ex6Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); From 4f7fd18d3ab1afc87df18894d37a58e8021bf322 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 13:14:49 -0500 Subject: [PATCH 040/127] PowerNoise: noise control bits fix --- src/engine/platform/powernoise.cpp | 46 +++++++++++++++--------------- src/gui/insEdit.cpp | 6 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index c6aa92a29..8a33d4d1c 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -81,15 +81,15 @@ const char** DivPlatformPowerNoise::getRegisterSheet() { void DivPlatformPowerNoise::acquire(short** buf, size_t len) { short left, right; - + for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp(((pn.n1.out_latch>>1)&0x7)+(pn.n1.out_latch>>5)); oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(((pn.n2.out_latch>>1)&0x7)+(pn.n2.out_latch>>5)); oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(((pn.n3.out_latch>>1)&0x7)+(pn.n3.out_latch>>5)); oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(((pn.s.out_latch>>1)&0x7)+(pn.s.out_latch>>5)); - + buf[0][h] = left; buf[1][h] = right; } @@ -109,10 +109,10 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { void DivPlatformPowerNoise::tick(bool sysTick) { for(int i = 0; i < 4; i++) { chan[i].std.next(); - + if (chan[i].std.ex1.had) { int val = chan[i].std.ex1.val; - + if (chan[i].slope) { chan[i].slopeA.clip = ((val & 0x20) != 0); chan[i].slopeB.clip = ((val & 0x10) != 0); @@ -144,7 +144,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { cWrite(i, 0x03, chan[i].std.ex8.val & 0xff); cWrite(i, 0x04, chan[i].std.ex8.val >> 8); } - + if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; @@ -165,7 +165,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].pan&=0xf0; chan[i].pan|=chan[i].std.panR.val&15; } - + if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); } @@ -184,13 +184,13 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].keyOn=true; } } - + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq = parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); - + if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; - + if (chan[i].freq >= 0x4000000) { chan[i].octave = 15; } @@ -239,12 +239,12 @@ void DivPlatformPowerNoise::tick(bool sysTick) { else { chan[i].octave = 0; } - + chan[i].freq = 0xfff-(chan[i].freq>>chan[i].octave); - + cWrite(i,0x01,chan[i].freq&0xff); cWrite(i,0x02,(chan[i].freq>>8) | (chan[i].octave<<4)); - + if (chan[i].keyOn) { if (chan[i].slope) { cWrite(i, 0x00, slopeCtl(true, false, chan[i].slopeA, chan[i].slopeB)); @@ -261,12 +261,12 @@ void DivPlatformPowerNoise::tick(bool sysTick) { cWrite(i, 0x00, noiseCtl(false, chan[i].am, chan[i].tapBEnable)); } } - + if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; } - + if (chan[i].slope) { unsigned char counter = pn.s.accum; regPool[0x18] = counter; @@ -337,7 +337,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2); - + bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -370,7 +370,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { int whatAMess = c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); - + chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -473,9 +473,9 @@ void DivPlatformPowerNoise::reset() { chan[i].std.setEngine(parent); chan[i].slope = i == 3; } - + pwrnoise_reset(&pn); - + // set master volume to full rWrite(0,0x87); // set per-channel panning @@ -504,10 +504,10 @@ void DivPlatformPowerNoise::notifyInsDeletion(void* ins) { void DivPlatformPowerNoise::setFlags(const DivConfig& flags) { chipClock=16000000; - + CHECK_CUSTOM_CLOCK; rate=chipClock/32; - + for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; } @@ -525,7 +525,7 @@ int DivPlatformPowerNoise::init(DivEngine* p, int channels, int sugRate, const D parent=p; dumpWrites=false; skipRegisterWrites=false; - + for (int i=0; i<4; i++) { oscBuf[i]=new DivDispatchOscBuffer; isMuted[i]=false; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 251386489..1dd483bdc 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -328,8 +328,8 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; -const char* powerNoiseControlBits[7]={ - "tap B", "slope AM", "reset B", "reset A", "clip B", "clip A", NULL +const char* powerNoiseControlBits[3]={ + "tap B", "slope AM", NULL }; const char* powerNoiseSlopeControlBits[7]={ @@ -7279,7 +7279,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWERNOISE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,6,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,2,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); From c1ae4a607b9740dce856eb2bf69618f199aafc08 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 13:21:06 -0500 Subject: [PATCH 041/127] please let me work ok? --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 1dd483bdc..44444d50b 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7279,7 +7279,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Noise",&ins->std.ex3Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_POWERNOISE) { - macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,2,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,powerNoiseControlBits)); macroList.push_back(FurnaceGUIMacroDesc("Tap A Location",&ins->std.ex4Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Tap B Location",&ins->std.ex5Macro,0,15,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Load LFSR",&ins->std.ex8Macro,0,16,256,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); From a9ca32ae3a0b056f0a7cb1a95f9c1b7ccd839ee0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 14:51:04 -0500 Subject: [PATCH 042/127] PowerNoise: code style fixes and stuff --- src/engine/platform/powernoise.cpp | 187 +++++++++++------------------ src/engine/platform/powernoise.h | 5 +- 2 files changed, 70 insertions(+), 122 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 8a33d4d1c..04e14c7d4 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -22,6 +22,7 @@ #include "../../ta-log.h" #include "furIcons.h" #include +#include "../bsr.h" #define rWrite(a,v) if (!skipRegisterWrites) {regPool[a]=(v); pwrnoise_write(&pn,(unsigned char)(a),(unsigned char)(v)); if (dumpWrites) {addWrite(a,v);}} #define cWrite(c,a,v) rWrite((c<<3)|((a)+1),(v)) @@ -90,8 +91,8 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(((pn.n3.out_latch>>1)&0x7)+(pn.n3.out_latch>>5)); oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(((pn.s.out_latch>>1)&0x7)+(pn.s.out_latch>>5)); - buf[0][h] = left; - buf[1][h] = right; + buf[0][h]=left; + buf[1][h]=right; } } @@ -104,45 +105,44 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { * EX6 - portion A offset (0-15) - slope only * EX7 - portion B offset (0-15) - slope only * EX8 - load LFSR (0-65535) - noise only -**/ + */ void DivPlatformPowerNoise::tick(bool sysTick) { - for(int i = 0; i < 4; i++) { + for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.ex1.had) { - int val = chan[i].std.ex1.val; + int val=chan[i].std.ex1.val; if (chan[i].slope) { - chan[i].slopeA.clip = ((val & 0x20) != 0); - chan[i].slopeB.clip = ((val & 0x10) != 0); - chan[i].slopeA.reset = ((val & 0x08) != 0); - chan[i].slopeB.reset = ((val & 0x04) != 0); - chan[i].slopeA.dir = ((val & 0x02) != 0); - chan[i].slopeB.dir = ((val & 0x01) != 0); - cWrite(i, 0x00, slopeCtl(chan[i].active, false, chan[i].slopeA, chan[i].slopeB)); - } - else { - chan[i].am = ((val & 0x02) != 0); - chan[i].tapBEnable = ((val & 0x01) != 0); - cWrite(i, 0x00, noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); + chan[i].slopeA.clip=(val&0x20); + chan[i].slopeB.clip=(val&0x10); + chan[i].slopeA.reset=(val&0x08); + chan[i].slopeB.reset=(val&0x04); + chan[i].slopeA.dir=(val&0x02); + chan[i].slopeB.dir=(val&0x01); + cWrite(i,0x00,slopeCtl(chan[i].active,false,chan[i].slopeA,chan[i].slopeB)); + } else { + chan[i].am=(val&0x02); + chan[i].tapBEnable=(val&0x01); + cWrite(i,0x00,noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); } } if (chan[i].std.ex2.had && chan[i].slope) { - cWrite(i, 0x03, chan[i].std.ex2.val); + cWrite(i,0x03,chan[i].std.ex2.val); } if (chan[i].std.ex3.had && chan[i].slope) { - cWrite(i, 0x04, chan[i].std.ex3.val); + cWrite(i,0x04,chan[i].std.ex3.val); } if ((chan[i].std.ex4.had || chan[i].std.ex5.had) && !chan[i].slope) { - cWrite(i, 0x05, (chan[i].std.ex4.val << 4) | chan[i].std.ex5.val); + cWrite(i,0x05,(chan[i].std.ex4.val<<4)|chan[i].std.ex5.val); } if ((chan[i].std.ex6.had || chan[i].std.ex7.had) && chan[i].slope) { - cWrite(i, 0x05, (chan[i].std.ex6.val << 4) | chan[i].std.ex7.val); + cWrite(i,0x05,(chan[i].std.ex6.val<<4)|chan[i].std.ex7.val); } if (chan[i].std.ex8.had && !chan[i].slope) { - cWrite(i, 0x03, chan[i].std.ex8.val & 0xff); - cWrite(i, 0x04, chan[i].std.ex8.val >> 8); + cWrite(i,0x03,chan[i].std.ex8.val&0xff); + cWrite(i,0x04,chan[i].std.ex8.val>>8); } if (chan[i].std.vol.had) { @@ -167,7 +167,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { - cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol, chan[i].pan)); + cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol,chan[i].pan)); } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -180,85 +180,35 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { if (chan[i].slope) { - cWrite(i, 0x00, slopeCtl(chan[i].active, true, chan[i].slopeA, chan[i].slopeB)); + cWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB)); chan[i].keyOn=true; } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq = parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; + chan[i].octave=MAX(bsr(chan[i].freq)-12,0); + if (chan[i].octave>15) chan[i].octave=15; + chan[i].fNum=0xfff-(chan[i].freq>>chan[i].octave); - if (chan[i].freq >= 0x4000000) { - chan[i].octave = 15; - } - else if (chan[i].freq >= 0x2000000) { - chan[i].octave = 14; - } - else if (chan[i].freq >= 0x1000000) { - chan[i].octave = 13; - } - else if (chan[i].freq >= 0x800000) { - chan[i].octave = 12; - } - else if (chan[i].freq >= 0x400000) { - chan[i].octave = 11; - } - else if (chan[i].freq >= 0x200000) { - chan[i].octave = 10; - } - else if (chan[i].freq >= 0x100000) { - chan[i].octave = 9; - } - else if (chan[i].freq >= 0x80000) { - chan[i].octave = 8; - } - else if (chan[i].freq >= 0x40000) { - chan[i].octave = 7; - } - else if (chan[i].freq >= 0x20000) { - chan[i].octave = 6; - } - else if (chan[i].freq >= 0x10000) { - chan[i].octave = 5; - } - else if (chan[i].freq >= 0x8000) { - chan[i].octave = 4; - } - else if (chan[i].freq >= 0x4000) { - chan[i].octave = 3; - } - else if (chan[i].freq >= 0x2000) { - chan[i].octave = 2; - } - else if (chan[i].freq >= 0x1000) { - chan[i].octave = 1; - } - else { - chan[i].octave = 0; - } - - chan[i].freq = 0xfff-(chan[i].freq>>chan[i].octave); - - cWrite(i,0x01,chan[i].freq&0xff); - cWrite(i,0x02,(chan[i].freq>>8) | (chan[i].octave<<4)); + cWrite(i,0x01,chan[i].fNum&0xff); + cWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4)); if (chan[i].keyOn) { if (chan[i].slope) { - cWrite(i, 0x00, slopeCtl(true, false, chan[i].slopeA, chan[i].slopeB)); - } - else { - cWrite(i, 0x00, noiseCtl(true, chan[i].am, chan[i].tapBEnable)); + cWrite(i,0x00,slopeCtl(true,false,chan[i].slopeA,chan[i].slopeB)); + } else { + cWrite(i,0x00,noiseCtl(true,chan[i].am,chan[i].tapBEnable)); } } if (chan[i].keyOff) { if (chan[i].slope) { - cWrite(i, 0x00, slopeCtl(false, false, chan[i].slopeA, chan[i].slopeB)); - } - else { - cWrite(i, 0x00, noiseCtl(false, chan[i].am, chan[i].tapBEnable)); + cWrite(i,0x00,slopeCtl(false,false,chan[i].slopeA,chan[i].slopeB)); + } else { + cWrite(i,0x00,noiseCtl(false,chan[i].am,chan[i].tapBEnable)); } } @@ -268,16 +218,19 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].slope) { - unsigned char counter = pn.s.accum; - regPool[0x18] = counter; - } - else { + unsigned char counter=pn.s.accum; + regPool[0x18]=counter; + } else { unsigned short lfsr; - if (i == 0) lfsr = pn.n1.lfsr; - else if (i == 1) lfsr = pn.n2.lfsr; - else lfsr = pn.n3.lfsr; - regPool[(i << 3) + 0x4] = lfsr & 0xff; - regPool[(i << 3) + 0x5] = lfsr >> 8; + if (i==0) { + lfsr=pn.n1.lfsr; + } else if (i==1) { + lfsr=pn.n2.lfsr; + } else { + lfsr=pn.n3.lfsr; + } + regPool[(i<<3)+0x4]=lfsr&0xff; + regPool[(i<<3)+0x5]=lfsr>>8; } } } @@ -286,8 +239,6 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE); - // ?????? - if (skipRegisterWrites) break; if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -341,8 +292,8 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; - if (chan[c.chan].baseFreq > 0x7ffffff) { - chan[c.chan].baseFreq = 0x7ffffff; + if (chan[c.chan].baseFreq>0x7ffffff) { + chan[c.chan].baseFreq=0x7ffffff; } if (chan[c.chan].baseFreq>=destFreq) { chan[c.chan].baseFreq=destFreq; @@ -350,8 +301,8 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } } else { chan[c.chan].baseFreq-=c.value; - if (chan[c.chan].baseFreq < 0) { - chan[c.chan].baseFreq = 0; + if (chan[c.chan].baseFreq<0) { + chan[c.chan].baseFreq=0; } if (chan[c.chan].baseFreq<=destFreq) { chan[c.chan].baseFreq=destFreq; @@ -369,7 +320,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); break; case DIV_CMD_LEGATO: { - int whatAMess = c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); + int whatAMess=c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)); chan[c.chan].baseFreq=NOTE_PERIODIC(whatAMess); chan[c.chan].freqChanged=true; @@ -390,16 +341,15 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { return 15; break; case DIV_CMD_POWERNOISE_COUNTER_LOAD: { - if (chan[c.chan].slope && c.value == 0) { - rWrite(0x18, c.value2 & 0x7f); - } - else if (!chan[c.chan].slope) { - cWrite(c.chan, 0x03 + c.value, c.value2); + if (chan[c.chan].slope && c.value==0) { + rWrite(0x18,c.value2&0x7f); + } else if (!chan[c.chan].slope) { + cWrite(c.chan,0x03+c.value,c.value2); } break; } case DIV_CMD_POWERNOISE_IO_WRITE: - rWrite(0x08 + (c.value << 3), c.value2); + rWrite(0x08+(c.value<<3),c.value2); break; case DIV_CMD_MACRO_OFF: chan[c.chan].std.mask(c.value,true); @@ -417,8 +367,8 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { } void DivPlatformPowerNoise::muteChannel(int ch, bool mute) { - isMuted[ch] = mute; - cWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol, chan[ch].pan)); + isMuted[ch]=mute; + cWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol,chan[ch].pan)); } void DivPlatformPowerNoise::forceIns() { @@ -446,18 +396,14 @@ DivChannelModeHints DivPlatformPowerNoise::getModeHints(int ch) { return ret; } -DivSamplePos DivPlatformPowerNoise::getSamplePos(int ch) { - return DivSamplePos(); +bool DivPlatformPowerNoise::getDCOffRequired() { + return true; } DivDispatchOscBuffer* DivPlatformPowerNoise::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformPowerNoise::mapVelocity(int ch, float vel) { - return round(15.0*pow(vel,0.22)); -} - unsigned char* DivPlatformPowerNoise::getRegisterPool() { return regPool; } @@ -471,7 +417,7 @@ void DivPlatformPowerNoise::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformPowerNoise::Channel(); chan[i].std.setEngine(parent); - chan[i].slope = i == 3; + chan[i].slope=(i==3); } pwrnoise_reset(&pn); @@ -480,9 +426,10 @@ void DivPlatformPowerNoise::reset() { rWrite(0,0x87); // set per-channel panning for (int i=0; i<4; i++) { - cWrite(i,0x06,volPan(chan[i].outVol, chan[i].pan)); + cWrite(i,0x06,volPan(chan[i].outVol,chan[i].pan)); } - addWrite(0xffffffff, 0); + // TODO: set LFSR defaults + addWrite(0xffffffff,0); } int DivPlatformPowerNoise::getOutputCount() { diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index 8df12af32..b6a1c0d28 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -37,12 +37,14 @@ class DivPlatformPowerNoise: public DivDispatch { }; struct Channel: public SharedChannel { + int fNum; unsigned char octave, pan, tapA, tapB; bool slope, am, tapBEnable, keyOn, keyOff; SlopePortion slopeA, slopeB; Channel(): SharedChannel(15), + fNum(0), octave(0), pan(255), tapA(0), @@ -72,9 +74,8 @@ class DivPlatformPowerNoise: public DivDispatch { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivChannelModeHints getModeHints(int chan); - DivSamplePos getSamplePos(int ch); + bool getDCOffRequired(); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From 1ab8c69068cbf6e52de9f1306b7687670b15b2fa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 15:04:51 -0500 Subject: [PATCH 043/127] make the arpeggio thing that explains plusgood --- doc/3-pattern/effects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index 8182d4c93..e8f02c8cf 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -34,7 +34,7 @@ however, effects are continuous, which means you only need to type it once and t - `E2xy`: **Note slide down.** `x` is the speed, while `y` is how many semitones to slide down. - --- - `EAxx`: **Toggle legato.** while on, new notes instantly change the pitch of the currently playing sound instead of starting it over. -- `00xy`: **Arpeggio.** after using this effect the channel will rapidly switch between semitone values of `note`, `note + x` and `note + y`. +- `00xy`: **Arpeggio.** this effect produces a rapid cycle between the current note, the note plus `x` semitones and the note plus `y` semitones. - `E0xx`: **Set arpeggio speed.** this sets the number of ticks between arpeggio values. default is 1. - --- - `04xy`: **Vibrato.** changes pitch to be "wavy" with a sine LFO. `x` is the speed, while `y` is the depth. From ee243a68c6a55e765976024d50ba41d3c1af9222 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 24 Jan 2024 16:21:42 -0500 Subject: [PATCH 044/127] PowerNoise: fix freq calc --- src/engine/bsr.h | 35 ++++++++-- src/engine/platform/powernoise.cpp | 102 +++++++++++++++++++---------- src/engine/platform/powernoise.h | 15 ++++- 3 files changed, 111 insertions(+), 41 deletions(-) diff --git a/src/engine/bsr.h b/src/engine/bsr.h index 437b02870..73e7e7e1d 100644 --- a/src/engine/bsr.h +++ b/src/engine/bsr.h @@ -31,22 +31,36 @@ static inline int bsr(unsigned short v) { } } +static inline int bsr32(unsigned int v) { + unsigned long idx; + if (_BitScanReverse(&idx,(unsigned long)v)) { + return idx; + } else { + return -1; + } +} + #elif defined( __GNUC__ ) -static inline int bsr(unsigned short v) -{ +static inline int bsr(unsigned short v) { if (v) { return 32 - __builtin_clz(v); + } else { + return -1; } - else{ +} + +static inline int bsr32(unsigned int v) { + if (v) { + return 32 - __builtin_clz(v); + } else { return -1; } } #else -static inline int bsr(unsigned short v) -{ +static inline int bsr(unsigned short v) { unsigned short mask = 0x8000; for (int i = 15; i >= 0; --i) { if (v&mask) @@ -57,5 +71,16 @@ static inline int bsr(unsigned short v) return -1; } +static inline int bsr32(unsigned int v) { + unsigned int mask = 0x80000000; + for (int i = 31; i >= 0; --i) { + if (v&mask) + return (int)i; + mask>>=1; + } + + return -1; +} + #endif diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 04e14c7d4..ddee331bc 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -25,7 +25,7 @@ #include "../bsr.h" #define rWrite(a,v) if (!skipRegisterWrites) {regPool[a]=(v); pwrnoise_write(&pn,(unsigned char)(a),(unsigned char)(v)); if (dumpWrites) {addWrite(a,v);}} -#define cWrite(c,a,v) rWrite((c<<3)|((a)+1),(v)) +#define chWrite(c,a,v) rWrite((c<<3)|((a)+1),(v)) #define noiseCtl(enable,am,tapB) (((enable)?0x80:0x00)|((am)?0x02:0x00)|((tapB)?0x01:0x00)) #define slopeCtl(enable,rst,a,b) (((enable)?0x80:0x00)| \ (rst?0x40:0x00)| \ @@ -38,7 +38,6 @@ #define volPan(v,p) (((v*(p>>4)/15)<<4)|((v*(p&0xf)/15)&0xf)) // TODO: optimize! #define mapAmp(a) ((((a)*65535/63-32768)*(pn.flags&0x7)/7)>>1) -#define CHIP_DIVIDER 128 const char* regCheatSheetPowerNoise[]={ "ACTL", "00", @@ -109,6 +108,8 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { void DivPlatformPowerNoise::tick(bool sysTick) { for (int i=0; i<4; i++) { + int CHIP_DIVIDER=2; + if (i==3) CHIP_DIVIDER=128; chan[i].std.next(); if (chan[i].std.ex1.had) { @@ -121,28 +122,46 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].slopeB.reset=(val&0x04); chan[i].slopeA.dir=(val&0x02); chan[i].slopeB.dir=(val&0x01); - cWrite(i,0x00,slopeCtl(chan[i].active,false,chan[i].slopeA,chan[i].slopeB)); + chWrite(i,0x00,slopeCtl(chan[i].active,false,chan[i].slopeA,chan[i].slopeB)); } else { chan[i].am=(val&0x02); chan[i].tapBEnable=(val&0x01); - cWrite(i,0x00,noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); + chWrite(i,0x00,noiseCtl(chan[i].active, chan[i].am, chan[i].tapBEnable)); } } - if (chan[i].std.ex2.had && chan[i].slope) { - cWrite(i,0x03,chan[i].std.ex2.val); - } - if (chan[i].std.ex3.had && chan[i].slope) { - cWrite(i,0x04,chan[i].std.ex3.val); - } - if ((chan[i].std.ex4.had || chan[i].std.ex5.had) && !chan[i].slope) { - cWrite(i,0x05,(chan[i].std.ex4.val<<4)|chan[i].std.ex5.val); - } - if ((chan[i].std.ex6.had || chan[i].std.ex7.had) && chan[i].slope) { - cWrite(i,0x05,(chan[i].std.ex6.val<<4)|chan[i].std.ex7.val); - } - if (chan[i].std.ex8.had && !chan[i].slope) { - cWrite(i,0x03,chan[i].std.ex8.val&0xff); - cWrite(i,0x04,chan[i].std.ex8.val>>8); + if (chan[i].slope) { + if (chan[i].std.ex2.had) { + chan[i].slopeA.len=chan[i].std.ex2.val; + chWrite(i,0x03,chan[i].std.ex2.val); + } + if (chan[i].std.ex3.had) { + chan[i].slopeB.len=chan[i].std.ex3.val; + chWrite(i,0x04,chan[i].std.ex3.val); + } + if (chan[i].std.ex6.had) { + chan[i].slopeA.offset=chan[i].std.ex6.val; + } + if (chan[i].std.ex7.had) { + chan[i].slopeB.offset=chan[i].std.ex7.val; + } + if (chan[i].std.ex6.had || chan[i].std.ex7.had) { + chWrite(i,0x05,(chan[i].slopeA.offset<<4)|chan[i].slopeB.offset); + } + } else { + if (chan[i].std.ex4.had) { + chan[i].tapA=chan[i].std.ex4.val; + } + if (chan[i].std.ex5.had) { + chan[i].tapB=chan[i].std.ex5.val; + } + if (chan[i].std.ex4.had || chan[i].std.ex5.had) { + chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB); + } + if (chan[i].std.ex8.had) { + chan[i].initLFSR=chan[i].std.ex8.val&0xffff; + chWrite(i,0x03,chan[i].std.ex8.val&0xff); + chWrite(i,0x04,chan[i].std.ex8.val>>8); + } } if (chan[i].std.vol.had) { @@ -167,7 +186,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].std.vol.had || chan[i].std.panL.had || chan[i].std.panR.had) { - cWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol,chan[i].pan)); + chWrite(i,0x06,isMuted[i]?0:volPan(chan[i].outVol,chan[i].pan)); } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -180,7 +199,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { if (chan[i].slope) { - cWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB)); + chWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB)); chan[i].keyOn=true; } } @@ -190,25 +209,28 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; - chan[i].octave=MAX(bsr(chan[i].freq)-12,0); + chan[i].octave=MAX(bsr32(chan[i].freq)-12,0); if (chan[i].octave>15) chan[i].octave=15; - chan[i].fNum=0xfff-(chan[i].freq>>chan[i].octave); + chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave); + if (chan[i].fNum<0) chan[i].fNum=0; + if (chan[i].fNum>4095) chan[i].fNum=4095; + logV("%.4x (%x, %x)",chan[i].fNum,chan[i].octave,chan[i].freq); - cWrite(i,0x01,chan[i].fNum&0xff); - cWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4)); + chWrite(i,0x01,chan[i].fNum&0xff); + chWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4)); if (chan[i].keyOn) { if (chan[i].slope) { - cWrite(i,0x00,slopeCtl(true,false,chan[i].slopeA,chan[i].slopeB)); + chWrite(i,0x00,slopeCtl(true,false,chan[i].slopeA,chan[i].slopeB)); } else { - cWrite(i,0x00,noiseCtl(true,chan[i].am,chan[i].tapBEnable)); + chWrite(i,0x00,noiseCtl(true,chan[i].am,chan[i].tapBEnable)); } } if (chan[i].keyOff) { if (chan[i].slope) { - cWrite(i,0x00,slopeCtl(false,false,chan[i].slopeA,chan[i].slopeB)); + chWrite(i,0x00,slopeCtl(false,false,chan[i].slopeA,chan[i].slopeB)); } else { - cWrite(i,0x00,noiseCtl(false,chan[i].am,chan[i].tapBEnable)); + chWrite(i,0x00,noiseCtl(false,chan[i].am,chan[i].tapBEnable)); } } @@ -236,6 +258,9 @@ void DivPlatformPowerNoise::tick(bool sysTick) { } int DivPlatformPowerNoise::dispatch(DivCommand c) { + int CHIP_DIVIDER=2; + if (c.chan==3) CHIP_DIVIDER=128; + switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE); @@ -344,7 +369,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { if (chan[c.chan].slope && c.value==0) { rWrite(0x18,c.value2&0x7f); } else if (!chan[c.chan].slope) { - cWrite(c.chan,0x03+c.value,c.value2); + chWrite(c.chan,0x03+c.value,c.value2); } break; } @@ -368,7 +393,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { void DivPlatformPowerNoise::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - cWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol,chan[ch].pan)); + chWrite(ch,0x06,isMuted[ch]?0:volPan(chan[ch].outVol,chan[ch].pan)); } void DivPlatformPowerNoise::forceIns() { @@ -426,9 +451,20 @@ void DivPlatformPowerNoise::reset() { rWrite(0,0x87); // set per-channel panning for (int i=0; i<4; i++) { - cWrite(i,0x06,volPan(chan[i].outVol,chan[i].pan)); + chWrite(i,0x06,volPan(chan[i].outVol,chan[i].pan)); } - // TODO: set LFSR defaults + // set default params so we have sound + // noise + for (int i=0; i<3; i++) { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); + chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB); + } + // slope + chWrite(3,0x03,chan[3].slopeA.len); + chWrite(3,0x04,chan[3].slopeB.len); + chWrite(3,0x05,(chan[3].slopeA.offset<<4)|chan[3].slopeB.offset); + addWrite(0xffffffff,0); } diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index b6a1c0d28..d59153fa0 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -27,6 +27,13 @@ class DivPlatformPowerNoise: public DivDispatch { struct SlopePortion { unsigned char len, offset; bool clip, reset, dir; + + SlopePortion(unsigned char l, unsigned char o, bool c, bool r, bool d): + len(l), + offset(o), + clip(c), + reset(r), + dir(d) {} SlopePortion(): len(0), @@ -38,6 +45,7 @@ class DivPlatformPowerNoise: public DivDispatch { struct Channel: public SharedChannel { int fNum; + unsigned short initLFSR; unsigned char octave, pan, tapA, tapB; bool slope, am, tapBEnable, keyOn, keyOff; SlopePortion slopeA, slopeB; @@ -45,16 +53,17 @@ class DivPlatformPowerNoise: public DivDispatch { Channel(): SharedChannel(15), fNum(0), + initLFSR(0x5555), octave(0), pan(255), - tapA(0), - tapB(0), + tapA(1), + tapB(2), slope(false), am(false), tapBEnable(false), keyOn(false), keyOff(false), - slopeA(), + slopeA(255,1,false,false,false), slopeB() {} }; From 9a6b5fb8504b3a43380314e1cc319a85d1ad559b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 01:30:39 -0500 Subject: [PATCH 045/127] PowerNoise: octave 5+ fix from upstream --- extern/pwrnoise/pwrnoise.c | 8 ++++---- src/engine/platform/powernoise.cpp | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index a284faf21..436b76f35 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -47,9 +47,9 @@ void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { } chan->octave_counter += cycles; - if ((cycles > 2) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { chan->period_counter += (cycles >> (chan->octave + 1)); - if (cycles == 1) ++chan->period_counter; + if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; if (chan->period_counter >= 4096) { chan->prev = (uint8_t)(chan->lfsr >> 15); @@ -107,9 +107,9 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero } chan->octave_counter += cycles; - if ((cycles > 2) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { + if (((cycles >= 2) && ((cycles >> (chan->octave + 1)) != 0)) || (!(((chan->octave_counter - 1) >> chan->octave) & 0x0001) && ((chan->octave_counter >> chan->octave) & 0x0001))) { chan->period_counter += (cycles >> (chan->octave + 1)); - if (cycles == 1) ++chan->period_counter; + if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; if (chan->period_counter >= 4096) { if (!chan->portion) { diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index ddee331bc..a45cb9bf1 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -214,7 +214,6 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave); if (chan[i].fNum<0) chan[i].fNum=0; if (chan[i].fNum>4095) chan[i].fNum=4095; - logV("%.4x (%x, %x)",chan[i].fNum,chan[i].octave,chan[i].freq); chWrite(i,0x01,chan[i].fNum&0xff); chWrite(i,0x02,(chan[i].fNum>>8)|(chan[i].octave<<4)); From ef9a629d76bc4f15f0c0ec4b334811c840e569ae Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 12:37:18 -0500 Subject: [PATCH 046/127] PowerNoise: add octave param --- papers/newIns.md | 9 +++++++++ src/engine/instrument.cpp | 31 +++++++++++++++++++++++++++++++ src/engine/instrument.h | 14 ++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/papers/newIns.md b/papers/newIns.md index 341956c32..d301d27c2 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -150,6 +150,7 @@ the following feature codes are recognized: - `X1`: X1-010 ins data - `NE`: NES DPCM sample map data - `EF`: ESFM ins data +- `PN`: PowerNoise ins data - `EN`: end of features - if you find this feature code, stop reading the instrument. - it will usually appear only when there sample/wave lists. @@ -657,3 +658,11 @@ size | description if some fields are missing, that's because they are defined in the SM feature. NES instruments with DPCM sample maps have both SM and NE features. + +# PowerNoise data (PN) + +``` +size | description +-----|------------------------------------ + 1 | octave +``` diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 78e509046..990af8da3 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -253,6 +253,10 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator& ); } +bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) { + return _C(octave); +} + #undef _C #define FEATURE_BEGIN(x) \ @@ -782,6 +786,14 @@ void DivInstrument::writeFeatureEF(SafeWriter* w) { FEATURE_END; } +void DivInstrument::writeFeaturePN(SafeWriter* w) { + FEATURE_BEGIN("PN"); + + w->writeC(powernoise.octave); + + FEATURE_END; +} + void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) { size_t blockStartSeek=0; size_t blockEndSeek=0; @@ -826,6 +838,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo bool featureX1=false; bool featureNE=false; bool featureEF=false; + bool featurePN=false; bool checkForWL=false; @@ -1044,8 +1057,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo featureEF=true; break; case DIV_INS_POWERNOISE: + featurePN=true; break; case DIV_INS_POWERNOISE_SLOPE: + featurePN=true; break; case DIV_INS_MAX: break; @@ -1097,6 +1112,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo if (esfm!=defaultIns.esfm) { featureEF=true; } + if (powernoise!=defaultIns.powernoise) { + featurePN=true; + } } // check ins name @@ -1242,6 +1260,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo if (featureEF) { writeFeatureEF(w); } + if (featurePN) { + writeFeaturePN(w); + } if (fui && (featureSL || featureWL)) { w->write("EN",2); @@ -2677,6 +2698,14 @@ void DivInstrument::readFeatureEF(SafeReader& reader, short version) { READ_FEAT_END; } +void DivInstrument::readFeaturePN(SafeReader& reader, short version) { + READ_FEAT_BEGIN; + + powernoise.octave=reader.readC(); + + READ_FEAT_END; +} + DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) { unsigned char featCode[2]; bool volIsCutoff=false; @@ -2747,6 +2776,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b readFeatureNE(reader,version); } else if (memcmp(featCode,"EF",2)==0) { // ESFM readFeatureEF(reader,version); + } else if (memcmp(featCode,"PN",2)==0) { // PowerNoise + readFeaturePN(reader,version); } else { if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) { // nothing diff --git a/src/engine/instrument.h b/src/engine/instrument.h index d5d62c142..80bd8f251 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -824,6 +824,17 @@ struct DivInstrumentESFM { } }; +struct DivInstrumentPowerNoise { + unsigned char octave; + + bool operator==(const DivInstrumentPowerNoise& other); + bool operator!=(const DivInstrumentPowerNoise& other) { + return !(*this==other); + } + DivInstrumentPowerNoise(): + octave(0) {} +}; + struct DivInstrument { String name; DivInstrumentType type; @@ -841,6 +852,7 @@ struct DivInstrument { DivInstrumentES5506 es5506; DivInstrumentSNES snes; DivInstrumentESFM esfm; + DivInstrumentPowerNoise powernoise; /** * these are internal functions. @@ -866,6 +878,7 @@ struct DivInstrument { void writeFeatureX1(SafeWriter* w); void writeFeatureNE(SafeWriter* w); void writeFeatureEF(SafeWriter* w); + void writeFeaturePN(SafeWriter* w); void readFeatureNA(SafeReader& reader, short version); void readFeatureFM(SafeReader& reader, short version); @@ -887,6 +900,7 @@ struct DivInstrument { void readFeatureX1(SafeReader& reader, short version); void readFeatureNE(SafeReader& reader, short version); void readFeatureEF(SafeReader& reader, short version); + void readFeaturePN(SafeReader& reader, short version); DivDataErrors readInsDataOld(SafeReader& reader, short version); DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song); From 30863e029bd2aab76a2deaf7c255d1c58e768cda Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:42:46 +0300 Subject: [PATCH 047/127] fix song collapse bringing pattern length to zero --- src/gui/editing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 183560191..555008efd 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -1665,6 +1665,10 @@ void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const Se void FurnaceGUI::doCollapseSong(int divider) { if (divider<2) return; + if (e->curSubSong->patLen Date: Thu, 25 Jan 2024 12:49:46 -0500 Subject: [PATCH 048/127] PowerNoise: and now implement it --- src/engine/platform/powernoise.cpp | 4 ++++ src/engine/platform/powernoise.h | 3 ++- src/gui/insEdit.cpp | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index a45cb9bf1..77e128ddd 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -206,6 +206,7 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + chan[i].freq>>=chan[i].octaveOff; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; @@ -268,6 +269,9 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } + if (chan[c.chan].insChanged) { + chan[c.chan].octaveOff=ins->powernoise.octave; + } chan[c.chan].active=true; chan[c.chan].macroInit(ins); if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { diff --git a/src/engine/platform/powernoise.h b/src/engine/platform/powernoise.h index d59153fa0..72f1562e9 100644 --- a/src/engine/platform/powernoise.h +++ b/src/engine/platform/powernoise.h @@ -46,7 +46,7 @@ class DivPlatformPowerNoise: public DivDispatch { struct Channel: public SharedChannel { int fNum; unsigned short initLFSR; - unsigned char octave, pan, tapA, tapB; + unsigned char octave, pan, tapA, tapB, octaveOff; bool slope, am, tapBEnable, keyOn, keyOff; SlopePortion slopeA, slopeB; @@ -58,6 +58,7 @@ class DivPlatformPowerNoise: public DivDispatch { pan(255), tapA(1), tapB(2), + octaveOff(0), slope(false), am(false), tapBEnable(false), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 44444d50b..6f334868d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7295,6 +7295,18 @@ void FurnaceGUI::drawInsEdit() { drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); } + if (ins->type==DIV_INS_POWERNOISE) { + if (ImGui::BeginTabItem("PowerNoise")) { + int pnOctave=ins->powernoise.octave; + if (ImGui::InputInt("Octave offset",&pnOctave,1,4)) { PARAMETER + if (pnOctave<0) pnOctave=0; + if (pnOctave>15) pnOctave=15; + ins->powernoise.octave=pnOctave; + } + ImGui::Text("go to Macros for other parameters."); + ImGui::EndTabItem(); + } + } if (ins->type==DIV_INS_NES || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || From 0a9566d5adb29aaa122b05382030be95f3f6fa5d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 13:22:55 -0500 Subject: [PATCH 049/127] PowerNoise: fix low periods sadly will increase CPU usage on higher notes but at least it is fixed --- extern/pwrnoise/pwrnoise.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 436b76f35..93a47a701 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -51,11 +51,11 @@ void pwrnoise_noise_step(noise_channel_t *chan, uint16_t cycles) { chan->period_counter += (cycles >> (chan->octave + 1)); if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; - if (chan->period_counter >= 4096) { + while (chan->period_counter >= 4096) { chan->prev = (uint8_t)(chan->lfsr >> 15); uint16_t in = ((chan->lfsr >> chan->tapa) ^ (chan->tapb_enable ? (chan->lfsr >> chan->tapb) : 0)) & 0x0001; chan->lfsr = (chan->lfsr << 1) | in; - chan->period_counter = chan->period + (chan->period_counter - 4096); + chan->period_counter -= 4096 - chan->period; } } @@ -111,7 +111,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero chan->period_counter += (cycles >> (chan->octave + 1)); if ((cycles >> (chan->octave + 1)) == 0) ++chan->period_counter; - if (chan->period_counter >= 4096) { + while (chan->period_counter >= 4096) { if (!chan->portion) { if ((chan->flags & 0x02) != 0) chan->accum -= chan->aoffset; else chan->accum += chan->aoffset; @@ -139,7 +139,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero } } - chan->period_counter = chan->period + (chan->period_counter - 4096); + chan->period_counter -= 4096 - chan->period; uint8_t left = chan->accum >> 3; uint8_t right = chan->accum >> 3; From 22ba9fd8b6e5658199cde9e5e6380c9c18e7fd0b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 13:30:20 -0500 Subject: [PATCH 050/127] PowerNoise: show octave param in slope ins --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 6f334868d..ec1a8d9c7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7295,7 +7295,7 @@ void FurnaceGUI::drawInsEdit() { drawMacros(macroList,macroEditStateMacros); ImGui::EndTabItem(); } - if (ins->type==DIV_INS_POWERNOISE) { + if (ins->type==DIV_INS_POWERNOISE || ins->type==DIV_INS_POWERNOISE_SLOPE) { if (ImGui::BeginTabItem("PowerNoise")) { int pnOctave=ins->powernoise.octave; if (ImGui::InputInt("Octave offset",&pnOctave,1,4)) { PARAMETER From f06f8fe7252498ba8cfa90953563ce7aafee670c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 13:33:22 -0500 Subject: [PATCH 051/127] update credits --- src/gui/about.cpp | 2 ++ src/main.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 8c014121f..fd3595162 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -44,6 +44,7 @@ const char* aboutLine[]={ "LTVA1", "MooingLemur", "OPNA2608", + "scratchminer", "superctr", "System64", "", @@ -231,6 +232,7 @@ const char* aboutLine[]={ "SM8521 emulator (modified version) by cam900", "D65010G031 emulator (modified version) by cam900", "Namco C140/C219 emulator (modified version) by cam900", + "PowerNoise emulator by scratchminer", "", "greetings to:", "NEOART Costa Rica", diff --git a/src/main.cpp b/src/main.cpp index df9a9577f..3b23068e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,6 +253,7 @@ TAParamResult pVersion(String) { printf("- SM8521 emulator (modified version) by cam900 (zlib license)\n"); printf("- D65010G031 emulator (modified version) by cam900 (zlib license)\n"); printf("- C140/C219 emulator (modified version) by cam900 (zlib license)\n"); + printf("- PowerNoise emulator by scratchminer (MIT)\n"); return TA_PARAM_QUIT; } From 7cca9ebe12cb5018c23eaa754c4428db672ed2b6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 13:54:34 -0500 Subject: [PATCH 052/127] re-enable macOS builds --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 115571cf1..01817d11b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build furnace on: push: - branches: [master, esfm-contest] + branches: master pull_request: branches: master @@ -22,8 +22,8 @@ jobs: - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } #- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } #- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - #- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - #- { name: 'macOS ARM', os: macos-latest, arch: arm64 } + - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } + - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { name: 'Linux x86_64', os: ubuntu-20.04, arch: x86_64 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false From 4deb0041f5ec7c09e3a2e5e1d4f2aee5dfff01da Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 14:38:41 -0500 Subject: [PATCH 053/127] PowerNoise: fix chan osc --- src/engine/platform/powernoise.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 77e128ddd..8738723f2 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -85,10 +85,10 @@ void DivPlatformPowerNoise::acquire(short** buf, size_t len) { for (size_t h=0; hdata[oscBuf[0]->needle++]=mapAmp(((pn.n1.out_latch>>1)&0x7)+(pn.n1.out_latch>>5)); - oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp(((pn.n2.out_latch>>1)&0x7)+(pn.n2.out_latch>>5)); - oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp(((pn.n3.out_latch>>1)&0x7)+(pn.n3.out_latch>>5)); - oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp(((pn.s.out_latch>>1)&0x7)+(pn.s.out_latch>>5)); + oscBuf[0]->data[oscBuf[0]->needle++]=mapAmp((pn.n1.out_latch&0xf)+(pn.n1.out_latch>>4)); + oscBuf[1]->data[oscBuf[1]->needle++]=mapAmp((pn.n2.out_latch&0xf)+(pn.n2.out_latch>>4)); + oscBuf[2]->data[oscBuf[2]->needle++]=mapAmp((pn.n3.out_latch&0xf)+(pn.n3.out_latch>>4)); + oscBuf[3]->data[oscBuf[3]->needle++]=mapAmp((pn.s.out_latch&0xf)+(pn.s.out_latch>>4)); buf[0][h]=left; buf[1][h]=right; From 91bddb8df6dbcb9489322fd8768938c84c30ae44 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 15:29:37 -0500 Subject: [PATCH 054/127] add MIDI debug issue #1701 --- src/engine/engine.cpp | 4 ++++ src/engine/engine.h | 5 +++++ src/engine/playback.cpp | 7 +++++++ src/gui/debugWindow.cpp | 7 +++++++ 4 files changed, 23 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 971559719..0151828d0 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3412,6 +3412,10 @@ void DivEngine::setMidiCallback(std::function what) { midiCallback=what; } +void DivEngine::setMidiDebug(bool enable) { + midiDebug=enable; +} + bool DivEngine::sendMidiMessage(TAMidiMessage& msg) { if (output==NULL) { logW("output is NULL!"); diff --git a/src/engine/engine.h b/src/engine/engine.h index 340e74c2e..c1d60bbea 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -497,6 +497,7 @@ class DivEngine { short effectSlotMap[4096]; int midiBaseChan; bool midiPoly; + bool midiDebug; size_t midiAgeCounter; blip_buffer_t* samp_bb; @@ -1208,6 +1209,9 @@ class DivEngine { // send MIDI message bool sendMidiMessage(TAMidiMessage& msg); + // enable MIDI debug + void setMidiDebug(bool enable); + // perform secure/sync operation void synchronized(const std::function& what); @@ -1329,6 +1333,7 @@ class DivEngine { cmdStreamInt(NULL), midiBaseChan(0), midiPoly(true), + midiDebug(false), midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index be8bc6de4..091924fa2 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1841,6 +1841,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi // process MIDI events (TODO: everything) if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) { TAMidiMessage& msg=output->midiIn->queue.front(); + if (midiDebug) { + if (msg.type==TA_MIDI_SYSEX) { + logD("MIDI debug: %.2X SysEx",msg.type); + } else { + logD("MIDI debug: %.2X %.2X %.2X",msg.type,msg.data[0],msg.data[1]); + } + } int ins=-1; if ((ins=midiCallback(msg))!=-2) { int chan=msg.type&15; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index ce56f470b..2d4a64e70 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -432,6 +432,13 @@ void FurnaceGUI::drawDebug() { ImGui::TreePop(); } + if (ImGui::TreeNode("MIDI Debug")) { + if (ImGui::Button("Enable Debug (go to log viewer")) { + e->setMidiDebug(true); + nextWindow=GUI_WINDOW_LOG; + } + ImGui::TreePop(); + } if (ImGui::TreeNode("Visualizer Debug")) { if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); From ed007526ffccd72d519ad5d188e95707732ccf62 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 15:46:08 -0500 Subject: [PATCH 055/127] BSR debug --- src/engine/bsr.h | 4 ++-- src/engine/platform/powernoise.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/bsr.h b/src/engine/bsr.h index 73e7e7e1d..72191f6bd 100644 --- a/src/engine/bsr.h +++ b/src/engine/bsr.h @@ -64,7 +64,7 @@ static inline int bsr(unsigned short v) { unsigned short mask = 0x8000; for (int i = 15; i >= 0; --i) { if (v&mask) - return (int)i; + return i; mask>>=1; } @@ -75,7 +75,7 @@ static inline int bsr32(unsigned int v) { unsigned int mask = 0x80000000; for (int i = 31; i >= 0; --i) { if (v&mask) - return (int)i; + return i; mask>>=1; } diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 8738723f2..941e8d2ce 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -210,7 +210,9 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; - chan[i].octave=MAX(bsr32(chan[i].freq)-12,0); + int bsr32Val=bsr32(chan[i].freq); + logV("%x: %d",chan[i].freq,bsr32Val); + chan[i].octave=MAX(bsr32Val-12,0); if (chan[i].octave>15) chan[i].octave=15; chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave); if (chan[i].fNum<0) chan[i].fNum=0; From 04d41dc4e15b9db90429efe4f176b4aafd89768a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:04:34 -0500 Subject: [PATCH 056/127] fix bsr() now it's the same result across compilers and systems --- src/engine/bsr.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/bsr.h b/src/engine/bsr.h index 72191f6bd..dcc865112 100644 --- a/src/engine/bsr.h +++ b/src/engine/bsr.h @@ -24,7 +24,7 @@ static inline int bsr(unsigned short v) { unsigned long idx; if (_BitScanReverse(&idx,(unsigned long)v)) { - return idx; + return idx+1; } else { return -1; @@ -34,7 +34,7 @@ static inline int bsr(unsigned short v) { static inline int bsr32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx,(unsigned long)v)) { - return idx; + return idx+1; } else { return -1; } @@ -62,7 +62,7 @@ static inline int bsr32(unsigned int v) { static inline int bsr(unsigned short v) { unsigned short mask = 0x8000; - for (int i = 15; i >= 0; --i) { + for (int i = 16; i >= 0; --i) { if (v&mask) return i; mask>>=1; @@ -73,7 +73,7 @@ static inline int bsr(unsigned short v) { static inline int bsr32(unsigned int v) { unsigned int mask = 0x80000000; - for (int i = 31; i >= 0; --i) { + for (int i = 32; i >= 0; --i) { if (v&mask) return i; mask>>=1; From 8487a7516d94f45a5003f83eb37139beffe4476e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:19:55 -0500 Subject: [PATCH 057/127] what? it was still there? issue #1562 issue #1701 --- src/engine/playback.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 091924fa2..d7b8426e8 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1853,8 +1853,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi int chan=msg.type&15; switch (msg.type&0xf0) { case TA_MIDI_NOTE_OFF: { - if (chan<0 || chan>=chans) break; if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); @@ -1867,15 +1867,16 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } case TA_MIDI_NOTE_ON: { - if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } } else { if (midiIsDirect) { + if (chan<0 || chan>=chans) break; pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true)); } else { autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); @@ -1890,6 +1891,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } } + } else if (midiDebug) { + logD("callback wants ignore"); } //logD("%.2x",msg.type); output->midiIn->queue.pop(); From f04eaffa4bd26a2d946a57ac42125029643d5a7d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:29:02 -0500 Subject: [PATCH 058/127] PowerNoise: apply slope fix --- extern/pwrnoise/pwrnoise.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.h b/extern/pwrnoise/pwrnoise.h index 0da3978b6..360176e40 100644 --- a/extern/pwrnoise/pwrnoise.h +++ b/extern/pwrnoise/pwrnoise.h @@ -41,8 +41,8 @@ typedef struct { uint8_t alength; uint8_t blength; - uint8_t a; - uint8_t b; + uint16_t a; + uint16_t b; bool portion; uint8_t aoffset; From 5164513fdfcea4546ffcda182c09a3ad11e73629 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:36:01 -0500 Subject: [PATCH 059/127] kill an ymfm warning possible compiler false-positive but it was hindering Windows build --- src/engine/platform/sound/ymfm/ymfm_fm.h | 2 +- src/engine/platform/sound/ymfm/ymfm_fm.ipp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index 3ac36b50d..f77e89434 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -459,7 +459,7 @@ protected: uint8_t m_clock_prescale; // prescale factor (2/3/6) uint8_t m_irq_mask; // mask of which bits signal IRQs uint8_t m_irq_state; // current IRQ state - uint8_t m_timer_running[2]; // current timer running state + uint8_t m_timer_running[4]; // current timer running state uint8_t m_total_clocks; // low 8 bits of the total number of clocks processed uint32_t m_active_channels; // mask of active channels (computed by prepare) uint32_t m_modified_channels; // mask of channels that have been modified diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 7a37ec8a3..a18eafdfb 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -1198,7 +1198,7 @@ fm_engine_base::fm_engine_base(ymfm_interface &intf) : m_clock_prescale(RegisterType::DEFAULT_PRESCALE), m_irq_mask(STATUS_TIMERA | STATUS_TIMERB), m_irq_state(0), - m_timer_running{0,0}, + m_timer_running{0,0,0,0}, m_total_clocks(0), m_active_channels(ALL_CHANNELS), m_modified_channels(ALL_CHANNELS), From 20ffd5147ffe6e2eaed6716647440d2e4f1cb0ac Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:41:31 -0500 Subject: [PATCH 060/127] GUI: add stereo 86PCM presets not replacing the previous ones due to uncertainty and lack of reliable sources issue #1575 --- src/gui/presets.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index b54137dba..0c8894615 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -767,6 +767,34 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") } ); + ENTRY( + "NEC PC-98 (with PC-9801-86) stereo", { // -73 also has OPNA + CH(DIV_SYSTEM_YM2608, 1.0f, 0, "clockSel=1"), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, -1.0f, // 2x 16-bit Burr Brown DAC + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, 1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") + } + ); + ENTRY( + "NEC PC-98 (with PC-9801-86; extended channel 3) stereo", { // -73 also has OPNA + CH(DIV_SYSTEM_YM2608_EXT, 1.0f, 0, "clockSel=1"), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, -1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCM_DAC, 1.0f, 1.0f, + "rate=44100\n" + "outDepth=15\n" + ), + CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "clockSel=1") + } + ); ENTRY( "NEC PC-98 (with PC-9801-73)", { CH(DIV_SYSTEM_YM2608, 1.0f, 0, "clockSel=1"), From faf600954832b4a00ae8ca052239f511e88ce407 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 19:48:49 -0500 Subject: [PATCH 061/127] temporarily disable safe mode mechanism issue #1581 --- src/engine/engine.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 0151828d0..eb02287ab 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3753,12 +3753,16 @@ bool DivEngine::preInit(bool noSafeMode) { initConfDir(); logD("config path: %s",configPath.c_str()); + // TODO: re-enable with a better approach + // see issue #1581 + /* if (!noSafeMode) { String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode"; if (touchFile(safeModePath.c_str())==-EEXIST) { wantSafe=true; } } + */ String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log"; startLogFile(logPath.c_str()); @@ -3782,8 +3786,12 @@ bool DivEngine::preInit(bool noSafeMode) { } void DivEngine::everythingOK() { + // TODO: re-enable with a better approach + // see issue #1581 + /* String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode"; deleteFile(safeModePath.c_str()); + */ } bool DivEngine::init() { From 6ead5235c43c579e9d048036a973bc6ba270575e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 20:01:29 -0500 Subject: [PATCH 062/127] GUI: de-localize this error --- src/gui/editing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 555008efd..549cffaf0 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -1666,7 +1666,7 @@ void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const Se void FurnaceGUI::doCollapseSong(int divider) { if (divider<2) return; if (e->curSubSong->patLen Date: Thu, 25 Jan 2024 20:54:02 -0500 Subject: [PATCH 063/127] CI: possibly work around macOS resource busy probl --- .github/workflows/build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01817d11b..67dfc7033 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -286,13 +286,20 @@ jobs: pushd build cpack + echo "making dirs" mkdir orig mkdir new + echo "attaching" echo "y" | hdiutil attach Furnace-*-Darwin.dmg -readonly -mount required -mountpoint orig + echo "copying" cp -v -r orig/Furnace.app new/Furnace.app + echo "synchronizing" + sync + echo "detaching" hdiutil detach orig + echo "removing orig" rmdir orig rm Furnace-*-Darwin.dmg @@ -301,6 +308,7 @@ jobs: rmdir new/Furnace.app/Contents/Resources/bin fi + echo "copying extra stuff" cp -v ../LICENSE new/LICENSE.txt cp -v ../res/releaseReadme/stable-mac.txt new/README cp -v -r ../demos new/demos @@ -310,6 +318,7 @@ jobs: wget https://tildearrow.org/furnace/doc/latest/manual.pdf cd .. + echo "creating new image" hdiutil create -srcfolder new -volname Furnace -format UDZO furnace.dmg mv furnace.dmg ../${{ steps.package-identify.outputs.filename }} From 2af85ba80de4fe52de609ae0262cab0fb7ee8cab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 21:16:51 -0500 Subject: [PATCH 064/127] work around the workaround --- .github/workflows/build.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67dfc7033..b052f2cef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -284,7 +284,16 @@ jobs: if: ${{ runner.os == 'macOS' }} run: | pushd build - cpack + retries=0 + while ! cpack; do + echo "TRYING AGAIN..." + retries=$((retries+1)) + if [ $retries -gt 5 ]; then + echo "OH NO, WE'VE FAILED..." + exit 1 + break + fi + done echo "making dirs" mkdir orig From 8146405f886346b78aec166f1b0cbeb84127398c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 25 Jan 2024 22:28:18 -0500 Subject: [PATCH 065/127] fasdasdf --- src/engine/platform/powernoise.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 941e8d2ce..9b1a06dbc 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -211,7 +211,6 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x7ffffff) chan[i].freq=0x7ffffff; int bsr32Val=bsr32(chan[i].freq); - logV("%x: %d",chan[i].freq,bsr32Val); chan[i].octave=MAX(bsr32Val-12,0); if (chan[i].octave>15) chan[i].octave=15; chan[i].fNum=0x1000-(chan[i].freq>>chan[i].octave); From 73f65d4721408665486baa3bcf091a9a5540babd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 00:48:55 -0500 Subject: [PATCH 066/127] GUI: why --- src/gui/debugWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 2d4a64e70..5dca05edb 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -433,7 +433,7 @@ void FurnaceGUI::drawDebug() { ImGui::TreePop(); } if (ImGui::TreeNode("MIDI Debug")) { - if (ImGui::Button("Enable Debug (go to log viewer")) { + if (ImGui::Button("Enable Debug (go to log viewer)")) { e->setMidiDebug(true); nextWindow=GUI_WINDOW_LOG; } From ba8cd2f6721cd22f6086d3ca8e8a146979424c38 Mon Sep 17 00:00:00 2001 From: Lunathir <18320914+lunathir@users.noreply.github.com> Date: Thu, 25 Jan 2024 21:55:45 -0800 Subject: [PATCH 067/127] Update build.yml --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b052f2cef..9a5a1fce2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4.1.1 with: submodules: recursive @@ -106,7 +106,7 @@ jobs: - name: Setup Toolchain [Windows MSVC] if: ${{ matrix.config.compiler == 'msvc' }} - uses: vadz/gha-setup-vsdevenv@avoid-deprecation-warnings + uses: lunathir/gha-setup-vsdevenv@avoid-deprecation-warnings with: arch: ${{ steps.windows-identify.outputs.vswhere-target }} @@ -374,7 +374,7 @@ jobs: - name: Upload artifact if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'master' }} - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4.3.0 with: name: ${{ steps.package-identify.outputs.id }} path: ${{ steps.package-identify.outputs.filename }} From c55cc8b2ea7007df172d0eba8d5ad54abb4622e1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 02:44:47 -0500 Subject: [PATCH 068/127] GUI: delayed layout loading issue #1629 --- src/gui/gui.cpp | 14 ++++++++++++++ src/gui/gui.h | 3 +++ src/gui/settings.cpp | 9 ++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 359c27e51..b93c13753 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4001,12 +4001,24 @@ bool FurnaceGUI::loop() { layoutTimeBegin=SDL_GetPerformanceCounter(); + if (pendingLayoutImport!=NULL) { + ImGui::LoadIniSettingsFromMemory((const char*)pendingLayoutImport,pendingLayoutImportLen); + } + if (!rend->newFrame()) { fontsFailed=true; } ImGui_ImplSDL2_NewFrame(sdlWin); ImGui::NewFrame(); + if (pendingLayoutImport!=NULL) { + WAKE_UP; + ImGui::Render(); + delete[] pendingLayoutImport; + pendingLayoutImport=NULL; + continue; + } + // one second counter secondTimer+=ImGui::GetIO().DeltaTime; if (secondTimer>=1.0f) secondTimer=fmod(secondTimer,1.0f); @@ -7263,6 +7275,8 @@ FurnaceGUI::FurnaceGUI(): headFont(NULL), fontRange(NULL), prevInsData(NULL), + pendingLayoutImport(NULL), + pendingLayoutImportLen(0), curIns(0), curWave(0), curSample(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 132032bc9..fba435170 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1976,6 +1976,9 @@ class FurnaceGUI { DivInstrument* prevInsData; + unsigned char* pendingLayoutImport; + size_t pendingLayoutImportLen; + int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, newSongCategory, latchTarget; int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 049044cc4..584ce17ad 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -4760,18 +4760,17 @@ bool FurnaceGUI::importLayout(String path) { fclose(f); return false; } - unsigned char* file=new unsigned char[len]; - if (fread(file,1,(size_t)len,f)!=(size_t)len) { + pendingLayoutImport=new unsigned char[len]; + if (fread(pendingLayoutImport,1,(size_t)len,f)!=(size_t)len) { perror("read error"); lastError=fmt::sprintf("on read: %s",strerror(errno)); fclose(f); - delete[] file; + delete[] pendingLayoutImport; return false; } fclose(f); - ImGui::LoadIniSettingsFromMemory((const char*)file,len); - delete[] file; + pendingLayoutImportLen=len; return true; } From 8f2084bcd66a27d1d2c9392e248743bd7c390d0d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 02:45:00 -0500 Subject: [PATCH 069/127] PowerNoise: prepare to write some docs --- doc/4-instrument/powernoise.md | 27 +++++++++++++++++++++++++++ doc/7-systems/powernoise.md | 23 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 doc/4-instrument/powernoise.md create mode 100644 doc/7-systems/powernoise.md diff --git a/doc/4-instrument/powernoise.md b/doc/4-instrument/powernoise.md new file mode 100644 index 000000000..d7b8fbb23 --- /dev/null +++ b/doc/4-instrument/powernoise.md @@ -0,0 +1,27 @@ +# PowerNoise instrument editor + +the PowerNoise instrument editor consists of two tabs. + +## LFSR-based synthesis + +this will be finished later... it's so late and I need to sleep now + +## Macros + +- **Volume**: volume sequence. +- **Arpeggio**: pitch sequence. +- **Duty/Noise**: duty cycle and noise mode. + - pulse duty cycles: + - `0`: 12.5% + - `1`: 25% + - `2`: 50% + - `3`: 75% + - noise modes: + - `0`: long noise + - `1`: short noise +- **Pitch**: fine pitch. +- **Phase Reset**: trigger restart of waveform. + +## PowerNoise tab + +this tab allows you to change the base octave - important when you have set a longer LFSR pattern. diff --git a/doc/7-systems/powernoise.md b/doc/7-systems/powernoise.md new file mode 100644 index 000000000..37b2b1e05 --- /dev/null +++ b/doc/7-systems/powernoise.md @@ -0,0 +1,23 @@ +# PowerNoise + +a fantasy sound chip created by StinkerB06 and jvsTSX for the Hexheld fantasy video game console. + +its design employs linear-feedback shift registers (LFSR) for sound generation. this technology is used in many random number generators to produce noise, but it is also capable of producing other tones. + +it has three noise channels and one "slope" channel capable of generating a variety of saw waves. + +it outputs stereo sound with 4-bit volume control per channel and 3-bit master volume control. + +refer to its instrument type's documentation for details on sound synthesis. + +## effect commands + +- `20xx`: **load LFSR value (low byte).** + - on the slope channel, this sets its accumulator (from `00` to `7F`). +- `21xx`: **load LFSR value (high byte).** +- `22xx`: **write to I/O port A.** +- `23xx`: **write to I/O port B.** + +## info + +this chip uses the [PowerNoise](../4-instrument/powernoise.md) instrument editor. From a34a19cb4f8700ea6700f2b5b0b55a9f09d84903 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 12:37:18 -0500 Subject: [PATCH 070/127] name correction --- doc/7-systems/powernoise.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7-systems/powernoise.md b/doc/7-systems/powernoise.md index 37b2b1e05..9b9b40fc0 100644 --- a/doc/7-systems/powernoise.md +++ b/doc/7-systems/powernoise.md @@ -1,6 +1,6 @@ # PowerNoise -a fantasy sound chip created by StinkerB06 and jvsTSX for the Hexheld fantasy video game console. +a fantasy sound chip created by The Beesh-Spweesh! and jvsTSX for the Hexheld fantasy video game console. its design employs linear-feedback shift registers (LFSR) for sound generation. this technology is used in many random number generators to produce noise, but it is also capable of producing other tones. From 1ba7f71a0fa3608c827de4b6d68fa8f77acce147 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 13:22:23 -0500 Subject: [PATCH 071/127] PowerNoise: fix slope reset bug by scratchminer --- extern/pwrnoise/pwrnoise.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/pwrnoise/pwrnoise.c b/extern/pwrnoise/pwrnoise.c index 93a47a701..40ef9a6ab 100644 --- a/extern/pwrnoise/pwrnoise.c +++ b/extern/pwrnoise/pwrnoise.c @@ -120,7 +120,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero chan->accum &= 0x7f; if (++chan->a > chan->alength) { - if ((chan->flags & 0x04) != 0) chan->accum = (chan->flags & 0x02) ? 0x7f : 0x00; + if ((chan->flags & 0x04) != 0) chan->accum = (chan->flags & 0x01) ? 0x7f : 0x00; chan->b = 0x00; chan->portion = true; } @@ -133,7 +133,7 @@ void pwrnoise_slope_step(slope_channel_t *chan, uint16_t cycles, bool force_zero chan->accum &= 0x7f; if (++chan->b > chan->blength) { - if ((chan->flags & 0x08) != 0) chan->accum = (chan->flags & 0x01) ? 0x7f : 0x00; + if ((chan->flags & 0x08) != 0) chan->accum = (chan->flags & 0x02) ? 0x7f : 0x00; chan->a = 0x00; chan->portion = false; } From 9130fffce7c6c375f8f27ba936c8f31d498aa9f3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 15:08:45 -0500 Subject: [PATCH 072/127] PowerNoise: fix phase reset macro --- src/engine/platform/powernoise.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 9b1a06dbc..91dab9482 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -201,6 +201,9 @@ void DivPlatformPowerNoise::tick(bool sysTick) { if (chan[i].slope) { chWrite(i,0x00,slopeCtl(chan[i].active,true,chan[i].slopeA,chan[i].slopeB)); chan[i].keyOn=true; + } else { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); } } From b3c82861070ba95e489855349756b06ff2b68778 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 26 Jan 2024 16:19:46 -0500 Subject: [PATCH 073/127] PowerNoise: fix phase reset macro and improve LFSR load one --- src/engine/platform/powernoise.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/powernoise.cpp b/src/engine/platform/powernoise.cpp index 91dab9482..353d1ffe8 100644 --- a/src/engine/platform/powernoise.cpp +++ b/src/engine/platform/powernoise.cpp @@ -158,9 +158,11 @@ void DivPlatformPowerNoise::tick(bool sysTick) { chWrite(i,0x05,(chan[i].tapA<<4)|chan[i].tapB); } if (chan[i].std.ex8.had) { - chan[i].initLFSR=chan[i].std.ex8.val&0xffff; - chWrite(i,0x03,chan[i].std.ex8.val&0xff); - chWrite(i,0x04,chan[i].std.ex8.val>>8); + if (chan[i].initLFSR!=(chan[i].std.ex8.val&0xffff)) { + chan[i].initLFSR=chan[i].std.ex8.val&0xffff; + chWrite(i,0x03,chan[i].std.ex8.val&0xff); + chWrite(i,0x04,chan[i].std.ex8.val>>8); + } } } @@ -407,6 +409,10 @@ void DivPlatformPowerNoise::forceIns() { for (int i=0; i<4; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; + if (i<3) { + chWrite(i,0x03,chan[i].initLFSR&0xff); + chWrite(i,0x04,chan[i].initLFSR>>8); + } } } From 845594fbd24d8d4d3129360fe0f190718fcb6ff0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 27 Jan 2024 01:00:58 -0500 Subject: [PATCH 074/127] and again --- .github/workflows/build.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a5a1fce2..6aac414cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -328,7 +328,17 @@ jobs: cd .. echo "creating new image" - hdiutil create -srcfolder new -volname Furnace -format UDZO furnace.dmg + retries=0 + while ! hdiutil create -srcfolder new -volname Furnace -format UDZO furnace.dmg; do + echo "TRYING AGAIN..." + retries=$((retries+1)) + if [ $retries -gt 5 ]; then + echo "OH NO, WE'VE FAILED..." + exit 1 + break + fi + sleep 5 + done mv furnace.dmg ../${{ steps.package-identify.outputs.filename }} popd From d8e48301fdc0ae6a3badae2a45dfbfe72a726c5c Mon Sep 17 00:00:00 2001 From: psykose Date: Sat, 27 Jan 2024 07:52:42 +0000 Subject: [PATCH 075/127] fix finding freetype via pkg-config --- CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a8f3805c..c3bf5b826 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,14 +198,14 @@ if (BUILD_GUI AND USE_FREETYPE) # TODO: FIX FIX FIX FIX FIX if (SYSTEM_FREETYPE) if (PKG_CONFIG_FOUND) - pkg_check_modules(FREETYPE freetype>=2.0.0) + pkg_check_modules(FREETYPE freetype2>=2.0.0) if (FREETYPE_FOUND) - list(APPEND DEPENDENCIES_INCLUDE_DIRS ${FMT_INCLUDE_DIRS}) - list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${FMT_CFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LIBRARIES ${FMT_LIBRARIES}) - list(APPEND DEPENDENCIES_LIBRARY_DIRS ${FMT_LIBRARY_DIRS}) - list(APPEND DEPENDENCIES_LINK_OPTIONS ${FMT_LDFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${FMT_LDFLAGS}) + list(APPEND DEPENDENCIES_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS}) + list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${FREETYPE_CFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LIBRARIES ${FREETYPE_LIBRARIES}) + list(APPEND DEPENDENCIES_LIBRARY_DIRS ${FREETYPE_LIBRARY_DIRS}) + list(APPEND DEPENDENCIES_LINK_OPTIONS ${FREETYPE_LDFLAGS_OTHER}) + list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${FREETYPE_LDFLAGS}) endif() endif() if (NOT FREETYPE_FOUND) From 45af9efd7948d266aa74bf8cbc47a4bd2e21f091 Mon Sep 17 00:00:00 2001 From: Abdullah-Ajeebi <111107624+Abdullah-Ajeebi@users.noreply.github.com> Date: Sat, 27 Jan 2024 10:53:20 +0300 Subject: [PATCH 076/127] Update qsound.md --- doc/4-instrument/qsound.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/4-instrument/qsound.md b/doc/4-instrument/qsound.md index 3e2f54ed5..37711fd0a 100644 --- a/doc/4-instrument/qsound.md +++ b/doc/4-instrument/qsound.md @@ -1,6 +1,6 @@ # Capcom QSound instrument editor -the QSound instrument editor contains three tabs: Sample and Macros. +the QSound instrument editor contains two tabs: Sample and Macros. ## Sample From be35b50ec82486cbb301f81d6d07c9320cb0e2c8 Mon Sep 17 00:00:00 2001 From: Abdullah-Ajeebi <111107624+Abdullah-Ajeebi@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:48:47 +0300 Subject: [PATCH 077/127] Update song-info.md --- doc/2-interface/song-info.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/2-interface/song-info.md b/doc/2-interface/song-info.md index 1ead4f879..24a42f95e 100644 --- a/doc/2-interface/song-info.md +++ b/doc/2-interface/song-info.md @@ -30,7 +30,7 @@ items in _italic_ don't appear in basic mode and are only available in advanced - clicking the Tick Rate button switches to a more traditional **Base Tempo** BPM setting. **Speed**: the number of ticks per row. -- clicking the "Speed" button changes to more complex modes covered in the [grooves] page. +- clicking the "Speed" button changes to more complex modes covered in the [grooves](../8-advanced/grooves.md) page. _**Virtual Tempo**:_ Simulates any arbitrary tempo without altering the tick rate. it does this by adding or skipping ticks to approximate the tempo. the two numbers represent a ratio applied to the actual tick rate. example: - set tick rate to 150 BPM (60 Hz) and speed to 6. From 68ee5167eed6db7e965533d84f88b1d56f8bf244 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 27 Jan 2024 13:27:16 -0500 Subject: [PATCH 078/127] GUI: work around layout import crash? not very well tested... may mess up layout issue #1629 --- src/gui/gui.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++------- src/gui/gui.h | 2 ++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b93c13753..21e2e5164 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3426,6 +3426,10 @@ bool FurnaceGUI::detectOutOfBoundsWindow(SDL_Rect& failing) { _x; \ MEASURE_END(_n) +#define IMPORT_CLOSE(x) \ + if (x) pendingLayoutImportReopen.push(&x); \ + x=false; + bool FurnaceGUI::loop() { DECLARE_METRIC(calcChanOsc) DECLARE_METRIC(mobileControls) @@ -4002,7 +4006,59 @@ bool FurnaceGUI::loop() { layoutTimeBegin=SDL_GetPerformanceCounter(); if (pendingLayoutImport!=NULL) { - ImGui::LoadIniSettingsFromMemory((const char*)pendingLayoutImport,pendingLayoutImportLen); + if (pendingLayoutImportStep==0) { + IMPORT_CLOSE(editControlsOpen); + IMPORT_CLOSE(ordersOpen); + IMPORT_CLOSE(insListOpen); + IMPORT_CLOSE(songInfoOpen); + IMPORT_CLOSE(patternOpen); + IMPORT_CLOSE(insEditOpen); + IMPORT_CLOSE(waveListOpen); + IMPORT_CLOSE(waveEditOpen); + IMPORT_CLOSE(sampleListOpen); + IMPORT_CLOSE(sampleEditOpen); + IMPORT_CLOSE(aboutOpen); + IMPORT_CLOSE(settingsOpen); + IMPORT_CLOSE(mixerOpen); + IMPORT_CLOSE(debugOpen); + IMPORT_CLOSE(inspectorOpen); + IMPORT_CLOSE(oscOpen); + IMPORT_CLOSE(volMeterOpen); + IMPORT_CLOSE(statsOpen); + IMPORT_CLOSE(compatFlagsOpen); + IMPORT_CLOSE(pianoOpen); + IMPORT_CLOSE(notesOpen); + IMPORT_CLOSE(channelsOpen); + IMPORT_CLOSE(regViewOpen); + IMPORT_CLOSE(logOpen); + IMPORT_CLOSE(effectListOpen); + IMPORT_CLOSE(chanOscOpen); + IMPORT_CLOSE(subSongsOpen); + IMPORT_CLOSE(findOpen); + IMPORT_CLOSE(spoilerOpen); + IMPORT_CLOSE(patManagerOpen); + IMPORT_CLOSE(sysManagerOpen); + IMPORT_CLOSE(clockOpen); + IMPORT_CLOSE(speedOpen); + IMPORT_CLOSE(groovesOpen); + IMPORT_CLOSE(xyOscOpen); + } else if (pendingLayoutImportStep==1) { + // let the UI settle + } else if (pendingLayoutImportStep==2) { + ImGui::LoadIniSettingsFromMemory((const char*)pendingLayoutImport,pendingLayoutImportLen); + } else if (pendingLayoutImportStep==3) { + // restore open windows + while (!pendingLayoutImportReopen.empty()) { + bool* next=pendingLayoutImportReopen.front(); + *next=true; + pendingLayoutImportReopen.pop(); + } + } else if (pendingLayoutImportStep==4) { + delete[] pendingLayoutImport; + pendingLayoutImport=NULL; + } + pendingLayoutImportStep++; + if (pendingLayoutImport==NULL) pendingLayoutImportStep=0; } if (!rend->newFrame()) { @@ -4011,14 +4067,6 @@ bool FurnaceGUI::loop() { ImGui_ImplSDL2_NewFrame(sdlWin); ImGui::NewFrame(); - if (pendingLayoutImport!=NULL) { - WAKE_UP; - ImGui::Render(); - delete[] pendingLayoutImport; - pendingLayoutImport=NULL; - continue; - } - // one second counter secondTimer+=ImGui::GetIO().DeltaTime; if (secondTimer>=1.0f) secondTimer=fmod(secondTimer,1.0f); @@ -7277,6 +7325,7 @@ FurnaceGUI::FurnaceGUI(): prevInsData(NULL), pendingLayoutImport(NULL), pendingLayoutImportLen(0), + pendingLayoutImportStep(0), curIns(0), curWave(0), curSample(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index fba435170..b9282b990 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1978,6 +1978,8 @@ class FurnaceGUI { unsigned char* pendingLayoutImport; size_t pendingLayoutImportLen; + int pendingLayoutImportStep; + FixedQueue pendingLayoutImportReopen; int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, newSongCategory, latchTarget; From 3f329e464dd423121b1d01be1a387a97b9f5697b Mon Sep 17 00:00:00 2001 From: psykose Date: Sat, 27 Jan 2024 08:48:25 +0000 Subject: [PATCH 079/127] GUI: fix overflow in timers these timer variables are used with SDL_GetPerformanceCounter, which returns a uint64_t. subtracting these can overflow, which is UB. --- src/gui/gui.cpp | 2 +- src/gui/gui.h | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 21e2e5164..60bddd006 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3411,7 +3411,7 @@ bool FurnaceGUI::detectOutOfBoundsWindow(SDL_Rect& failing) { } #define DECLARE_METRIC(_n) \ - int __perfM##_n; + uint64_t __perfM##_n; #define MEASURE_BEGIN(_n) \ __perfM##_n=SDL_GetPerformanceCounter(); diff --git a/src/gui/gui.h b/src/gui/gui.h index b9282b990..22c6a26bc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -27,6 +27,7 @@ #include "imgui_impl_sdl2.h" #include #include +#include #include #include #include @@ -2164,10 +2165,10 @@ class FurnaceGUI { ImVec2 orderScrollRealOrigin; ImVec2 dragMobileMenuOrigin; - int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; - int renderTimeBegin, renderTimeEnd, renderTimeDelta; - int drawTimeBegin, drawTimeEnd, drawTimeDelta; - int eventTimeBegin, eventTimeEnd, eventTimeDelta; + uint64_t layoutTimeBegin, layoutTimeEnd, layoutTimeDelta; + uint64_t renderTimeBegin, renderTimeEnd, renderTimeDelta; + uint64_t drawTimeBegin, drawTimeEnd, drawTimeDelta; + uint64_t eventTimeBegin, eventTimeEnd, eventTimeDelta; FurnaceGUIPerfMetric perfMetrics[64]; int perfMetricsLen; From ac1e09af84ffa746f5d5563989999837d1d7848c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 27 Jan 2024 18:01:28 -0500 Subject: [PATCH 080/127] update PowerNoise doc --- doc/4-instrument/powernoise.md | 45 +++++++++++++++++++++++++--------- doc/4-instrument/vrc6.md | 2 +- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/doc/4-instrument/powernoise.md b/doc/4-instrument/powernoise.md index d7b8fbb23..66f76a111 100644 --- a/doc/4-instrument/powernoise.md +++ b/doc/4-instrument/powernoise.md @@ -4,24 +4,47 @@ the PowerNoise instrument editor consists of two tabs. ## LFSR-based synthesis -this will be finished later... it's so late and I need to sleep now +PowerNoise employs LFSR-based synthesis for the noise channels, using linear-feedback shift registers for sound generation. + +a linear-feedback shift register is one method used for random number generation. +it works by shifting a sequence of binary numbers (bits), taking the last bit into the output. then one of the bits is either pushed back into the register, or combined with another, doing a XOR (exclusive or) operation and then being pushed back. + +think of it as a conveyor carrying glass bottles. each bottle may be empty or carrying water. +the bottle at the end is taken. if there's water, then the output is 1. if it's empty, the output is 0. +depending on the LFSR configuration: +- a bottle is pushed into the conveyor. it is either empty or filled with water depending on the bottle at a specific position in the conveyor (this is called a "tap"), or +- two bottles at specific positions ("taps") are looked at and combined as follows: + - if the bottles are identical, an empty bottle is pushed. + - if one bottle has water but the other is empty, a watee bottle is pushed. +the process is repeated indefinitely. + +PowerNoise uses either one or two taps for the LFSR, configurable via the Control macro. + +the LFSR must be initialized before it can produce sound. the Load LFSR macro allows you to do so. + +by default the LFSR is configured to produce square waves, by having a single tap in position 1 and an alternating LFSR pattern. ## Macros - **Volume**: volume sequence. - **Arpeggio**: pitch sequence. -- **Duty/Noise**: duty cycle and noise mode. - - pulse duty cycles: - - `0`: 12.5% - - `1`: 25% - - `2`: 50% - - `3`: 75% - - noise modes: - - `0`: long noise - - `1`: short noise +- **Panning (left)**: output level for left channel. +- **Panning (right)**: output level for right channel. - **Pitch**: fine pitch. -- **Phase Reset**: trigger restart of waveform. +- **Phase Reset**: trigger reloading the LFSR. +- **Control**: channel settings: + - **slope AM**: when enabled, this channel's output and the slope channel go through amplitude modulation. + - **tap B**: enables use of two taps for the LFSR. +- **Tap A Location**: sets the position of the first tap. +- **Tap B Location**: sets the position of the second tap. +- **Load LFSR**: allows you to load the LFSR with a specific pattern. ## PowerNoise tab this tab allows you to change the base octave - important when you have set a longer LFSR pattern. + +## PowerNoise (slope) instrument editor + +this channel has its own instrument type, as it does not use LFSR-based synthesis but instead generates saw waves. + +I will finish this section later... diff --git a/doc/4-instrument/vrc6.md b/doc/4-instrument/vrc6.md index 64691282d..330e5efb7 100644 --- a/doc/4-instrument/vrc6.md +++ b/doc/4-instrument/vrc6.md @@ -19,7 +19,7 @@ note that using samples on VRC6 is CPU expensive! ## VRC6 (saw) instrument editor -this channel has its own instrument type, a one-of-a-kind thing in Furnace that was decided as a compromise during a debate. +this channel has its own instrument type, a thing in Furnace that was decided as a compromise during a debate. the only differences from this instrument type compared to the regular one are: - the lack of a Sample tab. From b600be9b26e4af49cd7f70fbe7fab9f90260c749 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 28 Jan 2024 02:38:20 -0500 Subject: [PATCH 081/127] GUI: prepare to add "make me a drum kit" --- src/gui/doAction.cpp | 3 +++ src/gui/gui.h | 1 + src/gui/guiConst.cpp | 1 + src/gui/settings.cpp | 1 + 4 files changed, 6 insertions(+) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0d43a2eea..f2b3092ba 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -725,6 +725,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_DIR_VIEW: insListDir=!insListDir; break; + case GUI_ACTION_WAVE_LIST_ADD: { waveSizeList.clear(); @@ -945,6 +946,8 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_DIR_VIEW: sampleListDir=!sampleListDir; break; + case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: + break; case GUI_ACTION_SAMPLE_SELECT: if (curSample<0 || curSample>=(int)e->song.sample.size()) break; diff --git a/src/gui/gui.h b/src/gui/gui.h index 22c6a26bc..f24cb68ff 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -760,6 +760,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_LIST_PREVIEW, GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW, GUI_ACTION_SAMPLE_LIST_DIR_VIEW, + GUI_ACTION_SAMPLE_LIST_MAKE_MAP, GUI_ACTION_SAMPLE_LIST_MAX, GUI_ACTION_SAMPLE_MIN, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ff92b38f7..2629c7b49 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -730,6 +730,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("SAMPLE_LIST_PREVIEW", "Preview", 0), D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0), D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v), + D("SAMPLE_LIST_MAKE_MAP", "Make me a drum kit", 0), D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION), D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 584ce17ad..f9c15b987 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1966,6 +1966,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_MAKE_MAP); KEYBIND_CONFIG_END; ImGui::TreePop(); From b5ee0c22ef120233d08287113c3a86d2928cf697 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 28 Jan 2024 13:22:04 -0500 Subject: [PATCH 082/127] PowerNoise doc is now complete --- doc/4-instrument/powernoise.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/doc/4-instrument/powernoise.md b/doc/4-instrument/powernoise.md index 66f76a111..af921ebf6 100644 --- a/doc/4-instrument/powernoise.md +++ b/doc/4-instrument/powernoise.md @@ -24,7 +24,7 @@ the LFSR must be initialized before it can produce sound. the Load LFSR macro al by default the LFSR is configured to produce square waves, by having a single tap in position 1 and an alternating LFSR pattern. -## Macros +## Macros (noise) - **Volume**: volume sequence. - **Arpeggio**: pitch sequence. @@ -47,4 +47,32 @@ this tab allows you to change the base octave - important when you have set a lo this channel has its own instrument type, as it does not use LFSR-based synthesis but instead generates saw waves. -I will finish this section later... +it uses a custom algorithm which will be (roughly) described below. + +the slope channel uses two "portions" - each with length, offset, invert and clip parameters. +the channel alternates between these portions as it is cycled. + +on every cycle, the offset of the current portion is either added or subtracted into the accumulator (depending on the invert parameter), effectively behaving like a multiplier. +if the clip parameter is enabled, this will make sure the accumulator doesn't go past 0 or 127 (depending on the invert parameter, again). otherwise, the accumulator will be ANDed with 127. +once an amount of cycles set by the portion length parameter have elapsed, the channel switches into the other portion. + +the current value of the accumulator is output. + +## Macros (slope) + +- **Volume**: volume sequence. +- **Arpeggio**: pitch sequence. +- **Panning (left)**: output level for left channel. +- **Panning (right)**: output level for right channel. +- **Pitch**: fine pitch. +- **Control**: channel settings: + - **clip A**: sets clip parameter of first portion. + - **clip B**: sets clip parameter of second portion. + - **reset A**: resets the first portion. + - **reset B**: resets the second portion. + - **invert A**: sets invert parameter of first portion. + - **invert B**: sets invert parameter of second portion. +- **Portion A Length**: sets the duration of the first portion. +- **Portion B Length**: sets the duration of the second portion. +- **Portion A Offset**: sets the accumulator speed of the first portion. +- **Portion B Offset**: sets the accumulator speed of the second portion. From 4d777bf877dac561436c4e77864fc3764d0dae9e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 28 Jan 2024 15:03:19 -0500 Subject: [PATCH 083/127] you know what, new labels --- src/gui/doAction.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++- src/gui/insEdit.cpp | 4 +-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index f2b3092ba..4f358a4e8 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -947,6 +947,71 @@ void FurnaceGUI::doAction(int what) { sampleListDir=!sampleListDir; break; case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: + // determine instrument type + std::vector tempTypeList=e->getPossibleInsTypes(); + makeInsTypeList.clear(); + + for (DivInstrumentType& i: tempTypeList) { + if (i==DIV_INS_PCE || + i==DIV_INS_MSM6258 || + i==DIV_INS_MSM6295 || + i==DIV_INS_ADPCMA || + i==DIV_INS_ADPCMB || + i==DIV_INS_SEGAPCM || + i==DIV_INS_QSOUND || + i==DIV_INS_YMZ280B || + i==DIV_INS_RF5C68 || + i==DIV_INS_MULTIPCM || + i==DIV_INS_MIKEY || + i==DIV_INS_X1_010 || + i==DIV_INS_SWAN || + i==DIV_INS_AY || + i==DIV_INS_AY8930 || + i==DIV_INS_VRC6 || + i==DIV_INS_SU || + i==DIV_INS_SNES || + i==DIV_INS_ES5506 || + i==DIV_INS_K007232 || + i==DIV_INS_GA20 || + i==DIV_INS_K053260 || + i==DIV_INS_C140 || + i==DIV_INS_C219) { + makeInsTypeList.push_back(i); + } + } + + if (makeInsTypeList.size()>1) { + displayInsTypeList=true; + displayInsTypeListMakeInsSample=-2; + break; + } + + DivInstrumentType insType=DIV_INS_AMIGA; + if (!makeInsTypeList.empty()) { + insType=makeInsTypeList[0]; + } + + curIns=e->addInstrument(cursor.xCoarse); + if (curIns==-1) { + showError("too many instruments!"); + } else { + e->song.ins[curIns]->type=insType; + e->song.ins[curIns]->name="Drum Kit"; + e->song.ins[curIns]->amiga.useNoteMap=true; + if (insType!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; + + for (int i=0; i<120; i++) { + e->song.ins[curIns]->amiga.noteMap[i].freq=48; + e->song.ins[curIns]->amiga.noteMap[i].map=i; + e->song.ins[curIns]->amiga.noteMap[i].dpcmFreq=15; + } + + nextWindow=GUI_WINDOW_INS_EDIT; + MARK_MODIFIED; + wavePreviewInit=true; + updateFMPreview=true; + } + break; break; case GUI_ACTION_SAMPLE_SELECT: @@ -1460,7 +1525,8 @@ void FurnaceGUI::doAction(int what) { i==DIV_INS_K007232 || i==DIV_INS_GA20 || i==DIV_INS_K053260 || - i==DIV_INS_C140) { + i==DIV_INS_C140 || + i==DIV_INS_C219) { makeInsTypeList.push_back(i); } } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index ec1a8d9c7..f38dba6a1 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -329,11 +329,11 @@ const char* es5506FilterModes[4]={ }; const char* powerNoiseControlBits[3]={ - "tap B", "slope AM", NULL + "enable tap B", "AM with slope", NULL }; const char* powerNoiseSlopeControlBits[7]={ - "down B", "down A", "reset B", "reset A", "clip B", "clip A", NULL + "invert B", "invert A", "reset B", "reset A", "clip B", "clip A", NULL }; const char* panBits[5]={ From 311814f34acc9321d01af801c2855b753bb316b6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 28 Jan 2024 22:52:07 -0500 Subject: [PATCH 084/127] great --- src/gui/doAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 4f358a4e8..b493ceb2b 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -946,7 +946,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_DIR_VIEW: sampleListDir=!sampleListDir; break; - case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: + case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: { // determine instrument type std::vector tempTypeList=e->getPossibleInsTypes(); makeInsTypeList.clear(); @@ -1012,7 +1012,7 @@ void FurnaceGUI::doAction(int what) { updateFMPreview=true; } break; - break; + } case GUI_ACTION_SAMPLE_SELECT: if (curSample<0 || curSample>=(int)e->song.sample.size()) break; From 35b0543100caff8fd0b967dcfa0f4769dab3d479 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 28 Jan 2024 23:45:54 -0500 Subject: [PATCH 085/127] Future_Re-ference.fur by Yuzu --- demos/nes/Future_Re-ference.fur | Bin 0 -> 61548 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/nes/Future_Re-ference.fur diff --git a/demos/nes/Future_Re-ference.fur b/demos/nes/Future_Re-ference.fur new file mode 100644 index 0000000000000000000000000000000000000000..7f40ac1ed33e7d515b925b08310a5ee10ff7eb4a GIT binary patch literal 61548 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 z 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 Date: Mon, 29 Jan 2024 03:55:58 -0500 Subject: [PATCH 086/127] fix ADPCM-A global vol effect --- src/engine/sysDef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 082aa9f9d..65b4cf1f0 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -536,7 +536,7 @@ void DivEngine::registerSystems() { EffectHandlerMap fmOPNAPostEffectHandlerMap(fmOPNPostEffectHandlerMap); fmOPNAPostEffectHandlerMap.insert({ - {0x31, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}}, + {0x1f, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}}, }); EffectHandlerMap fmOPLLPostEffectHandlerMap={ From 5224c4666d600a8059213f638cd693912ad96604 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 29 Jan 2024 23:08:20 -0500 Subject: [PATCH 087/127] more work that I did not push --- src/gui/doAction.cpp | 33 ++++----------------------------- src/gui/gui.cpp | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index b493ceb2b..4f350c93d 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -980,37 +980,12 @@ void FurnaceGUI::doAction(int what) { } } - if (makeInsTypeList.size()>1) { - displayInsTypeList=true; - displayInsTypeListMakeInsSample=-2; - break; + if (makeInsTypeList.empty()) { + makeInsTypeList.push_back(DIV_INS_AMIGA); } - DivInstrumentType insType=DIV_INS_AMIGA; - if (!makeInsTypeList.empty()) { - insType=makeInsTypeList[0]; - } - - curIns=e->addInstrument(cursor.xCoarse); - if (curIns==-1) { - showError("too many instruments!"); - } else { - e->song.ins[curIns]->type=insType; - e->song.ins[curIns]->name="Drum Kit"; - e->song.ins[curIns]->amiga.useNoteMap=true; - if (insType!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; - - for (int i=0; i<120; i++) { - e->song.ins[curIns]->amiga.noteMap[i].freq=48; - e->song.ins[curIns]->amiga.noteMap[i].map=i; - e->song.ins[curIns]->amiga.noteMap[i].dpcmFreq=15; - } - - nextWindow=GUI_WINDOW_INS_EDIT; - MARK_MODIFIED; - wavePreviewInit=true; - updateFMPreview=true; - } + displayInsTypeList=true; + displayInsTypeListMakeInsSample=-2; break; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 60bddd006..ac0e190f1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5901,7 +5901,22 @@ bool FurnaceGUI::loop() { if (curIns==-1) { showError("too many instruments!"); } else { - if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) { + if (displayInsTypeListMakeInsSample==-2) { + e->song.ins[curIns]->type=i; + e->song.ins[curIns]->name="Drum Kit"; + e->song.ins[curIns]->amiga.useNoteMap=true; + if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; + + for (int j=0; j<120; j++) { + e->song.ins[curIns]->amiga.noteMap[i].freq=48; + e->song.ins[curIns]->amiga.noteMap[i].map=j; + e->song.ins[curIns]->amiga.noteMap[i].dpcmFreq=15; + } + + nextWindow=GUI_WINDOW_INS_EDIT; + wavePreviewInit=true; + updateFMPreview=true; + } else if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) { e->song.ins[curIns]->type=i; e->song.ins[curIns]->name=e->song.sample[displayInsTypeListMakeInsSample]->name; e->song.ins[curIns]->amiga.initSample=displayInsTypeListMakeInsSample; From 66bacef6810e47f48f866be2d0e0729ef7a6db13 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Jan 2024 02:14:14 -0500 Subject: [PATCH 088/127] GUI: implement "make me a drum kit" option --- src/gui/dataList.cpp | 3 +++ src/gui/gui.cpp | 44 ++++++++++++++++++++++++++++++++++++++++---- src/gui/gui.h | 2 ++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 6ae3fb8b7..9fb9f4042 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -254,6 +254,9 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { if (ImGui::MenuItem("make instrument")) { doAction(GUI_ACTION_SAMPLE_MAKE_INS); } + if (ImGui::MenuItem("make me a drum kit")) { + doAction(GUI_ACTION_SAMPLE_LIST_MAKE_MAP); + } if (ImGui::MenuItem("duplicate")) { doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ac0e190f1..3a0ec51be 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5893,6 +5893,26 @@ bool FurnaceGUI::loop() { if (ImGui::BeginPopup("InsTypeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) { char temp[1024]; + if (displayInsTypeListMakeInsSample==-2) { + ImGui::Text("Drum kit mode:"); + if (ImGui::RadioButton("Normal",!makeDrumkitMode)) { + makeDrumkitMode=false; + } + if (ImGui::RadioButton("12 samples per octave",makeDrumkitMode)) { + makeDrumkitMode=true; + } + + if (!makeDrumkitMode) { + ImGui::Text("Starting octave"); + ImGui::SameLine(); + if (ImGui::InputInt("##DKOctave",&makeDrumkitOctave,1,3)) { + if (makeDrumkitOctave<0) makeDrumkitOctave=0; + if (makeDrumkitOctave>9) makeDrumkitOctave=9; + } + } + + ImGui::Separator(); + } for (DivInstrumentType& i: makeInsTypeList) { strncpy(temp,insTypes[i][0],1023); if (ImGui::MenuItem(temp)) { @@ -5907,10 +5927,24 @@ bool FurnaceGUI::loop() { e->song.ins[curIns]->amiga.useNoteMap=true; if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; - for (int j=0; j<120; j++) { - e->song.ins[curIns]->amiga.noteMap[i].freq=48; - e->song.ins[curIns]->amiga.noteMap[i].map=j; - e->song.ins[curIns]->amiga.noteMap[i].dpcmFreq=15; + if (makeDrumkitMode) { + for (int j=0; j<120; j++) { + e->song.ins[curIns]->amiga.noteMap[j].freq=48; + e->song.ins[curIns]->amiga.noteMap[j].dpcmFreq=15; + e->song.ins[curIns]->amiga.noteMap[j].map=j%12; + if ((j%12)>=e->song.sampleLen) continue; + } + } else { + int index=-makeDrumkitOctave*12; + for (int j=0; j<120; j++) { + e->song.ins[curIns]->amiga.noteMap[j].freq=48; + e->song.ins[curIns]->amiga.noteMap[j].dpcmFreq=15; + if (index<0 || index>=e->song.sampleLen) { + index++; + continue; + } + e->song.ins[curIns]->amiga.noteMap[j].map=index++; + } } nextWindow=GUI_WINDOW_INS_EDIT; @@ -7267,6 +7301,7 @@ FurnaceGUI::FurnaceGUI(): killGraphics(false), safeMode(false), midiWakeUp(true), + makeDrumkitMode(false), audioEngineChanged(false), settingsChanged(false), debugFFT(false), @@ -7277,6 +7312,7 @@ FurnaceGUI::FurnaceGUI(): macroPointSize(16), waveEditStyle(0), displayInsTypeListMakeInsSample(-1), + makeDrumkitOctave(3), mobileEditPage(0), wheelCalmDown(0), shallDetectScale(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index f24cb68ff..badd269ed 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1477,6 +1477,7 @@ class FurnaceGUI { bool killGraphics; bool safeMode; bool midiWakeUp; + bool makeDrumkitMode; bool audioEngineChanged, settingsChanged, debugFFT; bool willExport[DIV_MAX_CHIPS]; int vgmExportVersion; @@ -1486,6 +1487,7 @@ class FurnaceGUI { int macroPointSize; int waveEditStyle; int displayInsTypeListMakeInsSample; + int makeDrumkitOctave; int mobileEditPage; int wheelCalmDown; int shallDetectScale; From c010b5f65904aa376e664a4dab1a27fc08a016c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Jan 2024 02:17:48 -0500 Subject: [PATCH 089/127] update doc --- doc/2-interface/asset-list.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/2-interface/asset-list.md b/doc/2-interface/asset-list.md index 2b5ffaaa9..3e2b4bed9 100644 --- a/doc/2-interface/asset-list.md +++ b/doc/2-interface/asset-list.md @@ -60,7 +60,22 @@ right-clicking the Save button brings up a menu with the following options: right-clicking a sample in the list brings up a menu: - **make instrument**: creates a new instrument which is set to use the selected sample. +- **make me a drum kit**: allows you to instantly create a drum kit using all the samples in the list. see the next section for more information. - **duplicate**: makes a copy of the selected sample. - **replace...**: opens a file dialog to choose a replacement sample. - **save**: opens a file dialog to choose where to save the sample. - **delete**: removes the sample. + +### make me a drum kit + +I have added this option to make it easier for you to create a drum kit. +it puts all the samples into a new instrument with sample map. + +after selecting this option, a list of parameters appears: + +- **Drum kit mode**: select how to arrange the samples in the sample map. + - **Normal**: put all samples from the starting octave onwards. + - **12 samples per octave**: map the first 12 samples to all octaves, DefleMask-style. +- **Starting octave**: change the octave where the first sample will be at. + +following that is a list of viable instrument types. click on one of them to proceed with drum kit creation! From f60b11215b7571527492b3f7b64b90c3de743c99 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Jan 2024 02:41:43 -0500 Subject: [PATCH 090/127] AY8930 demo song by traumatized --- demos/ay8930/powerful-sample-generator.fur | Bin 0 -> 24534 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/ay8930/powerful-sample-generator.fur diff --git a/demos/ay8930/powerful-sample-generator.fur b/demos/ay8930/powerful-sample-generator.fur new file mode 100644 index 0000000000000000000000000000000000000000..783e5f699a35f262ba5614859f097e79140497dc GIT binary patch literal 24534 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 F1CBDG^KAeC literal 0 HcmV?d00001 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index fd3595162..7ad3e7c70 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -156,6 +156,7 @@ const char* aboutLine[]={ "The Beesh-Spweesh!", "TheRealHedgehogSonic", "tildearrow", + "traumatized", "Uhrwerk Klockwerx", "Ultraprogramer", "UserSniper", From 19fdf2c7190d3897299bbd4939ee38f3c22c02eb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Jan 2024 02:44:08 -0500 Subject: [PATCH 091/127] new multichip demo song by Zenittikum --- demos/multichip/Young_Girl_A.fur | Bin 0 -> 6379 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/multichip/Young_Girl_A.fur diff --git a/demos/multichip/Young_Girl_A.fur b/demos/multichip/Young_Girl_A.fur new file mode 100644 index 0000000000000000000000000000000000000000..77f83edddf910babfe450dae0e35a3d5a6b0fc15 GIT binary patch literal 6379 zcmVDtob6m`a2&^VezP-sFLrT}1PGElAVh+cOcS6eiIOD~5HHXYNSFXcLt+T5 zz!eTHumE?sSqh|qCAehl1h!MYqOxc?PUS~ZIqZ~@N=l`&({TlU-!J3U2xCQix;L6BT0QSeg0x9x#vY9 z(#iQ8KXLSAO#m5HRfzf?o~H)VYVYK_-vB4ZH*ROJM&yhzlz`p~@qZm8zdEme?qGP~0;3DupK(>eI!$9mf z^aE+&-+^=qGv>I5PmhKMmgkhJfpU=JBYiH%JO80l(tG$1hm{Y$(OYET7*eSbje# zn%9!5z*4aVJ(?yqqgNBn6|DlRf_mEsumWKDxI$IY%BOi%@(T5pOoe(C9~=fAP4x)% zY6TW#MN0gS8!DR(KA$|4JU+n^;})Z5ma2L*uaEpbF0e*47I`n{H4mF=0tL(V^3NMTMCT8Zb%Kg{ z(X7MNLxS@$`WOwSFHVi>$Hp(D^lmzo&L%JDPhQd=Pfm>+#`x10CqpMMjE{~_B~pUj zKaSjF+DNe0f#Z7riTm!|f8c)oE`2aDnHWpxgT4DY@9ETco$Tw~UBtb&bD!Xz*tc6h zk~5N1=ac94Lt}}lsbosOyTn|4zNkBSH-EpGigm{61X_#1)RGElfEVxqejoq@fe;V| zB0vog1!91>=PHE5mcF#C03JXEG{6h^06!1_fWN1pCarXK1DjaB&Qafzne?cBfJ&r!x;dl}Kfhp{X?Hyf-m& zA#HJ3+a|ZR4!1VyK79PtL+mK{;2x!TRCu*N(70jarl#i2EnBv3i|ei1+wR!0vwhd@ z?o)mGuD~>$Ax^QP6qIgr+^JrKfry=amyjV9b#d!jyBiDUH;dOeIn&eRSMNTdo~-mdo5`gph9ho6pKG%j99 zO2zk)eqdWmwCe$)j##|D>v5ueJK_;8YjN+9|5#1&du+u~>Na#el}w!1)6@Dssecsu zL-EEg+!gmVZmv1@Bb*4e(oay1h;nuPr)>QJN?#aFOzHPX&C@OVdEb`WuCz*>(dR$d z6;tW%&)2mi2M)E>b=*^3hu7;}UB`XZb>K$6x{d?Y zbp!%|N;)2L>6lDRm^wBW>F_!8QQ44p+Dd{#$=>Q(RJF1xUAC4GYq8X{6shsMCN>zX zZViSsK2=?dD&oXSS`NC$G(NS8G4Pbi#uN<-YE~aq<;m%{^+es^}@!ATcXES?Wdk`eJo)>=T4#1Z`$Vzs$LO=(Xt3s&mr(* zzUrx{?-Q;eA3BxDjOo*vQK-xcm5-S!qct@((MTi~6`vaMiSQ>Jmex!Ac?3KL9)&p& zF%pc0KOT?A4nh&`Lwh&$H~b{xF(P z8X^1V`?)zgTD946-++##Q|Zk8GH@8x^nIIfLqPQ1Iae>!Xgu2WQ`{it;?eGns~xlh zuAt;(-+q01JTa9vZxN*L--w&pALFL8LRwvSzpXn+51;Dp@7Fs!r0SHBfp%w$IQBuDjuo+J9hNBRf! zlP8YYH`4n^-Z&4`4@J9vtk3~ZJT^3{(ET;C+}nLj1Xz*qGW{tjL82}!YZp2fliG!c znJ&b3A{^;Lkk|!F#3t=pUS9s(>p)PG4uA!A4u;CMzt^_Ch8`Iod0O9fB0ZkDwA+e@ z5Uj`RhxTHlH^n!->-Uj{nj0QL2%vc5;jU#73bqML{nBlJ^vcWM{qDEE76>jor;6sj z2UWHrV_UI_j!#Y}Mhv}Y+|c{`kL@{fOi!h!Mx{Ln!S~lSL{Hz3z#!HT9YVT491r$9 z4G*h9Ixg&q!X88ratnk)t91Ri?H9GBaiJX(DrQXBX~l$_WK6iPL3(@j9v-ZG?%mx9 zTN}i74amugMnVCf=BXGr^xG2@gjIS5&x(d^UQCA>ScW!@}>T6vo z>N|#ww=KRw=zDP67V7E1uxsN{@ief1Zgc3^&eilM>AbWPvCSDtP6w#l?3WTqSI*WeAKwv}5~m<}BQ z?8wtAPfCMNPRa(~8Ybl#dmOjgUD)id#&7PgnAl&MxxZH5V12d)4b*Y3-j|%3O2GY3 zB}S4%9rDic!!FXtcQn=>#bLI4uS}oZ*4WSsecKylTDC%uD%M2$=&|vs@sxeI9;A4K zSkzO&c>FY;9Xj@WgP&eITH^m1~)SHV;2YjOy4=7;7geN?8NB3`7Z)Fg;c=7SY9 z_=}6dx%XbV{7lX}=T`iSnW?AUM$EpK6R*=OZ09B&=GHQ6Hpry8+>CJ8D79y~Y)(_I z=cuE-Lx)eD?j0IPPukCy;hG_>i{B!Aqh6di57ZCUJu-u$cPP~Ls&J0DxEVCcn?WQL z4ETNGEg>zx{y+cy+E0J-;~y3G+1EXABE(NjxSf4po!zl4IRC{Q_<$Y>D0z@tiqAJ0 z+F@ppGEdBtsaV01@>Ob$%m-@oPIJ5J7MjkLh+5^@lJY#7&b^^tdGP$EgjCnyi!};K z#mbnRS=39rTO(KP{2OmJ$3s%AjLDJB=3LFnhVAO@s^DBR2U-B*nmN!v;DLI}2I{h+ zC_B3j?o}sy&B+!ja<5j<;I`(5o5_~{1zR3;`=_~-W$SfgrDa@=ZTgp0xaKYtR;;&fRi z&6-$WKF?`x#Z*|FSFiAh@{F8`EceSJY2uLt#7KH~&1r$U=k!Po-4ulD{ag?FIv4+z z$Bh&09kH{XK<y!>o2&6jZ`U0i{`qI_HA2@k4^5~{>3uA@@spe$8L)H5C))!pKp zS3F`=$CZ~A_)YV1G*ynvv=Uig-5b`_V-cmtRVX2AsZ)BXxmPf&k~K;3)IG)yfit}2 z>^8wa>n0yBE}Fb2M6QTbl4I@sR$altsh;+{qz-s)s)KUHws1wjsc0U2St;0rWm?QV zN-d}1syCH^o64EyLACxl<;uo}QFEUtR3kiX{F4m@@xStdb4V!mA;Dpe$0x#^5gF$E0&_g# zHwv>&+Tpx1>%MRUxKYfpnAv>sMM}1!BzLlvI}xrp*8J_cn?-aK)G^g#o7!Q9Uv#x) z*sCopD(4EyNI@Ac&I}g>9pvi?Zub#x$KLYLW8reF^Mn4)ZFGpC0kHt3+1_7LC_h#GTiP%+)jLZtYkKR_bTsegmdF4vMT+tj~5H=id^8{>fWA{?s?sb+q!oB>nO~&ou{?NbHlgBtvs7o_bT-ky-NK#^(wW*t2EW+Rqtnx_|{>@ zn-{8Ph%Y;1=kwOJvVf5{2W5}0D4$m{^5PkAGIrkaK@EPcCg6Jl!wkm<#qVRJ4?b=_ zYC6_0@MYOLEw2T>tYTVT3w&9{NqH?ODO`EJoEg--#WUzFn?dn&6*c9R4TW9ReM zwX%R|B=Kcu?7VF}@nvW1yfcGdF@t9@a{^BQgRU9$+A}z6KC~g`_U272n8;T^S)H|} z3w&9{8EZA1Id-Pg>rCfYMA;`pZ6%;^D0{Xn$ycpid?RfUKr0-cKVeaF<;(e0%o*f- zDOJw!;4X+W&L<;oMB;dWY7r@^$313lC3>q8KV!{34pn-GRT}kmLFvKLDo>h zX<)jE9Qj7c;Yn%Z)#0aXQLVi2ET)T-C;U!{MZd_RUibXfGu_BEp8uKg?XlJ~-N>{G z-yT^#(~V45ZwVZJmIJLV>K1ZU>%E}3bzk>#^2F9tT&7|yF>jS{0~IswVC#v3-jQr)P@6Bx1U z32C)<-21O_@0i5v+@cgCc8k1e#mX{cjm;Bc(@2Y`43=dRoEGbXMRkZKj#0Ylyu_V4P?wTW%5T%xk*!R+NImH z#}Ls?$Ya@~`MqNykQ9i2rcqF%(aEQnjxiPTQd+XTGq$wMrW}D`ZZYBL@J>6z(^iMx ziCdEOCLPhV$Yz|ZG&tF~Zs8G^6_c(RSc#u5o1c*?{E*|fA+6YN!)noq9>)6Ayb#lv zbAvhJ&D9GFSTDbpeXIP3&3RJzP8hzE_X}gZ-Z4Q^Aea(cch%_ zcuKg$QzDAxDg1&qBJQb+8^ZCt?i5Ad}5$wf>rtDlf<8PQZ`Opa`tarKy~(qmR|a^kl$U|a$t zXU^*9c)aqbOzzRPMGU=iKD|zU)|Jn>@|RZNXDjM)TQ*z7U&M}e!Hx=n3Tv{FrS#ezJI#0vw8;xuZF zyb!3HZ^xNrXbVDu&m1Ao=a0o1WzLwz8MS20DyP-FDIqqsB{5-fWSa?#GiJ$5P)VzK zQzA8Q)u%*x-jrvyVzK4O=9Q#uIb%=RyponRX<0ILq+C7ByN|$V$q^VWe&mYT@d^K^ zRQ!dWGp}(26L`(s&jwjLUY0W@Y(3f zp9|D!c_&<0!?jAQnTnST&$;8k?dn**G~%A>xhL$7AE>Fehhn*B)Os__n&ukZ3)>^P zwz)v99;uIC(<9pTLaUY)8%VoOt=c86H8P7khbZP-wVYP4MGAK9H66E~=9T*#u3lZ9Um&SQDO17}>LBqwKXSSG+j+u6Z9_mc{tz)`6%0d&@ zcgnmOYJ+c*9RzEv?J?5U!mZT(-`3*$5ZKyDxaGc{)lS)+KJ(JbuklC%$n@v5%zM z-zu;<88*lNCYe((!Cj}gYdA+!pq65jt%f{}vEEnm$~@B?b5iU9$X{muWy>d0d_Tys zqs&?EGQ%4ZwU=1KtffTmlpYc4)M?zOUvxQ+X9kd*AXfyLw{tnWc5;n!DM7L}@3Jj- z+2&=}EjMHHW(AqEb2+KG_QPZQtO>#Rjj(25;9UTjw;*@EUUUY z$F-AlYm-X}lC^o4ZMn-fFS~BJ8JjmN$ef+a*|lqxOWT?++16L$In4}Pq@3sa33t8M zy3K7D#Rg3Be;$k(I6oJ}l9z6BXNNaX@l#ZT{RIG@{tX`a(5WoM#bKPieg46H{uTGB z$-l3fN0$?te8UL4uZX{15;Vu0$rbo1Yn+#ve_0-BRrxcVpA~=OCTNa1EAi9Z>Js

;6>B9PZ&Q z3ntu`D6{zcgm|O)a{T^`B Date: Tue, 30 Jan 2024 02:47:17 -0500 Subject: [PATCH 092/127] Genesis demo song by Molkirill --- demos/genesis/Kobito_of_the_Shining_Needle.fur | Bin 0 -> 72889 bytes src/gui/about.cpp | 1 + 2 files changed, 1 insertion(+) create mode 100644 demos/genesis/Kobito_of_the_Shining_Needle.fur diff --git a/demos/genesis/Kobito_of_the_Shining_Needle.fur b/demos/genesis/Kobito_of_the_Shining_Needle.fur new file mode 100644 index 0000000000000000000000000000000000000000..ef37f2c193252386050621dc3bbe882b42a45e9f GIT binary patch literal 72889 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", From 7c3c505c0b658abd86e0380d866f7a33c09dbbbc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Jan 2024 14:02:37 -0500 Subject: [PATCH 093/127] CI: get rid of system lib compilation --- .github/workflows/build.yml | 51 ------------------------------------- 1 file changed, 51 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6aac414cd..e1328c322 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,57 +150,6 @@ jobs: libjack-jackd2-dev:armhf ls /usr/arm-linux-gnueabihf/lib - - name: Configure (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - export USE_WAE=ON - export CMAKE_EXTRA_ARGS=() - if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_GENERATOR_PLATFORM=${{ steps.windows-identify.outputs.msvc-target }}') - elif [ '${{ matrix.config.compiler }}' == 'mingw' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_TOOLCHAIN_FILE=scripts/Cross-MinGW-${{ steps.windows-identify.outputs.mingw-target }}.cmake') - else - # Test with system libs - CMAKE_EXTRA_ARGS+=( - '-DSYSTEM_FMT=OFF' - '-DSYSTEM_LIBSNDFILE=ON' - '-DSYSTEM_RTMIDI=ON' - '-DSYSTEM_ZLIB=ON' - '-DWITH_JACK=ON' - ) - # Too old on Ubuntu - if [ '${{ runner.os }}' == 'macOS' ]; then - CMAKE_EXTRA_ARGS+=('-DSYSTEM_SDL2=ON') - fi - fi - - cmake \ - -B ${PWD}/build \ - -DCMAKE_INSTALL_PREFIX=${PWD}/target \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DWARNINGS_ARE_ERRORS=${USE_WAE} \ - "${CMAKE_EXTRA_ARGS[@]}" - - - name: Build (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - cmake \ - --build ${PWD}/build \ - --config ${{ env.BUILD_TYPE }} \ - --parallel ${{ steps.build-cores.outputs.amount }} - - - name: Install (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - cmake \ - --install ${PWD}/build \ - --config ${{ env.BUILD_TYPE }} - - - name: Cleanup (System Libraries) - if: ${{ runner.os == 'Linux' && matrix.config.compiler != 'mingw' && matrix.config.arch == 'x86_64' }} - run: | - rm -rf build/ target/ - - name: Configure run: | export USE_WAE=ON From 26bf3c407ab109c69af0b850448dec890fc4cae0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 31 Jan 2024 01:47:38 -0500 Subject: [PATCH 094/127] Revert "new multichip demo song" - disqualify Content ID... This reverts commit 19fdf2c7190d3897299bbd4939ee38f3c22c02eb. --- demos/multichip/Young_Girl_A.fur | Bin 6379 -> 0 bytes src/gui/about.cpp | 1 - 2 files changed, 1 deletion(-) delete mode 100644 demos/multichip/Young_Girl_A.fur diff --git a/demos/multichip/Young_Girl_A.fur b/demos/multichip/Young_Girl_A.fur deleted file mode 100644 index 77f83edddf910babfe450dae0e35a3d5a6b0fc15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6379 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

;6>B9PZ&Q z3ntu`D6{zcgm|O)a{T^`B Date: Wed, 31 Jan 2024 15:07:48 -0500 Subject: [PATCH 095/127] prepare for issues #1554 and #1684 --- src/engine/chipUtils.h | 5 ++++- src/engine/defines.h | 1 + src/engine/dispatch.h | 3 +-- src/engine/platform/amiga.cpp | 10 +++++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/engine/chipUtils.h b/src/engine/chipUtils.h index d7cef0e28..84e2cf6b2 100644 --- a/src/engine/chipUtils.h +++ b/src/engine/chipUtils.h @@ -20,6 +20,7 @@ #ifndef _CHIP_UTILS_H #define _CHIP_UTILS_H +#include "defines.h" #include "macroInt.h" // custom clock limits @@ -29,7 +30,7 @@ // common shared channel struct template struct SharedChannel { int freq, baseFreq, baseNoteOverride, pitch, pitch2, arpOff; - int ins, note; + int ins, note, sampleNote, sampleNoteDelta; bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta; T vol, outVol; DivMacroInt std; @@ -71,6 +72,8 @@ template struct SharedChannel { arpOff(0), ins(-1), note(0), + sampleNote(DIV_NOTE_NULL), + sampleNoteDelta(0), active(false), insChanged(true), freqChanged(false), diff --git a/src/engine/defines.h b/src/engine/defines.h index 8d9a501db..2b150ed45 100644 --- a/src/engine/defines.h +++ b/src/engine/defines.h @@ -36,5 +36,6 @@ // dispatch #define DIV_MAX_OUTPUTS 16 +#define DIV_NOTE_NULL 0x7fffffff #endif diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index d736fcfe0..fd9697375 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -25,11 +25,10 @@ #include "../pch.h" #include "config.h" #include "chipUtils.h" +#include "defines.h" #define ONE_SEMITONE 2200 -#define DIV_NOTE_NULL 0x7fffffff - #define addWrite(a,v) regWrites.push_back(DivRegWrite(a,v)); // HOW TO ADD A NEW COMMAND: diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 95c9d49d5..cbf4449fc 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -383,7 +383,7 @@ void DivPlatformAmiga::tick(bool sysTick) { chan[i].handleArp(); } else if (chan[i].std.arp.had) { // TODO: why the off mult? this may be a bug! - chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val))); + chan[i].baseFreq=round(NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val))); chan[i].freqChanged=true; } if (chan[i].useWave && chan[i].std.wave.had) { @@ -580,7 +580,9 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; } chan[c.chan].useWave=false; } @@ -657,9 +659,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].updateWave=true; break; case DIV_CMD_NOTE_PORTA: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); - chan[c.chan].sample=ins->amiga.getSample(c.value2); - int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2)); + int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2+chan[c.chan].sampleNoteDelta)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -682,7 +682,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); + chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; From dca9262ec26c05fd22aa0ab838d39012e285611a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 31 Jan 2024 15:23:33 -0500 Subject: [PATCH 096/127] bullsh --- src/engine/platform/amiga.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index cbf4449fc..9555019bd 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -370,19 +370,9 @@ void DivPlatformAmiga::tick(bool sysTick) { chan[i].outVol=((chan[i].vol%65)*MIN(64,chan[i].std.vol.val))>>6; chan[i].writeVol=true; } - double off=1.0; - if (!chan[i].useWave && chan[i].sample>=0 && chan[i].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[i].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } if (NEW_ARP_STRAT) { chan[i].handleArp(); } else if (chan[i].std.arp.had) { - // TODO: why the off mult? this may be a bug! chan[i].baseFreq=round(NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val))); chan[i].freqChanged=true; } @@ -577,6 +567,8 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].updateWave=true; } } + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].sample=ins->amiga.getSample(c.value); From baecd648db9d97201e853a120ad6a6d6b76d4b5e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 31 Jan 2024 16:19:36 -0500 Subject: [PATCH 097/127] ESFM demo song contest qualificatiom --- demos/esfm/AAAA.fur | Bin 0 -> 2064 bytes demos/esfm/ALCATRAZ_ALWAYS.fur | Bin 0 -> 2977 bytes demos/esfm/CampingJourney.fur | Bin 0 -> 3599 bytes demos/esfm/Deadline.fur | Bin 0 -> 7118 bytes demos/esfm/Devil_Detective.fur | Bin 0 -> 2905 bytes demos/esfm/Drinkin_Coffee_Way_Too_Much.fur | Bin 0 -> 4102 bytes demos/esfm/EdgeRunning_2078.fur | Bin 0 -> 2360 bytes ...g_the_Dimensions_Big_Bad_Because_its_Fun.fur | Bin 0 -> 3961 bytes demos/esfm/I_Cant_Escape_Your_Memory.fur | Bin 0 -> 2951 bytes demos/esfm/Just_me_and_my_Brain.fur | Bin 0 -> 3591 bytes demos/esfm/Ken_Stage.fur | Bin 0 -> 4659 bytes demos/esfm/Magician_Lake_Cover.fur | Bin 0 -> 6948 bytes demos/esfm/Napalm-Loader.fur | Bin 0 -> 3011 bytes demos/esfm/Perilous059.fur | Bin 0 -> 2362 bytes demos/esfm/Poets_I.fur | Bin 0 -> 4613 bytes demos/esfm/Redial.fur | Bin 0 -> 1601 bytes demos/esfm/SearchPrincess.fur | Bin 0 -> 2481 bytes demos/esfm/Second_Start.fur | Bin 0 -> 7320 bytes demos/esfm/Strange_Sunset.fur | Bin 0 -> 5617 bytes demos/esfm/Tangent_to_a_Segue.fur | Bin 0 -> 3989 bytes demos/esfm/TheOneWayFuture.fur | Bin 0 -> 8045 bytes demos/esfm/Unconscious_Unravelling.fur | Bin 0 -> 11418 bytes demos/esfm/Unknown_Location.fur | Bin 0 -> 1731 bytes demos/esfm/WalkInThePark.fur | Bin 0 -> 2905 bytes demos/esfm/act_of_fighter.fur | Bin 0 -> 2148 bytes demos/esfm/cielos_esfumados.fur | Bin 0 -> 10697 bytes demos/esfm/esfm_ins.fur | Bin 0 -> 7393 bytes demos/esfm/experiment.fur | Bin 0 -> 4567 bytes demos/esfm/flashback.fur | Bin 0 -> 2043 bytes demos/esfm/frosty_dusk.fur | Bin 0 -> 7693 bytes demos/esfm/haunted_castle.fur | Bin 0 -> 8687 bytes demos/esfm/her_wishes.fur | Bin 0 -> 6294 bytes demos/esfm/ledstorm.fur | Bin 0 -> 1283 bytes demos/esfm/loneguitarist.fur | Bin 0 -> 4334 bytes demos/esfm/moon_closedspace.fur | Bin 0 -> 3325 bytes demos/esfm/sticker1.fur | Bin 0 -> 2741 bytes demos/esfm/synthy.fur | Bin 0 -> 2401 bytes demos/esfm/wack.fur | Bin 0 -> 4170 bytes demos/esfm/x_evil_soul.fur | Bin 0 -> 2462 bytes src/gui/about.cpp | 14 ++++++++++++++ 40 files changed, 14 insertions(+) create mode 100644 demos/esfm/AAAA.fur create mode 100644 demos/esfm/ALCATRAZ_ALWAYS.fur create mode 100644 demos/esfm/CampingJourney.fur create mode 100644 demos/esfm/Deadline.fur create mode 100644 demos/esfm/Devil_Detective.fur create mode 100644 demos/esfm/Drinkin_Coffee_Way_Too_Much.fur create mode 100644 demos/esfm/EdgeRunning_2078.fur create mode 100644 demos/esfm/Fighting_the_Dimensions_Big_Bad_Because_its_Fun.fur create mode 100644 demos/esfm/I_Cant_Escape_Your_Memory.fur create mode 100644 demos/esfm/Just_me_and_my_Brain.fur create mode 100644 demos/esfm/Ken_Stage.fur create mode 100644 demos/esfm/Magician_Lake_Cover.fur create mode 100644 demos/esfm/Napalm-Loader.fur create mode 100644 demos/esfm/Perilous059.fur create mode 100644 demos/esfm/Poets_I.fur create mode 100644 demos/esfm/Redial.fur create mode 100644 demos/esfm/SearchPrincess.fur create mode 100644 demos/esfm/Second_Start.fur create mode 100644 demos/esfm/Strange_Sunset.fur create mode 100644 demos/esfm/Tangent_to_a_Segue.fur create mode 100644 demos/esfm/TheOneWayFuture.fur create mode 100644 demos/esfm/Unconscious_Unravelling.fur create mode 100644 demos/esfm/Unknown_Location.fur create mode 100644 demos/esfm/WalkInThePark.fur create mode 100644 demos/esfm/act_of_fighter.fur create mode 100644 demos/esfm/cielos_esfumados.fur create mode 100644 demos/esfm/esfm_ins.fur create mode 100644 demos/esfm/experiment.fur create mode 100644 demos/esfm/flashback.fur create mode 100644 demos/esfm/frosty_dusk.fur create mode 100644 demos/esfm/haunted_castle.fur create mode 100644 demos/esfm/her_wishes.fur create mode 100644 demos/esfm/ledstorm.fur create mode 100644 demos/esfm/loneguitarist.fur create mode 100644 demos/esfm/moon_closedspace.fur create mode 100644 demos/esfm/sticker1.fur create mode 100644 demos/esfm/synthy.fur create mode 100644 demos/esfm/wack.fur create mode 100644 demos/esfm/x_evil_soul.fur diff --git a/demos/esfm/AAAA.fur b/demos/esfm/AAAA.fur new file mode 100644 index 0000000000000000000000000000000000000000..36f29a6136e8ef00178846549bd13014500a9719 GIT binary patch literal 2064 zcmV+r2=DiJoXwYQY+F|u$Dec0_4P|${F3wygl1@w+KCEvLaM~j>a;F!$Xagt%em)1&-p*+oaZ?=x4Q@Cri;l`COTD0&lNJe-yotWIoF|~ zfg>v(+%V3FPVY}p4_R9S(l!I$q1#`vJ6v}de&4UYVzjSN$Ye9=XkRKdH=Uf#6wc#O zek@OqC3B_eIXM0H02u}o?u^+389Qtd{Rnh>iHhI`_@a;KCE)iH<-rZG8@u^1NP}m= z3*d*~Cipuz+(9$~X22Ej3it(B0Uv@*okZKgBj5y>1qqJ2XBL4gFl0gZlZ1AOW+8Y0Q2An;K$&nU3UHTq=)YTu4NBiS@&Ye3Koykn+Gc(bh{qcdpT}?W;K4(u` zD%@OSzyKVWzymDc1wP;h0nh1x(R-S>xv;txHE_6^!gLwx}{ke@vt-JhJ9p@G32bPH~VuR9yti+u@o z?uf0r`lR|_dldRn&r#3MSpNWC5FFhHy!6onq}u)=5o4qg!=iXGIh|?fQ#okF-0gF! zWfZhp=riayXsv%Re=s@QUccPX_qCb-9rO?24@kP9Z~i1mF;RJopEqM$oWA*e)o-D{ z3f)z-UbJ4Nf1;u9wNq&a7~ZJ_N1h9^7`P+8Xs?)BgRh5C8GOT60WHi-V7BRLrLuF*{XK zb{;vMDehXI8v5^3vjudtP0jvNFpN`JJW z-$C)Yv1q&WywH@MD|eHg$6e_;8vp8^hWzmdG3UP_9TBv*Ab%O*UL%Wz0vvl}t&+jvw_89qk()vg{Yin&^GD{-Ps(f-dtXX`Ux&0ryotLeHXK<)@V6XGpzMi9Zg< zlW=^B7Wr{FoRE&v$tRugw}&mSIkCc8EPNzD`!XM`#~OjeC?pbSPr75_%YlER9%~e_ zhU>BFPJ_~^w~f=NgfM%8GVwGR2U(B@ll3-Rw~diAVPMmz7Bu6x(r1nNBsCdRv*SdcIss5udm`ryO&- zh9Q=cRi^PKQ%IWRe6#v|jd0sl8e%EwmfL2CrHm=lSd*!@l}RpzYfBOTnc)THIH_xJ zv6KmAnrJeGq-jZ+wE1$elyPMmZ!$%ky~#ES_Bh3nX)^V;GO49(CRe%fq`PNo%QVF@ zOG>1q>ziVkMP-`O^-Zp6gruWfSy5k^8I}dHqzXbeiACp?e7-4iPuu-4ds~_0YeD`W z)HO*Dt50&9>|r&(;u*7okt`>FPgLseUy%-6B-~tNg$g?Zp$eO=vI4QmMK(cfoLHJz zvcksX4Sp}HZk4578m2g2<~3nVR9H&NWUDNLV2k2D4<#8pRbfeKP01${Qsc1Hz&Z?b zLD&%YOhwGWeScP}pCvX!?6m7b)<>9%|$*q_WJG%Y0Hb1(Wz`Fb!tF)8Lz?Q#@8v z@mO~8Y?eI~&kdgOxr@iix+N-S8Q(31yDc78hZ zjV0=4$|o%!r*xdUvFdem2IVnZ7J=0~AbeKlyp6%B8%x&Bw@?-rz(rLW7gcFoRCRM3 zqbsjk-B@{X-6pSP-LD?s(t zGk4#)zrC*%AC=<9hKt)a8N@Gxi1+(B%e6rJgDWF@5jlslL}_pIy(_exG5K}o?cK1@ zP6(f!zB95X(wFd5dBR)a!(QUUOtlG5g&z^E*Xi5#g!a7k*x{CAW#9`kuyIcVyT>}I z_leXeT(#}fQtyj2&2i)JU7_vNPsv{IMury4XYRJ_!kL0!JK6qE@?`t(#mV-I^j>n+ zzF$kP|6ZPKzjm_ypTx-yXeT>xzfN}G|KVf@MCylKwH?q>AGnt%JD{EH!2LMc9pwHl u=wR+%r)KNaZ6SBBOY`i~Ji9epw`L1!wxDJUX||AN3v0Hpu>A+lb`2Ps-3*@q literal 0 HcmV?d00001 diff --git a/demos/esfm/ALCATRAZ_ALWAYS.fur b/demos/esfm/ALCATRAZ_ALWAYS.fur new file mode 100644 index 0000000000000000000000000000000000000000..08d61df241ae11c59c78f44a8c2a8ec82412a2d0 GIT binary patch literal 2977 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/CampingJourney.fur b/demos/esfm/CampingJourney.fur new file mode 100644 index 0000000000000000000000000000000000000000..0eb13e69a286197ad1fce647d8e4421b802d1834 GIT binary patch literal 3599 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 D1%sn= literal 0 HcmV?d00001 diff --git a/demos/esfm/Drinkin_Coffee_Way_Too_Much.fur b/demos/esfm/Drinkin_Coffee_Way_Too_Much.fur new file mode 100644 index 0000000000000000000000000000000000000000..9b946f1862857bcb906f62490c44e91335222dda GIT binary patch literal 4102 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 E@9u~o2LJ#7 literal 0 HcmV?d00001 diff --git a/demos/esfm/EdgeRunning_2078.fur b/demos/esfm/EdgeRunning_2078.fur new file mode 100644 index 0000000000000000000000000000000000000000..946d973534ff3850b75a8a622a43140a9f76c673 GIT binary patch literal 2360 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⁣nT_K&WXmyn#97#nndGcO=96=O=7WQ4gYg)qS3J?b5w19#f4l*8Sda5 zYU(t(pPMXlq^Z-7W1gsUplR~Nyh$Bs5c86&wr zF>h*gpvhPV4sqd%c~gXkazROaO*rOFQEtLfu2iclV*a#Vh{@jr>;lY*pIMK{8oMB46NQe%wphCmsYCq!s!z%`P61elU%)yh z{W^?V9mSY{brfYy$tqY!$*;o{I!eB#0@hKMHFm)|%6=U|p`&O!;PYd85kH<~9U<{+ zR8;(;IT=Ml26-?64ueA=2gbn|7zHC>7#svcU=SPt17JVc2lj$JU^mzm71M|VbPAPzR)Y=h?j&$4@kMkFCIp-8BQOWS??EO!*G*q+BJL zUNLTADCKpcpY^zF23gOrnracfBka^+RWmG|dxf2gyPb2KLKi+7N0l@)DI~61YZ@ps z%Du6&GUMDk$5K%@BCj-mJG;(WlcHx@_Vh0_XLFUrHsDjsF9uvcDyKZVuaqYKWvxHD ziK`yvDv>S4^Ikv2)JpvML242CGE`J;Ym$sDSwxFbM+@>3U@g;1G80n;YnkzD5jj(2 zL2qspu$GFFRAQ=NEfv2Oku`0L(>1Wl!gqlobDX(V=qc zwMm}r&F9&hhzig3lRd3xpZ~*QDysauUXmvw&)&Vr-tlSnK=dl)PBHsGU*=@}GHU6ep>4-Nt*G1-ZwsPsH*S+f?aXSCvk`^b*T`KU`K5sgE%ygXG To6qYnZMw3doi6_m_ryn~bK%;7 literal 0 HcmV?d00001 diff --git a/demos/esfm/I_Cant_Escape_Your_Memory.fur b/demos/esfm/I_Cant_Escape_Your_Memory.fur new file mode 100644 index 0000000000000000000000000000000000000000..66c5f3eaede0cef1498ba754b3a2198f23a812e0 GIT binary patch literal 2951 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 j&LHVY25KM5uU^|Jo2{cc;g_Hu`*ehaZ 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 NiK>fE{1?ph+hdna@%jJ& literal 0 HcmV?d00001 diff --git a/demos/esfm/Ken_Stage.fur b/demos/esfm/Ken_Stage.fur new file mode 100644 index 0000000000000000000000000000000000000000..b2ecc1d8d939a56cd9319e93d168de9d5b11bb85 GIT binary patch literal 4659 zcmV-363p#*oW)uTa8%cM{_j1z_uk#T`;b6b_Bz%&wlO#X8AS$TY*=AX%-CRvpmvFf z*g+Cf(St6F5i!BLpiR9Qt1)Rv;!J6p^p)wEdHj#_pZ`Dqx#wzK@5$^$=EzWLd~)#Q*wDHw zM3f@$w`))D<6pP%U>Mr)@UL!9(=B8rqTKd2@OM=Imu~QDK>q*!dvs_b)tAd09ZGFT zt?4^?d?<@zc4#P<>K#2gl0$LVuC?@wlgB1geIujeljA3|nbexcv!h2xCo*GeQ)g0p zCUco%qZ6q$J=tt#;^@%$&_r%6IREp*r0bfLp=qu(O^bzyz5v`DCi(>M4lorVirYk= z0oq{t7lF1I(FGtHCpr)O8t@$;oFIA(I0t+ipcbNSz%K#M0N(`u321L6`Y7-M@O{8u zLG%bP3Oon=4)6xBX(dqs_*+q z%sYr40-ge127U~zzLRJGcpi8Kco*otizov;5Bw?c7I4>UqK^TWfPV#UxtpjT_#*Iq zKwE?H0FMFVz*m9SfwgdtVc;vktAMx%b-)wA^T4-(p8{PUAQ}Wd2YeTJ4_N;}%pve4 z;Jd)T0Ivh@0@gaB2Z5)7KL)M>A6O6Bz$M^2z#Bk&C(#4I0iXo@4G_7Ps24Z~{2A~= zAi06)BfuAd*MRk1M5lqj1l|BPeh6jY%fJtSNH;xu(i@=`)?*NI- zM4N$wz;-+6qhp&jFW#9{}?}#}?QS7y>>E{0VRkh(3t%0*8Q)1D^+8 z2HphP9)dptBftgVTfkod+E(}*kl(7JUl&?H7e>&uLg7frj>h7NXiF<;x(EafQ$jY_ zQ7)mesTRd6qDs*iw*&$~z#&qlhAm7VvQQn1+VMn7>x#t6HXn_ngDTlkm&OVjsEvb0 zlB{fLTP0gUfk0Ry7&P#QClj#hx9FA?ln9#!{_qq60^97{E%Od@po^kr^?CVSI?AtIkHI+Is zlpP&9ky^8}uXp!aauffK(Gy1|vx6yFn|gS1A~$p*=g{W2xzgV64vV>eZ(aj*KmY+C z2pE6~SU?B}0};RmqCgCY14MJ6p9B3I=;uH`2l_eC&w+jp^mCw}1N|K6=RiLP`Z>_g zfnL(O$`}iN^pBX0Yk-an+OuQVM^>`R_(iGgkkYedTfpJsf8MpHuj?G{S7;OM=~+dv zO>Q!q8yZY~_~d9VlTAG~IdPPFci%}@O~iD*Q%~>{e3hoWQtnKVYHonPxjkX0|BY}Gd?mPELO-`3Jxv5%{ zz7Z6wRnc+3TA_-bz9v<%{i;+ca0-G4subuiD-fo>vCQ$*_RNVBN+vA!(VOGJ^fyTy z_-fotV>zeK$Bi`7s2+@q^iPo17RV5Cg)`}AfZl<-k}=_zkvWyxp7qPvm%m5qFZKo2 zX!5Q(Wz8w??ulVs0injV`SxnlBKNlssC_|dK*j@0{X|)+L63}%WO8MR-`f^{GyM)3 zv@ITymW*GC$I@>n3~a$nS>D}qyR>A)vE(-tN=axAqYdvaVUayO;+Qk>Fn5$z2_Ft*xxS-ys;4aLLKyt{ikr6LJP z6;Nf{Jeqv#j}Y_p3PJ>1aNEH`wKU<*xP)+L{pqq=0op$~?)q5j35sj54DFYSye$I@ zip*kCb(H@{Dzs55)aF>G{M-8A{r7csuD#>d6%j3_Y-AY$oqqnapT7Iw*M9t8Z&&EG zhziH6CTAcB(O1d9E`t&2UVdOlhdJm(>FQK`#j`f_~c004g+lv_F-%R z3HQ&zLkMy3ap~T$&s)+byX#GiTm>*Om=L-;KYu0W#H3cG*Cx!$9jJQ7nIWau;j&&a z+BI<^2baMMMQX#nWg{K92YO+i_O-H6Nc&n`PrtY6|S`SwS*sk~`S6wU`IWaX~J|r?60V3qAhSPbOc@5 zjn<k2pMovWn1&8uJri3%fMs2nza*^hVA8?XEnlFL~x7nN}N$kZQV>NqP~ z=l7!)sqooIyHwZpJlC&Wxr!arPAS@(nYOj8E+%AnM*3NMU?X=(I?+WP2lQ=L;ze;l zFNh2mb$4D*^{b1D35|#>Pw4E8rP|tu- z#u-W@V93dgE(YbG-N|~GIR$ypMIQ7Lk?aRYX0=tl7%bgP$tS_WT@fB)F6MRGXja+h zE@on>QAsyBq+jqJi>}_S-1%F_Mdkr)q5+UKGq|Ei+26-%Fpq#Oh{rUJCkkcZ8X; zdncV-F2jC{rPU03y;j)k%N6$ea)rIVY+q`lHeYwJ3U#_s%mn-b`C4_y8vk@orG;MxKXdylf^0{Yagy7Ya0SoWWuFY= z2Id7kYnOd8%o~^!@T^^C87Y=wv5ZT28CZnI=bNDl-&A#Bw!!5Jog@FJ^Q+|IDo&N< zKFM`au4B2Un5O=9ii*wpZG!p7cwD>&pC)e=l*h#8xY&YC{SLh06(b`e!0&=WUXKYk zz*lo;8Q{mnG+IUlFA}3d5?+w6XrltJO2-8E{vh|3p+7Eo0Uj4DxyH>$Bg1%7(M+o5 z8aMCNTz(}nhsDN{7*vhJqQDqbIzFjtpDd4sPa*;cGBYEoj;QtwC?t+3sspO_fGQmk zUV0B0^(prYlCxO*#;n#cD;XgWA@&KDDEh@k-!oFBVEx4t!dtNA_Zc}hZjc=gi5Gm& zNG<&A8)~S&fie}taP(86WQ1e{8?UV20Y|i_pN~L>Pahik9D)H5c+X0=enqf>32Yur z*_MM!E!UP#?Gd-eHz)**Ip~{(nv}lzQ!XzFFJxIdtZT_Cc`W) z`ksrf54*F=9vGC-aolN;9Yzry-ZNX(odz|~5HC};tW!A(e{clDJWwzGrFt1hvZtPp zKn8~lf&qu%7zn&)rP_#JHM_1U7gddeBN%lfez-E?hZj<+lF#2`YEY{Ff)iN2_;SSX zE?*u^YqRG&#znW4sFgvy&AEZ_n68&Uys0-38+n<28Yd7Rk&r}k?iywg&&CF#0}7Sm zxkz^tXVTv3s(}E>mrvV@Cg__a=c8f6s333pc-T8dG)y_%v%Yh@rq88~&4483gzW(Q{xn%)~o6A zwPKC(Rt%HBVU;($jg}}cd7DBK7Mob1n5L6y;@(Tqrn5!R!r?WniI zGHt0^Gi3hkX+~Ys)!B@4cm9-WQTbCUe-gIfw7!yFQ4&sx{mz09eM+Ucb@fx6m)$Hl z&^hNjYSSssOAnS&P2MQWcvE4)sbo5FKf^Z6aGv8^a{D(GDK$S_PKZ{hj>I}?%}Xd2 z9PX11Q<|^8+zeKIeU%w4nX^TglheEeWX1UtYr**%T9MKCU)e1ANwQ8xhl(Ht|owb^Mg zhFKXi7k~1?BqTE)s*_`;m1v4A&1AY%Y7Em`1yi0mE!t8B^XX+i;(Gazf~ysOBngFT za%%|hSSKV;cHMzrH>;h@Dtu$C+|i|&Sx}9x1YE4JK;9Lq=Ek^gE4cFbzav6s1LaHg z#;;VilqxeWpYpY=GQY6FOjOtN7Wkkd(w0WV#tAQzx^t7duNc-ZoGNVd_8#@wI(3C%Xk+HxiTkAiH$-m_yW*llSu zVk+o@yR70a8fV&IRV#0+qPl7=0R^DA0A9w9ipriPr$@={#RnKsX;ZJECQCjsj0USZ zyZ3^Rm4IPM?c8WCDtg9w_V$`lYf-U2PNA|Gzv3moKJBt&OqqW%>$2@* zb!{JO+V-)gY#&>S?PE)|eXO4CW6jz=*2wm;W^EsrK{{qxiI>c&a5sN63_J0{`o=kt zV(pOCzDAZ3*SWF8)z4L3y|>u7sL^rScl9PJF5gA#G37mXJXqNap^jz1)3f@@v~i|2 zSfPsx2V~aJ^I5NB2Y0lbj$_sfz77kmC%>wSDJ;p0Hfr3sxM>qQH#RnHLe%KExM>qj znKn^n+Nq{Zn_v&GGHt@Fq31%=CM>sOEz|NzM+=otI{3h?1=cjP_CxZ;81G$KyC`36 zaWC+Fl3$AP{;WP`*xrtyMOu2&P9A4E`LgSDI#woe-!}2ytX_wY+XcV5T7)!L*5>!N p9HgDJ<$pi3D*WD-<8o2aVK-rW>9Zxk;!BY8wRXJC{xAQTXtL|j___c9 literal 0 HcmV?d00001 diff --git a/demos/esfm/Magician_Lake_Cover.fur b/demos/esfm/Magician_Lake_Cover.fur new file mode 100644 index 0000000000000000000000000000000000000000..7e8c00d3b768c982a2aee5afbc24fd41bb7c9236 GIT binary patch literal 6948 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Napalm-Loader.fur b/demos/esfm/Napalm-Loader.fur new file mode 100644 index 0000000000000000000000000000000000000000..56501f1446abd334fff100cc9c5123c0c0766274 GIT binary patch literal 3011 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 CcHrp% literal 0 HcmV?d00001 diff --git a/demos/esfm/Perilous059.fur b/demos/esfm/Perilous059.fur new file mode 100644 index 0000000000000000000000000000000000000000..abe825a8c988ba1c0e65e08c7921865db906a1c0 GIT binary patch literal 2362 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Poets_I.fur b/demos/esfm/Poets_I.fur new file mode 100644 index 0000000000000000000000000000000000000000..7b24ee6caec1b5344b1b59834346101452f4c4cf GIT binary patch literal 4613 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Redial.fur b/demos/esfm/Redial.fur new file mode 100644 index 0000000000000000000000000000000000000000..477af17f5b639e36f84559ba6e390dfc034eb600 GIT binary patch literal 1601 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Second_Start.fur b/demos/esfm/Second_Start.fur new file mode 100644 index 0000000000000000000000000000000000000000..3473e63dd184dd87781cf82917cb5cbf0552b1e7 GIT binary patch literal 7320 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Strange_Sunset.fur b/demos/esfm/Strange_Sunset.fur new file mode 100644 index 0000000000000000000000000000000000000000..19328b6120f1c28977d8441d9b5d6dba45d32877 GIT binary patch literal 5617 zcmVX|GrJTHrIWRgA>3=HnK4RTgD2uu`F9F zX}wxKR?@Ck!cTT3%SHx690GZ?PTHn3?W8kF+A7VolL<+udHfa9%(T<#WKvBFZDtCS z&NPoQX{`Rf@7$NPdN_$m2n)Y^?sv zbq3Kzz*t2z4O|8e$BDiH++IucdEh3KXbN}@=(dQy1>CWk=udzdpstRn6L=Z;HgFlJ zy@u#f;I9C>mgrUB`87lzN)Y`u@J-;qfN`wj4}nef7`K+_JHQ{T1HB~Co!1ln6!^0n zhyphfbpc-lPBwr}im3i3XbpVsJw%1~5+&b9GzR<=@E^cWflD_NJ@k7-Zve4dh;9Kk z13Q4%fyrBmz65*^_z@7l4SEBA3;Z+i6X2TLiS7oz2K)f{Ik5KqME3&U1YQHQJBS*9 z2Y~MYZvZvxiPi(31{hO zoGL28(EO^!btRSbYE>V->a$4D=hKA@J(}z8r=981BDEAsgT=r5OWKw$7P-Iuq28fF z&jymBQ573iv8ltR(cZFwS{kWkBekGyTbs0XZ=mi*>fT7*?l^qPP2^+_{y*p>k$^P6 zOdliqaBn)_Urdb--4|QmpU$QSbGdwMeNW%$wp|Be>$eT0k7rW*Gnt-z?y1<(p+ai3 zHhs-Fw@7Iq`Xqlj2xl`tFk^Jg)C7V~E@e-`s+F`wt!A2?qi5cYinRV|tx zp8qkBoi_kmfUPZ$?7dNx!sj~Mfs}Ktir1Rf>bh&LU6bHZL7Suh{gK^mjlC#eq0O{= zYe;#|j$QAgA7jMhWE#zzDTuPB`7DLBvKemv39~}^Q=(m4;}iwMxj8mznC2d9fk3Iy zp3v8{?K_a=;iUhfhv@c(5d9npxMBNl=J#2bU)c6phv5?xGn&6l;k{(WnqQ`HnHgoc zTor2mI0bfXwU}WD47ZZ@zh>z2<+;{P8_^cLUI`JJ_X+#|3)ck$b877aRibJTj~dLn z{suI|VaiI>CtOi=YTfC|*>;UdY>2%<$afFZGo#HvV|`Ia#$7->phv9BOc?fHM!!(R z_xtQFG0NmeZ=uxv&zX^y50n0H6R~JZ3sJgk2DTw7zgVuJmT(n$CQCY+z~UDT#%xfL zKjD&((zXKV=kl%sdtQm?&FdidCt`Z@Mv71w3Q4d;sTo^->xNY@s-Y>P(p+mJuQbU@ zKY*6SR+@BkR?q+_5kBRL5TmyAcuK@jLZGKHU~0{C6xu*2e?q#Hb6&Y?Yb~#=u2%bd z|HER`p(PekVnxE?P!LPmx@oc2g%ZNch{I^ zGtMxLEK@7#o31r=$fltlLK$An4%bEKEpX6Lh833{mrEVB`5hbZ_lM>N9kt?FfO2+ESXP`nB*FN^l43xlaW>7LZBgXzA_ zx4Bx~i{mhB@d3Nsq{`qucDZXSFTbWK%l~jOTLN=-xdP{0-pASD_(8jLINrM`=hqhD z>~ZxtkGY($UC23=%a6I-H_g}gSL7a8zDM zVtT-rL?1#}^?EMbq+E8Xa?#NBLD4_r(vQ=_nS7?0E2i8eg%px%3ce8EISvK2R&6Iv zj~lILV>@v=-ax2dSIx$x`5vp-bhvB`Y8xqGGib}@Gv0!|7gl;&!jG**jhuLYOyS0L zY5}Wk74cl-8dsN-E|Xf?+C5Y%j^?_3%G^_*Dz15TRdXlN$m&;l$3q@DuXD(S*1j5i z_-DM+)pPy}suc6|sZ+-zc5Vz;r^1k%jj2I*Wa#%f$*v7YhSRzHkYbNvy>;fp?|&cN z(7n#w(S#Fyy4Kve11EWGxWVnWFnjh(vROV*%`zMb>5;dBVEcbyJEvG4@@3yWnV$4O z3P-Ay+$7XQW(1}URPA9t7x8?5-X4}281MpJGlRTh51L4n|0*jOY{oUc(UA74#OVfg z$WMNJNiT<(oQPHV0?H;N=r7=+LQtcTO_t(YD@JE2~$PeX;$FAh4@k=ln_C zeW^@$ujhi9_g-xm+_Uw=yQ6Z*WLakBxV}#vd5I<>ovWLsuHHka?-1&6pb5Qm_0+G3 zir%RSwL9&*0!0R{X+n>7VHr$1vu=0J^=GeihT;?WCHaVDHP5p5NFA2_s@_J^TH8ew z6KmVv43sfrBH5|oEz+qqRorSN*+y>$WOApiDPd9tm|TFVMcOJ$jIC&Gi!}?M?ZQVB zK7QKMzhqsAeM`&+3q&pi)>rfk0kUz`-`{QqqRy8^o#5}1I+4n?_fG{??9JFtOp)i; zG*zzOylSp;_6}aESy|BD^V(g&XBYD zI&pVI^T!aOUa@0islLhzbM3fYs+&uZB`30YF-#EG<1M0Jm3BANwaAviao%n=P3u|n zqJGGn2B!3*X0q4R*PN*jgepe68Bo3Z(YqhLX*1bv_m-o|kbf_!qZ9RGzz+ZC5Ri%kwdxMt)5XROx%jCwrGqr_RM~=+g$bV4KpFugvn= z_}6ETulGYfng@Nn6y?3X_x-+>C#u>X_4V#|wfTx@6LGa^pSS9ZugyN6jEY`cmAwwx z&oi#AAFuMo4qxw~s%Q5S-h9ZGA=z%~3EzrDT^*G4S$Q6nXQoQ4_9{+CeY1Lfy?wrx ztdE+1d^O*$D#{sOOLgSzb64bRqD{?$6`(z!KlLea ztLVE?KNvp;B-`V9=)6r|?h_&GJPoa8)9c5kv2Yi$D$~FeydinW-FsSj@8RA@(fcTR zpD>gA7u$P6@1ngY^sd@_Lhq`*C-g4ZdqVH3y(jd$zxVu+{q+hpWFz-QEip|*2xTLT zc@0wZM$Y)#Sf4&s@d~|1CibX?o(L^$a0!`}T+)q`&RGg9a8xawaYE=P)WHzmjF_|ka1E_n(sXY^v-{FdkR(YoZg zw4A6*o|Z8STb`?$wHTeDx@19G#_E#CWK2~{{p)2B^V1?`*cS7Os9%YcrMl!((sHsc z`Lv8#*m5OwTq(t&`KKTaU2te^oN_0yz$LU^(%U1KoZdCk%RYHH;&mR3cn%ct94aDpIMijSeq5^T zPw=H%-$}kC;=L^5u^+v|e8~H zyDzj6en5~Ap1a^VsrQHAa}~WWRNbkb6%4WGPcAVa8}fSCopbZ+K2_M8J3{d0_RuB$ z$q<)5RoDt0A+PhvkTGc7+ax>taBf6=H0Ku8`Mx z*k=WUtuVq?C_u>(V9;j;gRQ`g+*y=TI?qcdb1f}vg)wD?lBbwnT%cspvw{pu7DEdS zHL}1C#ii`PKNgH+*bY5VlU45V*}-5tWI|qNT6JE}ovUC6Y_-(T7hE5;sv;vVV~|J7 zkxb%#N@f%K9IBik?l7oZJ!>0Ix4O7OHeU|sd`YOJP3|QKXHgiGmOYj#F{G5>I%OG9hj^kW_{8Q59JA=Ifv86{Ic zozTlH7T3#YujnV#l_4X%_{wmK#XQb~AY($d(PDyi&Ea_wO(N!4Li8^PrxKpeIjWb` zoa1Oa!yLGBECFW#MisLuVTNQodHqasIys1@3ft`|k9Jw~JIC@s zisWeGO)WWwag-eA*JF6K0tEF~Be6m`p-*7BP9@$5u-XvH`=jbA#B`a}v$fY|&Wdhj zd7fc1%xowz#b(Ki@*=6+7P@WqSj|&F8JJMi%R=2?RZB{XX-pR_^1?+(R@eiW+cCZt zl=X2-*;vwo8gN`$*R_CMvsVF)qCGbj5@&c(#?;7ylA|PYka&4LE+5F^cl)xi3on09 zF6dg2x94{)nX!fW9}9Eji~8v5iX6O4)rX~0)p6uXjv7~TtXY|kd!$v82Ifh6`)^Lt zg41%$8B^8bNGfS7?xd|e(w0`WmDR}@q^%ofwnSHB=J%0aq~|8-iSV%&S;dm9Vo6r9 zB&%2zd+9dIK;(tbI|*vcg3o_kKgGpV@|5+arb67BA@B+zA=2j4l6;MMK8j?5pEq-X=e3Z{bcfEwR0OH^8_Z zGH}8>-bvwdZr(jL9&n|+^PLnb=jMeCLF#7rECA@s$&s%uAKX@!C`+IaaIRS4p0#)jw28 z9>@EX*MjE>X&IOI(~GviQEFGBa!guAt6F%Bh_+Ki3-dwR+Cpoy>YS@(oL8K3aX`(g zCF`|t%H`LWRLX|~*bf|W)iP}rdUnWUWF0dS5gBp&LA9nl-Ag31NUh`zQj2PnGsI#iNHL<_aq2`c%w`>=oh7QKubNQu_n%gyr~O zrx5436KhOkNz}K*!lajj?u`F5Syq8XdXAaBqzs=Nvcqgp!fePtZpiY!|KcAX+k6j7 zUE_c15PTuhWo)2ir=j0Y$u2zQ3qQl(GF=}Wn>MO#x##vH(4JXpEUZ6U~<6FW6z*AZRCyMRB~A8 zi^kfLzV0LFykHcKpl^+l`OoXaMsft>o{>3cF{*3~7(s9kl#PrL5YZ;kJP{QxHqV?9 ztms(-mmgyqs=>_0l-^^UN2hl&H2D{q@=gDj0@0wJHwfJ5YH-X0!{9h#uznSX>nEA3 z<>Zf#5c0>(zy187T13Ep5fE9Vq0ibPTTg|LcpPMKKyt#gANfN<>qWT%%cdq_Essy( zG`PYwS>23<~Uzg=j`K;RWCZQa*TSvV?=b$NfWJ~VIs;A^-jx)|S=wzrqfs;i3 zP#EzazmRv6SxcW|QN8yG-+NglV^YNwM>2M0I|Z^_I7oP#E|2Hz`iz*kU?oQ_zCv4a zh33~|mVJd5yNG$p7Ea^1LR)f$wvb?u_q;#sSTW8M7lpXwbl(0|8f8U?RJ!X`=`O3R zgMA4oO|(h~unx@72tDQ(Bjo_+W54^Xn%*<)27=lUG0SJ!7XjU)TlN zFm~cAzm19SCE;sd5(jEzQ literal 0 HcmV?d00001 diff --git a/demos/esfm/Tangent_to_a_Segue.fur b/demos/esfm/Tangent_to_a_Segue.fur new file mode 100644 index 0000000000000000000000000000000000000000..6ca6e7ac3c159a2672177567843884d03daafa19 GIT binary patch literal 3989 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/TheOneWayFuture.fur b/demos/esfm/TheOneWayFuture.fur new file mode 100644 index 0000000000000000000000000000000000000000..dc4150c3ba0659e70a4a7b3d42ed8869d5211550 GIT binary patch literal 8045 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/Unknown_Location.fur b/demos/esfm/Unknown_Location.fur new file mode 100644 index 0000000000000000000000000000000000000000..58d3b9a34191673a5c95331f3e094141229631a0 GIT binary patch literal 1731 zcmV;!20ZzAoW+=5Y!pQt$A7zfcYEDld!Rg233N0@3(}qtjXoH9S7{9iv<*ekaPfN9 zg*({Zmfp6sdWMrAiqV)DAu%yTjnTx2l1NN68hwFR|2_Di4`=j2V|+1+F$U`QH?zC9 z#}!D7(J7zKZ+*N9JZL#VK1%yN}E{cK;_tB&b=oj*g7IWkN_KG@_ZI zN$R9nDk=LmgD>dHC3}@}74!eS+*jFOaSv9+Hg~G%mE8*Mu;=I9A=mNH`RzTV>lzQ! zw7@{qH0(|Zd=55u5WNp>u!x=oRwvOccoLidZ-S4(m*59*9wbvlYd{9%zyv6PN5Qk; zBzO;;1z&@oz(ug4i|9shJJfsyy4PpGO7UX-s|AS?fcn#dRO2+4JZXUY{TGBuu6DA)4r z-?AQb=R~PEGhXHD)CVcJ-TW^WTy&kAbOJx&<3RL_wr7RRQz6XS6^m61HquWSr!JDw;1oz* zFj(B8^2J%aB}^dKDzj226CW5Lsp9THZpMS~}!9v1Qr@~wEbQSX95d$04 zA@k?$&sQdMcw#~fPi$ORSP)e{KvmHCpHyOe?oWCXs(OyDU}^Z z^f~q(Zyk+nzlnb1Q~z?}aCQp`tsc{|!)U*Lvz}eGf@FIq(Y7rJEaKzwPqpEZl|*;~ zrg}}fU}*I78q~w19A+{M(;bAFE$=P;OL5kQarT69R?#E-yrJz6i0WLW>hAHx!^LVf zj5dBeZDcd3UrAFoflFOSjjek^)%r5gE*j~^t%&$nuTC))Z6>lRySit(f0H|NaBkLF z4CBC`k$sxa73!x@52n-ErDi?0F@;2RYD-Jx_y!6~dq*&%#?ywowAy#~7$Pv^4VFSnt8Ug$Od#0!#iER)(pO}W!E zWyh8~xxYNMUxh->PCxm4-`KnOj5v}uayU_^pBpn}yS`1!4K5Q3)8i2Qwph3Vbh#K}u^6p66Ai&9Oi5A@;&&_wM5Mk+HYT8`I16Kzk!{XA_<53M1OrnDJ^=sEk50oTGPtXFSX)dtM|27b+Xmoh*f9Qir;jUVyj7=lRBk0SCNwO z^izhdlq;o@Z`58W9sM}!NQdE%j&#U)saPbQ)UfyK&iIq;ml_V`7)L?faE)WAaT(FB zqJ03e3)zM27}98?G}7CQlzat46^*CLgDP^cinf6E0^0MC4?sQud7shTM}B<7&#Z4g zO1d#;b9Ze0DdVA7M2kt853zPK)|@JDvoRSz29AQdFCSY*$8vM4`0qY*GI5*K$p1Fr Z&DPGyc8_dh8nL@zX&#TT^f!+uBfjkkTDJfI literal 0 HcmV?d00001 diff --git a/demos/esfm/WalkInThePark.fur b/demos/esfm/WalkInThePark.fur new file mode 100644 index 0000000000000000000000000000000000000000..a593e1988a6f77f9576fc6d885acd600f64e702a GIT binary patch literal 2905 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 Gt^WnfrZ~I+ literal 0 HcmV?d00001 diff --git a/demos/esfm/esfm_ins.fur b/demos/esfm/esfm_ins.fur new file mode 100644 index 0000000000000000000000000000000000000000..48af457e8e929bd6786951e75dce058866a5f087 GIT binary patch literal 7393 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 literal 0 HcmV?d00001 diff --git a/demos/esfm/experiment.fur b/demos/esfm/experiment.fur new file mode 100644 index 0000000000000000000000000000000000000000..d0a7a69920b356fcf210e06ee75a3dc4fce56092 GIT binary patch literal 4567 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 I@`ktn4JSDDn*aa+ literal 0 HcmV?d00001 diff --git a/demos/esfm/flashback.fur b/demos/esfm/flashback.fur new file mode 100644 index 0000000000000000000000000000000000000000..597abd45e582d8dd0b92ea34388cf4a5a43b23cf GIT binary patch literal 2043 zcmVer&S+#s?;L!fXpMLmC74PR7Jdi#0rQ+fPknFR3gsu z-7`Nsvp>mVl1503{mr@e+;h%%?m2g6*Y4_{&(BPp%4W{aPR>tfyDk$^hHBTqVE@oQ z4FgRRKl|zQ6KkZAmJIXSmw@-^=2y=xu3Na@^{an+qHyNq#Hp!FSEe_AHanRao}JF2 zUQbNS<#UtSJQZ@&(~D!LXJ-pp*#G*Aq$omq3gN^MViOfi1c<%~7(t>U@JHZjo#>aq zL4)W`;NcL_Ch!|zKZ1wV zqOsjXds46gegXUr_y7?16D5H(@DT7ga0vJc@GP(hYyjT|-T;0GybHV!{1*5dKo6i+ zz$bw&U_bC>;KT#6l!|&gprA+oOKrGzd}P+T$?3oR)tsZC$SHjbKi1VZI-2PleWX|{ zW(wJSE?da#?Hlbs{9s)iT%UFh5=Z!XRR9X00vZqif`ASfKnMr}5g-c0fH;rO2L?wUU4gtzpQpjz7!Ax6=JNArvomvU^wu$=<7#zYLJ3yor36f zH5H(0-(Y)L><%lOMKH=z+!qb7+-2$i(=6~(Ko+EGRE4y&m~yiS(y2UNLN5KVNbqKd z$&$2YOT1zrgNJ()pcex4?~vGor@5X|fJ#wy+6EL^0E_aBOR3Ts4<99h7GKz~d32T< z%QpCwdlM8+>o<&DI>o>jvG|^K`2sYRJ2mBHjM_Vl*o+Qy&?>N(v9k#y7u1W!q84T* z@>vgMJYrK?R3fTLsi+~tOQ}@pZ4&#M6*aA2|1hop>4S9Q50p-*H!RBMT*|0Lxp!_N zH~pYXJSw|*9fwI)^*YX?NQ8UNfnUY^pKTGV`d`q+?QKg2w=Lpnmsm$PXU}D4yqmQ< z8nWveusf>hjrDEbQC)VFxTD=}N1ya`^yEaL;4+@r&1X{^{cM8EDw0-t1zGckXF}tH z=^W7QHlNIqIKK~q$M5GyLZL9v6%k5^ojjRZ)P6E``w$KH9vvKU-hdCsPE)sbrF^MV zTQ@tk4d9{~d4@EG~keqfXUpn?*fF)~63e7V6FO zq?zhGmDM7d?rNjiJj^1?l8{ICItPE8gRAWaFIod&ipr1Q#!@wpI#aK07L_Y1WJQ;nl}cU2rq5&3=bcSAm@UdWu1P{3ov*K`P+JBs z)(2m1RZ)d2I@6@0XX_(R$y9Rn6ah;q9Mk{)-; ze={SuN4B=6Xg7ss3vKyYRIjwoldJMw`Os_Xoj4{|)9a1D(Q2V(6wY&uzy1QLaqXJ8 z#CMbL<%WBS?=O06ZfRf*jO(X z86qe@DvAm#_`i%n)a)OQCdL|tO|GirUNcbt*wI9!MwdM;D%UJklvOg8)LDcbtWzvA zaWysWELg@eVog_;I{fWGp)GAQJs;YCGkF$B6vr%L=<vQ}1?1Q-MufMtxOTolL#5_++PJXuXxw#-(r zya1Tct@6C)Ds4=lWZSJkZ`q+NR)6|93H4S<%!`s*1neYC1Y6}KWT4GX?E(i|lfmS9 zG_}cI1z+(X}sdA^F%@T{rJSqdAcQP6SQHLg{ui`g<~< ze1W8fj4S7;^Uqj5Xp~tF879l)Mv3L1D@|Kk_D77`_>jFeWU$^@XE|!eJ!0F8+3^mw zl(qOzu@9&GCZ{aV#E|hnk?@Z=VTBqKtdW~GVU65L`k1>`@+IT7Z6mJhbKD+3ZObuR zo@g1LH}NWOLcML9uBa=>i3QMePe##nBO<%_l@~|<9^?`-#6~}jr)BQe&2-OH{th9_Gw_geLMWV9e&>qzi$Wo{u}h{={$Wr1)2Z= literal 0 HcmV?d00001 diff --git a/demos/esfm/frosty_dusk.fur b/demos/esfm/frosty_dusk.fur new file mode 100644 index 0000000000000000000000000000000000000000..a836a3ef462a8abb664a21e90f1918c6f9f87506 GIT binary patch literal 7693 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