From 577d6fd4d4b380acb506fc304c8cc36951fefa3a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 22 Aug 2023 20:23:21 -0500 Subject: [PATCH 01/11] GUI: fix expand selection upwards --- src/gui/cursor.cpp | 12 ++++++++---- src/gui/debugWindow.cpp | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 15f9e53ff..da2028454 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -374,7 +374,9 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) { } void FurnaceGUI::moveCursorTop(bool select) { - finishSelection(); + if (!select) { + finishSelection(); + } curNibble=false; if (cursor.y==0) { DETERMINE_FIRST; @@ -384,16 +386,18 @@ void FurnaceGUI::moveCursorTop(bool select) { } else { cursor.y=0; } - selStart=cursor; if (!select) { - selEnd=cursor; + selStart=cursor; } + selEnd=cursor; e->setMidiBaseChan(cursor.xCoarse); updateScroll(cursor.y); } void FurnaceGUI::moveCursorBottom(bool select) { - finishSelection(); + if (!select) { + finishSelection(); + } curNibble=false; if (cursor.y==e->curSubSong->patLen-1) { DETERMINE_LAST; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 9d59e44d2..b3c07b071 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -308,6 +308,21 @@ void FurnaceGUI::drawDebug() { } ImGui::TreePop(); } + if (ImGui::TreeNode("Do Action")) { + char bindID[1024]; + for (int j=0; j Date: Wed, 23 Aug 2023 02:19:36 -0500 Subject: [PATCH 02/11] GUI: improve wavetable tab in ins edit --- src/gui/insEdit.cpp | 361 +++++++++++++++++++++++--------------------- 1 file changed, 190 insertions(+), 171 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 86f08232b..5a3537f4b 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -5275,180 +5275,199 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::Checkbox("Enable synthesizer",&ins->ws.enabled)) { wavePreviewInit=true; } - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ins->ws.effect&0x80) { - if ((ins->ws.effect&0x7f)>=DIV_WS_DUAL_MAX) { - ins->ws.effect=0; + if (ins->ws.enabled) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ins->ws.effect&0x80) { + if ((ins->ws.effect&0x7f)>=DIV_WS_DUAL_MAX) { + ins->ws.effect=0; + wavePreviewInit=true; + } + } else { + if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) { + ins->ws.effect=0; + wavePreviewInit=true; + } + } + if (ImGui::BeginCombo("##WSEffect",(ins->ws.effect&0x80)?dualWSEffects[ins->ws.effect&0x7f]:singleWSEffects[ins->ws.effect&0x7f])) { + ImGui::Text("Single-waveform"); + ImGui::Indent(); + for (int i=0; iws.effect=i; + wavePreviewInit=true; + } + } + ImGui::Unindent(); + ImGui::Text("Dual-waveform"); + ImGui::Indent(); + for (int i=129; iws.effect=i; + wavePreviewInit=true; + } + } + ImGui::Unindent(); + ImGui::EndCombo(); + } + const bool isSingleWaveFX=(ins->ws.effect>=128); + if (ImGui::BeginTable("WSPreview",isSingleWaveFX?3:2)) { + DivWavetable* wave1=e->getWave(ins->ws.wave1); + DivWavetable* wave2=e->getWave(ins->ws.wave2); + if (wavePreviewInit) { + wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true); + wavePreviewInit=false; + } + float wavePreview1[256]; + float wavePreview2[256]; + float wavePreview3[256]; + for (int i=0; ilen; i++) { + if (wave1->data[i]>wave1->max) { + wavePreview1[i]=wave1->max; + } else { + wavePreview1[i]=wave1->data[i]; + } + } + for (int i=0; ilen; i++) { + if (wave2->data[i]>wave2->max) { + wavePreview2[i]=wave2->max; + } else { + wavePreview2[i]=wave2->data[i]; + } + } + if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) { + wavePreview.tick(true); + } + for (int i=0; idata[i]>wavePreviewHeight) { + wavePreview3[i]=wavePreviewHeight; + } else { + wavePreview3[i]=wavePreview.output[i]; + } + } + + float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,"Wave 1",0,wave1->max,size1); + if (isSingleWaveFX) { + ImGui::TableNextColumn(); + ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2); + } + ImGui::TableNextColumn(); + ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize); + PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ins->std.waveMacro.len>0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Wave 1 " ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("waveform macro is controlling wave 1!\nthis value will be ineffective."); + } + } else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Wave 1"); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SelWave1",&ins->ws.wave1,1,4)) { + if (ins->ws.wave1<0) ins->ws.wave1=0; + if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1; + wavePreviewInit=true; + } + if (ins->std.waveMacro.len>0) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("waveform macro is controlling wave 1!\nthis value will be ineffective."); + } + } + if (isSingleWaveFX) { + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Wave 2"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) { + if (ins->ws.wave2<0) ins->ws.wave2=0; + if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1; + wavePreviewInit=true; + } + } + ImGui::TableNextColumn(); + if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) { + wavePreviewPaused=!wavePreviewPaused; + } + if (ImGui::IsItemHovered()) { + if (wavePreviewPaused) { + ImGui::SetTooltip("Resume preview"); + } else { + ImGui::SetTooltip("Pause preview"); + } + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) { + wavePreviewInit=true; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Restart preview"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) { + curWave=e->addWave(); + if (curWave==-1) { + showError("too many wavetables!"); + } else { + wantScrollList=true; + MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; + nextWindow=GUI_WINDOW_WAVE_EDIT; + + DivWavetable* copyWave=e->song.wave[curWave]; + copyWave->len=wavePreviewLen; + copyWave->max=wavePreviewHeight; + memcpy(copyWave->data,wavePreview.output,256*sizeof(int)); + } + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Copy to new wavetable"); + } + ImGui::SameLine(); + ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1); + ImGui::EndTable(); + } + + if (ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_SEVEN)) { + wavePreviewInit=true; + } + int speed=ins->ws.speed+1; + if (ImGui::InputInt("Speed",&speed,1,16)) { + if (speed<1) speed=1; + if (speed>256) speed=256; + ins->ws.speed=speed-1; + wavePreviewInit=true; + } + + if (ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN)) { + wavePreviewInit=true; + } + + if (ins->ws.effect==DIV_WS_PHASE_MOD) { + if (ImGui::InputScalar("Power",ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_SEVEN)) { + wavePreviewInit=true; + } + } + + if (ImGui::Checkbox("Global",&ins->ws.global)) { wavePreviewInit=true; } } else { - if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) { - ins->ws.effect=0; - wavePreviewInit=true; - } - } - if (ImGui::BeginCombo("##WSEffect",(ins->ws.effect&0x80)?dualWSEffects[ins->ws.effect&0x7f]:singleWSEffects[ins->ws.effect&0x7f])) { - ImGui::Text("Single-waveform"); - ImGui::Indent(); - for (int i=0; iws.effect=i; - wavePreviewInit=true; - } - } - ImGui::Unindent(); - ImGui::Text("Dual-waveform"); - ImGui::Indent(); - for (int i=129; iws.effect=i; - wavePreviewInit=true; - } - } - ImGui::Unindent(); - ImGui::EndCombo(); - } - const bool isSingleWaveFX=(ins->ws.effect>=128); - if (ImGui::BeginTable("WSPreview",isSingleWaveFX?3:2)) { - DivWavetable* wave1=e->getWave(ins->ws.wave1); - DivWavetable* wave2=e->getWave(ins->ws.wave2); - if (wavePreviewInit) { - wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true); - wavePreviewInit=false; - } - float wavePreview1[256]; - float wavePreview2[256]; - float wavePreview3[256]; - for (int i=0; ilen; i++) { - if (wave1->data[i]>wave1->max) { - wavePreview1[i]=wave1->max; - } else { - wavePreview1[i]=wave1->data[i]; - } - } - for (int i=0; ilen; i++) { - if (wave2->data[i]>wave2->max) { - wavePreview2[i]=wave2->max; - } else { - wavePreview2[i]=wave2->data[i]; - } - } - if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) { - wavePreview.tick(true); - } - for (int i=0; idata[i]>wavePreviewHeight) { - wavePreview3[i]=wavePreviewHeight; - } else { - wavePreview3[i]=wavePreview.output[i]; - } - } - - float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,"Wave 1",0,wave1->max,size1); - if (isSingleWaveFX) { - ImGui::TableNextColumn(); - ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,"Wave 2",0,wave2->max,size2); - } - ImGui::TableNextColumn(); - ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize); - PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Wave 1"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SelWave1",&ins->ws.wave1,1,4)) { - if (ins->ws.wave1<0) ins->ws.wave1=0; - if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1; - wavePreviewInit=true; - } - if (isSingleWaveFX) { - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Wave 2"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) { - if (ins->ws.wave2<0) ins->ws.wave2=0; - if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1; - wavePreviewInit=true; - } - } - ImGui::TableNextColumn(); - if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) { - wavePreviewPaused=!wavePreviewPaused; - } - if (ImGui::IsItemHovered()) { - if (wavePreviewPaused) { - ImGui::SetTooltip("Resume preview"); - } else { - ImGui::SetTooltip("Pause preview"); - } - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) { - wavePreviewInit=true; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Restart preview"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) { - curWave=e->addWave(); - if (curWave==-1) { - showError("too many wavetables!"); - } else { - wantScrollList=true; - MARK_MODIFIED; - RESET_WAVE_MACRO_ZOOM; - nextWindow=GUI_WINDOW_WAVE_EDIT; - - DivWavetable* copyWave=e->song.wave[curWave]; - copyWave->len=wavePreviewLen; - copyWave->max=wavePreviewHeight; - memcpy(copyWave->data,wavePreview.output,256*sizeof(int)); - } - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Copy to new wavetable"); - } - ImGui::SameLine(); - ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1); - ImGui::EndTable(); - } - - if (ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_SEVEN)) { - wavePreviewInit=true; - } - int speed=ins->ws.speed+1; - if (ImGui::InputInt("Speed",&speed,1,16)) { - if (speed<1) speed=1; - if (speed>256) speed=256; - ins->ws.speed=speed-1; - wavePreviewInit=true; - } - - if (ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN)) { - wavePreviewInit=true; - } - - if (ins->ws.effect==DIV_WS_PHASE_MOD) { - if (ImGui::InputScalar("Power",ImGuiDataType_U8,&ins->ws.param2,&_ONE,&_SEVEN)) { - wavePreviewInit=true; - } - } - - if (ImGui::Checkbox("Global",&ins->ws.global)) { - wavePreviewInit=true; + ImGui::TextWrapped("wavetable synthesizer disabled.\nuse the Waveform macro to set the wave for this instrument."); } ImGui::EndTabItem(); From 7e63692f603f6e157bd478d98c47566cf493ac3d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Aug 2023 02:22:49 -0500 Subject: [PATCH 03/11] improve demo song --- demos/pc98/atomic_failure.fur | Bin 6467 -> 1937 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/demos/pc98/atomic_failure.fur b/demos/pc98/atomic_failure.fur index 1f6e60454e3480135221a64da37cc4abb56f36ab..61b4305373fcb0389d99a88ed26ad22021ea0d46 100644 GIT binary patch literal 1937 zcmbu;`9Bj30|0PqQH~*VJRR<%MBl7`FicUel~JcEncE2 z&x?Cc{mdJUQ3BcdX{Z;j%)G(zyGwK3ai0FtES3;+irbO%RUfX(4fOSacUPEa}a5a+0ej}jZ6HvT#53mPm04fE7-^z1O{ zW5RjS^0lDP*JRtI@FFx(*rN8XP9U(TV>^uo%-_q(OWRJ3zLG`U1e4ux@<5i5)Ci9s zy7X=>nBzuwY7ntm<}|(;onzJz1PtMMQ7vIi(vRqc&I}9ddy|XdeN!LudoeyLmT|!X zR<{{EQsSRzW=*{k)TTiMdhLus+53#ga!}|b!0GDmbKtTU87NQx!~;q-0FVkeb@Y(B z-H}ea!&!#{?4)>NiK*sxf2UzZ_Z0*_6Y&29NjMgKss5lGzz_W^0n{WI6qqRnK5LNV z-huaVLp0g^f6}kSImm`wX5Vy~Y9#xi=}3bSH^ao}xaz=MP>BsVK+nJFda*w%Pof>! z_B5t8Cteyx3=yYZ4|ggzP1)~fNy08bnx}!X{hyANO~CRhlOh)uRzq1UpF*QriwvtQ zX8P$-$SQ<1yQ@2&2R5C7)*N$tY3w5=Rjk;zVpbk$m>BBVAHljg$JRD6SN=ne4}v=W z!QuakA|~K;7*jkQmEx9!vmp_;er_hTwK2|JB7RJdjGO;>x%k`5`wnOL;%TSx@>`me zS!b)I#q*PcGhQzO&dbeXr!JKkZ*0(3ky*#P?<7NGaro6yV1`u+2tD1F*3x)+ZTqouv-FZY(Dq+N2^WK5lFy2{VBAVkFwA>sAc=$#xVpIvbMi@1m#%2p^=0g{ys zNDqm zb1TO8=6ul(<1^@u3l@qq5qd0Kr!Ok$3xARDvlN{OWB-ey01ukBmVF~0k)>NXwY0&b z5=Ln7+QD$vyKi}2W9s=3Z&GikVr}o}b07H>(Yl%6B{A67Cs<2W^@`pYR^MWItzjeE zwu-h?B`E=SA0*bskMIoc+`jKcd1~%m)a07zN67tSBzw!etpAs(QR!W6Jj7xI6*jANao&K{|_dBCoRR=88&1X3dbFI%8TsvhPrg<}4DYyFcF2TaNjU*P>u*okc ze^*l5TE7*%>Yt=CDS}PdKnAWqB0`ZU)gh9YtcRAyTmf@` z(;_CM7H}Q4u z@BI_I?wyX>N4kW2kHvwIyb#sGKs;SZLw~8KT$e$`bef+WdH{m5PE*GTh)5oNmoglO zpbv}_?EFcSaq2LztG(@|cz4=t-J9N?>Sq3X)934c;M`QkRtq0*NjxG`t_c}oAKB!*E`lJVr1(yfHQ^)6Jc zjB$fj5_G<|kkG9d)%-#Z`h$mhE}8SN_4`F@Oil_$5^=}W5V7$|W>v8tJyf8}fs+Ie(n~Y%0bmDgh7JzEo{QKdXUpAPLP_OF6lp0 vBh-*GNWA5$ckliU%`-_;`JU${w!}sZDQISZh8{EE|4FsGiv=WAzx4VqrDW*3 literal 6467 zcmdT|O>7(25gu}vd?H1Ojwo4D>2kMi5W59j%c|_S5EN1IPh%(gv8>85WT%p7T9mpH zWs<6Gp@pSei}deM2R+z_X{FC?f>I7S1B$p=GqZ-1&QMWzoBtpDUG$cfN{b|9qChVYG`VBzLV{vWXr-eh=x15Y0pW4jGCP{S5LU zq(u>3fqbcj=pT?;jHtDh=r538Cy36q5&adi)=m^p5=|Z=`r`{kl@7#A5xoQ%fcysX zN+;2`As%EM@w0WQOq@r7y{V(*P#FL8 z#g*^5HiiY_-td34L$@g`@H_O5?=g1%tUGb`_&3kE$L=gv3R>?wdA$GhiPyQ!a{0la z_xT97oI}Z5izix4Xf2_wgq9LoO8f$&rFfZ%&`v@-3GKx7hn)~446z_KBm#*-6r=?b zgTx`NkOZU+(hf;Nh^p|f!oLdtD*UVPufo3y|0?{e@UOzZ3jZqntMISFzY2f$^{H5K1mpj(*?1t5)QGTkf8LEE&jx=+Bw` z&Xa1eT~)!z*VN6*OuU)i%wG4VXsn`j-Icw#?wR&7b<@(orJAT5X*i6S8?_LvFgwsI z3Ryv~D8z%VaSu4 zca1C=$b#tbcz*1kY+U~13V9Wbd`;b~%*31N&Fpn=ipDBh*In6*>z-*ZQ#UQ^xK#5C zA%{2a9s5364id>RX^emBT~b;<342f-Jje=U6;gqeAtlID=bd?4w+=ns=AamolYBVxN0Pe!sdLI9r_8hDgtet| zvUQ{QPf~ok5e+BDWA{G}*^k{s%7t`)B{3aKbq=bj7^**$Ou6>so>X^QgX1ylOdag$ zNgZ~kV)s*DJhGbVcBY|qLhDtC_}N7pRx=3PZTF-y!^x{qd+ojok^f+ zV6JmnG{%ra>WE;YyRfu)@N6$4a#`}6^q;|a=E)dkW`-57aoDyivg~05$q2dY(nstD zo!ghWSv`ZWwr1_YNnaLWDc@E|VMiKbkTmErZfdQuK3lY1A4a0Zhz zF)N!Aohd%nW~ZDp$x!48#AYYj-iqB1f$-I%i5n=_Rd2pp>s~?jv z@Jre9__BKXmRAede5L(uWUhWN$i{b@Z!)zak#Ichwo$V{b{#R2-$Tg{pXN{B{_3hk z-E%|;zALJOqs{7TSevV>TS+D*HDzWfm>hx zJ{7S9${S2_3991}PBpc)YPmA~1(`-qID-w$T~%9amizEnn`#YN(5nhYzNT(gX5!8CX7;)_MPn7M>#pp@bZz;_3^iu5rxW y;i`k~G|nG1KFFWra(r-CU#Dn{nXO>VY_WYXWb^B&VK>jpP^&` From e4e92f9af33e87a962f660001759dc0e22a20cc3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Aug 2023 04:29:17 -0500 Subject: [PATCH 04/11] too many buttons --- src/gui/dataList.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index b373f72af..dce62c82a 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -892,15 +892,11 @@ void FurnaceGUI::drawSampleList(bool asChild) { doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW); } if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Preview"); + ImGui::SetTooltip("Preview (right click to stop)"); } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) { + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW); } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Stop preview"); - } ImGui::SameLine(); pushDestColor(); if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) { From b946c35fa7f88f3ddc364a31f7e5bad7fe25bd98 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Aug 2023 12:21:39 -0500 Subject: [PATCH 05/11] GUI: don't update FM/wave synth preview twice issue #1404 --- src/gui/dataList.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index dce62c82a..1506c5ff3 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -105,8 +105,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { bool insPressed=ImGui::IsItemActivated(); if (insReleased || (!insListDir && insPressed)) { curIns=i; - wavePreviewInit=true; - updateFMPreview=true; + if (!insReleased || insListDir) { + wavePreviewInit=true; + updateFMPreview=true; + } lastAssetType=0; if (settings.insFocusesPattern && patternOpen) nextWindow=GUI_WINDOW_PATTERN; From 62a1a383ee53bdd687d8b539b04d7d48a159d297 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 23 Aug 2023 12:50:22 -0500 Subject: [PATCH 06/11] dev168 - new behavior for porta in C64 --- papers/format.md | 3 ++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 15 +++++++++++++-- src/engine/playback.cpp | 20 +++++++++++++++++++- src/engine/song.h | 4 +++- 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/papers/format.md b/papers/format.md index 38696622a..267b74108 100644 --- a/papers/format.md +++ b/papers/format.md @@ -348,7 +348,8 @@ size | description --- | **a couple more compat flags** (>=138) 1 | broken portamento during legato 1 | broken macro during note off in some FM chips (>=155) - 6 | reserved + 1 | pre note (C64) does not compensate for portamento or legato (>=168) + 5 | reserved --- | **speed pattern of first song** (>=139) 1 | length of speed pattern (fail if this is lower than 0 or higher than 16) 16 | speed pattern (this overrides speed 1 and speed 2 settings) diff --git a/src/engine/engine.h b/src/engine/engine.h index 3a2972170..683289edd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -56,8 +56,8 @@ #define DIV_UNSTABLE -#define DIV_VERSION "dev167" -#define DIV_ENGINE_VERSION 167 +#define DIV_VERSION "dev168" +#define DIV_ENGINE_VERSION 168 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 9cf94547b..1465c816e 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -183,6 +183,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.brokenPortaArp=false; ds.snNoLowPeriods=true; ds.disableSampleMacro=true; + ds.preNoteNoEffect=true; ds.delayBehavior=0; ds.jumpTreatment=2; @@ -1844,6 +1845,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<155) { ds.brokenFMOff=true; } + if (ds.version<168) { + ds.preNoteNoEffect=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -2355,7 +2359,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<6; i++) { + if (ds.version>=168) { + ds.preNoteNoEffect=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<5; i++) { reader.readC(); } } @@ -5383,7 +5392,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) { // even more compat flags w->writeC(song.brokenPortaLegato); - for (int i=0; i<7; i++) { + w->writeC(song.brokenFMOff); + w->writeC(song.preNoteNoEffect); + for (int i=0; i<5; i++) { w->writeC(0); } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index f379b7219..0078b015b 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1208,8 +1208,26 @@ void DivEngine::nextRow() { if (disCont[dispatchOfChan[i]].dispatch!=NULL) { wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote(); if (wantPreNote) { + bool doPreparePreNote=true; int addition=0; + for (int j=0; jdata[curRow][4+(j<<1)]==0x03) { + doPreparePreNote=false; + break; + } + if (pat->data[curRow][4+(j<<1)]==0x06) { + doPreparePreNote=false; + break; + } + if (pat->data[curRow][4+(j<<1)]==0xea) { + if (pat->data[curRow][5+(j<<1)]>0) { + doPreparePreNote=false; + break; + } + } + } if (pat->data[curRow][4+(j<<1)]==0xed) { if (pat->data[curRow][5+(j<<1)]>0) { addition=pat->data[curRow][5+(j<<1)]&255; @@ -1217,7 +1235,7 @@ void DivEngine::nextRow() { } } } - dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks+addition)); + if (doPreparePreNote) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks+addition)); } } diff --git a/src/engine/song.h b/src/engine/song.h index 803f87360..f05100798 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -375,6 +375,7 @@ struct DivSong { bool patchbayAuto; bool brokenPortaLegato; bool brokenFMOff; + bool preNoteNoEffect; std::vector ins; std::vector wave; @@ -493,7 +494,8 @@ struct DivSong { oldArpStrategy(false), patchbayAuto(true), brokenPortaLegato(false), - brokenFMOff(false) { + brokenFMOff(false), + preNoteNoEffect(false) { for (int i=0; i Date: Wed, 23 Aug 2023 17:25:05 -0500 Subject: [PATCH 07/11] Eknous please HELP ME --- src/engine/dispatch.h | 7 +++++++ src/engine/platform/abstract.cpp | 4 ++++ src/engine/platform/arcade.cpp | 4 ++++ src/engine/platform/arcade.h | 1 + src/engine/platform/c140.cpp | 4 ++++ src/engine/platform/c140.h | 1 + src/engine/platform/es5506.cpp | 4 ++++ src/engine/platform/es5506.h | 1 + src/engine/platform/gb.cpp | 5 +++++ src/engine/platform/gb.h | 1 + src/engine/platform/genesis.h | 1 + src/engine/platform/genesisext.cpp | 6 ++++++ src/engine/platform/genesisext.h | 1 + src/engine/platform/k007232.cpp | 4 ++++ src/engine/platform/k007232.h | 1 + src/engine/platform/k053260.h | 1 + src/engine/platform/lynx.cpp | 4 ++++ src/engine/platform/lynx.h | 1 + src/engine/platform/msm6258.h | 1 + src/engine/platform/namcowsg.cpp | 5 +++++ src/engine/platform/namcowsg.h | 1 + src/engine/platform/opl.h | 1 + src/engine/platform/pce.cpp | 4 ++++ src/engine/platform/pce.h | 1 + src/engine/platform/pcmdac.cpp | 4 ++++ src/engine/platform/pcmdac.h | 1 + src/engine/platform/qsound.h | 1 + src/engine/platform/rf5c68.h | 1 + src/engine/platform/saa.cpp | 4 ++++ src/engine/platform/saa.h | 1 + src/engine/platform/segapcm.cpp | 4 ++++ src/engine/platform/segapcm.h | 1 + src/engine/platform/sms.h | 1 + src/engine/platform/snes.cpp | 4 ++++ src/engine/platform/snes.h | 1 + src/engine/platform/su.h | 1 + src/engine/platform/swan.cpp | 4 ++++ src/engine/platform/swan.h | 1 + src/engine/platform/t6w28.cpp | 4 ++++ src/engine/platform/t6w28.h | 1 + src/engine/platform/tx81z.cpp | 4 ++++ src/engine/platform/tx81z.h | 1 + src/engine/platform/vb.cpp | 4 ++++ src/engine/platform/vb.h | 1 + src/engine/platform/vera.h | 1 + src/engine/platform/x1_010.cpp | 4 ++++ src/engine/platform/x1_010.h | 1 + src/engine/platform/ym2608.h | 1 + src/engine/platform/ym2608ext.cpp | 6 ++++++ src/engine/platform/ym2608ext.h | 1 + src/engine/platform/ym2610.h | 1 + src/engine/platform/ym2610b.h | 1 + src/engine/platform/ym2610bext.cpp | 6 ++++++ src/engine/platform/ym2610bext.h | 1 + src/engine/platform/ym2610ext.cpp | 6 ++++++ src/engine/platform/ym2610ext.h | 1 + src/engine/platform/ymz280b.h | 1 + 57 files changed, 139 insertions(+) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 875ff487d..0ad264e53 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -411,6 +411,13 @@ class DivDispatch { */ virtual DivMacroInt* getChanMacroInt(int chan); + /** + * get the stereo panning of a channel. + * @param chan the channel. + * @return a 16-bit number. left in top 8 bits and right in bottom 8 bits. + */ + virtual unsigned short getPan(int chan); + /** * get currently playing sample (and its position). * @param chan the channel. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 2be22d1f4..a06d9caf0 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -33,6 +33,10 @@ void* DivDispatch::getChanState(int chan) { return NULL; } +unsigned short DivDispatch::getPan(int chan) { + return 0; +} + DivMacroInt* DivDispatch::getChanMacroInt(int chan) { return NULL; } diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 29b62e30a..95a3d7750 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -857,6 +857,10 @@ DivMacroInt* DivPlatformArcade::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformArcade::getPan(int ch) { + return (chan[ch].chVolL<<8)|(chan[ch].chVolR); +} + DivDispatchOscBuffer* DivPlatformArcade::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index b5720f197..39ae3705e 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -75,6 +75,7 @@ class DivPlatformArcade: public DivPlatformOPM { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); void notifyInsChange(int ins); void notifyInsDeletion(void* ins); void setFlags(const DivConfig& flags); diff --git a/src/engine/platform/c140.cpp b/src/engine/platform/c140.cpp index c9e83338c..ba80284a7 100644 --- a/src/engine/platform/c140.cpp +++ b/src/engine/platform/c140.cpp @@ -344,6 +344,10 @@ DivMacroInt* DivPlatformC140::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformC140::getPan(int ch) { + return (chan[ch].chPanL<<8)|(chan[ch].chPanR); +} + DivDispatchOscBuffer* DivPlatformC140::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/c140.h b/src/engine/platform/c140.h index bb34dac65..1c8ed9079 100644 --- a/src/engine/platform/c140.h +++ b/src/engine/platform/c140.h @@ -74,6 +74,7 @@ class DivPlatformC140: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index 4f8745bda..b25e6954c 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -1057,6 +1057,10 @@ DivMacroInt* DivPlatformES5506::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformES5506::getPan(int ch) { + return ((chan[ch].lVol>>4)<<8)|(chan[ch].rVol>>4); +} + void DivPlatformES5506::reset() { while (!hostIntf32.empty()) hostIntf32.pop(); while (!hostIntf8.empty()) hostIntf8.pop(); diff --git a/src/engine/platform/es5506.h b/src/engine/platform/es5506.h index b7658c52f..07bdb2380 100644 --- a/src/engine/platform/es5506.h +++ b/src/engine/platform/es5506.h @@ -295,6 +295,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf { virtual int dispatch(DivCommand c) override; virtual void* getChanState(int chan) override; virtual DivMacroInt* getChanMacroInt(int ch) override; + virtual unsigned short getPan(int chan) override; virtual DivDispatchOscBuffer* getOscBuffer(int chan) override; virtual unsigned char* getRegisterPool() override; virtual int getRegisterPoolSize() override; diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 59c950d18..741fe2c3c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -578,6 +578,11 @@ DivMacroInt* DivPlatformGB::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformGB::getPan(int ch) { + unsigned char p=lastPan&(0x11<=4+extChanOffs) return DivPlatformGenesis::getPan(ch-3); + if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + return DivPlatformGenesis::getPan(ch); +} + DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index c668d5104..63112c069 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -34,6 +34,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index d1037988a..fb44a1a0d 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -424,6 +424,10 @@ DivMacroInt* DivPlatformK007232::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformK007232::getPan(int ch) { + return ((chan[ch].panning&15)<<8)|((chan[ch].panning&0xf0)>>4); +} + DivDispatchOscBuffer* DivPlatformK007232::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/k007232.h b/src/engine/platform/k007232.h index b1025f574..c8ea8aea7 100644 --- a/src/engine/platform/k007232.h +++ b/src/engine/platform/k007232.h @@ -85,6 +85,7 @@ class DivPlatformK007232: public DivDispatch, public k007232_intf { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/k053260.h b/src/engine/platform/k053260.h index ce531f3d5..27b2a7910 100644 --- a/src/engine/platform/k053260.h +++ b/src/engine/platform/k053260.h @@ -64,6 +64,7 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf { virtual int dispatch(DivCommand c) override; virtual void* getChanState(int chan) override; virtual DivMacroInt* getChanMacroInt(int ch) override; + virtual unsigned short getPan(int chan) override; virtual DivDispatchOscBuffer* getOscBuffer(int chan) override; virtual unsigned char* getRegisterPool() override; virtual int getRegisterPoolSize() override; diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 24ee1f9c4..c8a34d461 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -430,6 +430,10 @@ DivMacroInt* DivPlatformLynx::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformLynx::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivSamplePos DivPlatformLynx::getSamplePos(int ch) { if (ch>=4) return DivSamplePos(); if (!chan[ch].pcm) return DivSamplePos(); diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index c68106dec..e081f7ff7 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -72,6 +72,7 @@ class DivPlatformLynx: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index 6be120c2b..21ff2a9f2 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -62,6 +62,7 @@ class DivPlatformMSM6258: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 5ac493f19..f6b6d062a 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -473,6 +473,11 @@ DivMacroInt* DivPlatformNamcoWSG::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformNamcoWSG::getPan(int ch) { + if (devType!=30) return 0; + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivDispatchOscBuffer* DivPlatformNamcoWSG::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/namcowsg.h b/src/engine/platform/namcowsg.h index 6aaef0952..7d6943323 100644 --- a/src/engine/platform/namcowsg.h +++ b/src/engine/platform/namcowsg.h @@ -62,6 +62,7 @@ class DivPlatformNamcoWSG: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index a417b5088..2298f6132 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -114,6 +114,7 @@ class DivPlatformOPL: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index b6f43e2da..2daba109e 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -508,6 +508,10 @@ DivMacroInt* DivPlatformPCE::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformPCE::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivSamplePos DivPlatformPCE::getSamplePos(int ch) { if (ch>=6) return DivSamplePos(); if (!chan[ch].pcm) return DivSamplePos(); diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index baca77701..f989034af 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -82,6 +82,7 @@ class DivPlatformPCE: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 940efb3e7..1ddd22dae 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -497,6 +497,10 @@ DivMacroInt* DivPlatformPCMDAC::getChanMacroInt(int ch) { return &chan[0].std; } +unsigned short DivPlatformPCMDAC::getPan(int ch) { + return (chan[0].panL<<8)|chan[0].panR; +} + DivSamplePos DivPlatformPCMDAC::getSamplePos(int ch) { if (ch>=1) return DivSamplePos(); return DivSamplePos( diff --git a/src/engine/platform/pcmdac.h b/src/engine/platform/pcmdac.h index 8ef10149c..17513690a 100644 --- a/src/engine/platform/pcmdac.h +++ b/src/engine/platform/pcmdac.h @@ -78,6 +78,7 @@ class DivPlatformPCMDAC: public DivDispatch { void muteChannel(int ch, bool mute); int getOutputCount(); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); void setFlags(const DivConfig& flags); void notifyInsChange(int ins); diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index aff53f679..6daccc120 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -66,6 +66,7 @@ class DivPlatformQSound: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/rf5c68.h b/src/engine/platform/rf5c68.h index 4ba40318c..9c706ccd5 100644 --- a/src/engine/platform/rf5c68.h +++ b/src/engine/platform/rf5c68.h @@ -59,6 +59,7 @@ class DivPlatformRF5C68: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 803a6cad3..b1ba48826 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -365,6 +365,10 @@ DivMacroInt* DivPlatformSAA1099::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformSAA1099::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivDispatchOscBuffer* DivPlatformSAA1099::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index ffd79db72..36db57cb7 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -79,6 +79,7 @@ class DivPlatformSAA1099: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index b9af8a912..5e584e016 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -394,6 +394,10 @@ DivMacroInt* DivPlatformSegaPCM::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformSegaPCM::getPan(int ch) { + return (chan[ch].chPanL<<8)|chan[ch].chPanR; +} + DivSamplePos DivPlatformSegaPCM::getSamplePos(int ch) { if (ch>=16) return DivSamplePos(); if (chan[ch].pcm.sample<0 || chan[ch].pcm.sample>=parent->song.sampleLen) return DivSamplePos(); diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 067054fe1..639d875b4 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -89,6 +89,7 @@ class DivPlatformSegaPCM: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 692c0a42c..d87546b30 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -77,6 +77,7 @@ class DivPlatformSMS: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index a32492fcf..1918cb842 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -699,6 +699,10 @@ DivMacroInt* DivPlatformSNES::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformSNES::getPan(int ch) { + return (chan[ch].panL<<8)|chan[ch].panR; +} + DivSamplePos DivPlatformSNES::getSamplePos(int ch) { if (ch>=8) return DivSamplePos(); if (!chan[ch].active) return DivSamplePos(); diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index cec51c0c1..a799c7a5d 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -100,6 +100,7 @@ class DivPlatformSNES: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index d83ae4777..99b784df3 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -105,6 +105,7 @@ class DivPlatformSoundUnit: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 04039e6e5..4e5fb4cca 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -476,6 +476,10 @@ DivMacroInt* DivPlatformSwan::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformSwan::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 72ddae394..1e0fbeef7 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -62,6 +62,7 @@ class DivPlatformSwan: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/t6w28.cpp b/src/engine/platform/t6w28.cpp index 90140da99..f2dfe7120 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -300,6 +300,10 @@ DivMacroInt* DivPlatformT6W28::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformT6W28::getPan(int ch) { + return (chan[ch].panL<<8)|chan[ch].panR; +} + DivDispatchOscBuffer* DivPlatformT6W28::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index 33c03a886..4bb3cda2a 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -63,6 +63,7 @@ class DivPlatformT6W28: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 8577d4553..9de7262b3 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -965,6 +965,10 @@ DivMacroInt* DivPlatformTX81Z::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformTX81Z::getPan(int ch) { + return (chan[ch].chVolL<<8)|(chan[ch].chVolR); +} + DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index d0bc759c1..c14bc0117 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -65,6 +65,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 9edaf2db4..588676aff 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -419,6 +419,10 @@ DivMacroInt* DivPlatformVB::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformVB::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivDispatchOscBuffer* DivPlatformVB::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/vb.h b/src/engine/platform/vb.h index 2efcdd1b8..2282f62fe 100644 --- a/src/engine/platform/vb.h +++ b/src/engine/platform/vb.h @@ -69,6 +69,7 @@ class DivPlatformVB: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 227512a73..515e17e56 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -64,6 +64,7 @@ class DivPlatformVERA: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 29601ae92..e95d543a4 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -863,6 +863,10 @@ DivMacroInt* DivPlatformX1_010::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformX1_010::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index a3af7b291..cc698b650 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -131,6 +131,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index fc850acf4..e7a8bc6e5 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -80,6 +80,7 @@ class DivPlatformYM2608: public DivPlatformOPN { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + virtual unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 2966f9dfb..7c6e58ce2 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -750,6 +750,12 @@ DivMacroInt* DivPlatformYM2608Ext::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformYM2608Ext::getPan(int ch) { + if (ch>=4+extChanOffs) return DivPlatformYM2608::getPan(ch-3); + if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + return DivPlatformYM2608::getPan(ch); +} + DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index c9348b48f..0c9c1a418 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -33,6 +33,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 3805aa48a..99b1e73b7 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -48,6 +48,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + virtual unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 43c7af828..80c040e77 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -44,6 +44,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + virtual unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 2252a09df..98da9bfb9 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -740,6 +740,12 @@ DivMacroInt* DivPlatformYM2610BExt::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformYM2610BExt::getPan(int ch) { + if (ch>=4+extChanOffs) return DivPlatformYM2610B::getPan(ch-3); + if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + return DivPlatformYM2610B::getPan(ch); +} + DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) { if (ch>=(extChanOffs+4)) return oscBuf[ch-3]; if (ch<(extChanOffs+1)) return oscBuf[ch]; diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index b14ba99c4..024119cff 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -33,6 +33,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 1713114b7..bf17659be 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -740,6 +740,12 @@ DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformYM2610Ext::getPan(int ch) { + if (ch>=4+extChanOffs) return DivPlatformYM2610::getPan(ch-3); + if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + return DivPlatformYM2610::getPan(ch); +} + DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) { if (ch>=(extChanOffs+4)) return oscBuf[ch-3]; if (ch<(extChanOffs+1)) return oscBuf[ch]; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 2ce887c36..f860a36cd 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -33,6 +33,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ymz280b.h b/src/engine/platform/ymz280b.h index 14a67dc98..913b8ec6e 100644 --- a/src/engine/platform/ymz280b.h +++ b/src/engine/platform/ymz280b.h @@ -59,6 +59,7 @@ class DivPlatformYMZ280B: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivMacroInt* getChanMacroInt(int ch); + unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); From 01a5c6c4f9c901c13c0a225dad71f1cdb10f97c0 Mon Sep 17 00:00:00 2001 From: Electric Keet Date: Wed, 23 Aug 2023 23:33:17 -0700 Subject: [PATCH 08/11] Cleaning up the AY envelope guide. Lots of formatting, small corrections, and edits for clarity. --- doc/9-guides/envelope.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/doc/9-guides/envelope.md b/doc/9-guides/envelope.md index ec9fc1249..42aa3dfa0 100644 --- a/doc/9-guides/envelope.md +++ b/doc/9-guides/envelope.md @@ -1,27 +1,34 @@ -# AY-3-8910/8930/SAA1099 envelope guide +# AY-3-8910 / AY8930 / SAA1099 envelope guide -AY-3-8910 programmable sound generator, aside of normal 4-bit volume control, has an hardware volume envelope - a feature that allows you for defining shape of volume envelope at arbibrary speed, according to 8 preset envelope shapes. One may think, what is any upside of hardware envelope? Well, it's somewhat independent of tone/noise generators, and it goes so high in frequency, it can be used melodically! This guide explains how to abuse AY/SAA envelope. +The AY-3-8910 programmable sound generator, aside from normal 4-bit volume control, has an hardware volume envelope. This feature that allows for defining the shape of the volume envelope at arbitrary speed according to 8 preset envelope shapes. One may think, what is any upside of hardware envelope? Well, it's somewhat independent of tone/noise generators, and since it goes so high in frequency, it can be used melodically! This guide explains how to make best use of the AY/SAA envelope. -## AY-3-8910/AY8930 +## AY-3-8910 / AY8930 -going into instrument editor, first set the waveform macro value to `envelope`. This will disable any output, but don't worry. Then, go to `Envelope` macro and select `enable`. You will hear a very high-pitched squeak. This is because you must set envelope period - the frequency at which hardware envelope runs. You can do it in two ways: -- either via 23xx and 24xx effects (envelope coarse and fine period) or... -- 29xx auto-envelope period effect and macros +In the instrument editor: +- Add a single tick to the "Waveform" macro with only `envelope` turned on. This will disable any output, but don't worry. +- Add a single tick to the "Envelope" macro and select `enable`. -Auto-envelope works via numerator and denominator. In general, the higher the numerator, the higher the envelope pitch. The higher the denominator, the lower the envelope pitch. Why there are both of these? Because, envelope generator might be used to mask the tone output (i.e. affect the square wave as well). To do it, set the waveform macro values to both square and envelope. Then, the higher the denominator value, then the lower the envelope pitch relative to the square wave output, analogously the numerator. With square + envelope setting, a lot of wild, detuned, synth instruments can do made. +If you play a note now, you will hear a very high-pitched squeak. This is because you must set envelope period, which is the frequency at which the hardware envelope runs. You can do it in two ways: +- `23xx` and `24xx` effects (envelope coarse and fine period); +- `29xx` auto-envelope period effect and macros. -Back to the hardware envelope itself. Depending of the `Envelope` macro value, different envelope shapes can be obtained. The most basic one, at 8 is a sawtooth wave. The `direction` value will invert the envelope, producing the reverse sawtooth. The `alternate` value produces an interesting pseudo-triangular wave, similiar to halved sine. That one can also be reversed. `Hold` option disables the envelope. +Auto-envelope works via numerator and denominator. In general, the higher the numerator, the higher the envelope pitch. The higher the denominator, the lower the envelope pitch. Why are there both of these? Because the envelope generator might be used to mask the tone output (i.e. affect the square wave as well). To do it, set the "Waveform" macro values to both `tone` and `envelope`. The higher the denominator value, then the lower the envelope pitch relative to the square wave output, and similarly with the numerator. With the square-and-envelope setting, a lot of wild, detuned synth instruments can be made. -WARNING: the envelope pitch resolution is fairly low, at high pitched it will be detuned. Hence, it was used mostly for bass. -WARNING: there is only one hardware envelope generator. So, you cant use two pitches/two waveforms at once. +Back to the hardware envelope itself. Depending on the "Envelope" macro value, different envelope shapes can be obtained. The most basic one, 8, is a sawtooth wave. The `direction` value will invert the envelope, producing the reverse sawtooth. The `alternate` value produces an interesting pseudo-triangular wave, similiar to halved sine. That one can also be reversed. `Hold` option disables the envelope. + +_Warning:_ The envelope pitch resolution is fairly low; at high pitches it will be detuned. Because of this, it's used mostly for bass. + +_Warning_: There is only one hardware envelope generator. You can't use two pitches or two waveforms at once. ## SAA1099 -SAA envelope works a bit differently, It doesn't have its own pitch, it reles on a channel 2/5 pitch. It also has much more parameters than AY envelope. To use it: go to waveform macro, and set it to 0 (unless you want to have sqaure wave mask). Then, set up an envelope macro: tuen on enabled, loop and, depending on a desired shape, cut and direction. Resolution will give you higher pitch range than on AY. -Then lay two notes in pattern editor: the one in channel 2 will control the envelope pitch, the one in channel 3 can be any note you wish, its just to enable the envelope output. +SAA envelope works a bit differently. It doesn't have its own pitch; instead, it relies on the channel 2/5 pitch. It also has many more parameters than the AY envelope. To use it: +- Go to waveform macro and add a single tick set to 0 (unless you want to have a square wave mask). +- Set up an envelope macro. Turn on `enabled`, `loop`, and depending on the desired shape, `cut` and `direction`. `Resolution` will give you higher pitch range than on the AY. +- Place two notes in the pattern editor. One in channel 2 will control the envelope pitch. The other in channel 3 can be any note you wish; it's just to enable the envelope output. ## examples - [Demoscene-type Beat by Duccinator](https://www.youtube.com/watch?v=qcBgmpPrlUA) - [Philips SAA1099 Test by Duccinator](https://www.youtube.com/watch?v=IBh2gr09zjs) -- [Touhou Kaikidan: Mystic Square title theme by ZUN](https://www.youtube.com/watch?v=tUKei7Pz0Fw) /rare instance of AY envelope used for drums, it can be used to mask the noise generator output too \ No newline at end of file +- [Touhou Kaikidan: Mystic Square title theme by ZUN](https://www.youtube.com/watch?v=tUKei7Pz0Fw): Rare instance of AY envelope used for drums, it can be used to mask the noise generator output too \ No newline at end of file From 31335b95c9a7eb9f7cc728a729e0bd03a6190e00 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Aug 2023 03:05:25 -0500 Subject: [PATCH 09/11] finish it --- src/engine/platform/genesis.cpp | 5 +++++ src/engine/platform/genesisext.cpp | 1 + src/engine/platform/k053260.cpp | 4 ++++ src/engine/platform/msm6258.cpp | 4 ++++ src/engine/platform/opl.cpp | 12 ++++++++++++ src/engine/platform/qsound.cpp | 4 ++++ src/engine/platform/rf5c68.cpp | 4 ++++ src/engine/platform/sms.cpp | 6 ++++++ src/engine/platform/su.cpp | 4 ++++ src/engine/platform/vera.cpp | 4 ++++ src/engine/platform/x1_010.cpp | 1 + src/engine/platform/ym2608.cpp | 5 +++++ src/engine/platform/ym2610.cpp | 5 +++++ src/engine/platform/ym2610b.cpp | 5 +++++ src/engine/platform/ymz280b.cpp | 4 ++++ 15 files changed, 68 insertions(+) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index eb0c956bc..e8f3410fd 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1274,6 +1274,11 @@ DivMacroInt* DivPlatformGenesis::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformGenesis::getPan(int ch) { + if (ch>5) ch=5; + return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); +} + DivSamplePos DivPlatformGenesis::getSamplePos(int ch) { if (!chan[5].dacMode) return DivSamplePos(); if (ch<5) return DivSamplePos(); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 027cf1742..a1dacdc3d 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -801,6 +801,7 @@ DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) { } unsigned short DivPlatformGenesisExt::getPan(int ch) { + if (ch==csmChan) return 0; if (ch>=4+extChanOffs) return DivPlatformGenesis::getPan(ch-3); if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); return DivPlatformGenesis::getPan(ch); diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp index 5791d2426..17ff6db48 100644 --- a/src/engine/platform/k053260.cpp +++ b/src/engine/platform/k053260.cpp @@ -370,6 +370,10 @@ DivMacroInt* DivPlatformK053260::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformK053260::getPan(int ch) { + return parent->convertPanLinearToSplit(chan[ch].panning,8,7); +} + DivDispatchOscBuffer* DivPlatformK053260::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index e5de16456..92d38a980 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -282,6 +282,10 @@ DivMacroInt* DivPlatformMSM6258::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformMSM6258::getPan(int ch) { + return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); +} + DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 51e4131e2..1bb9dd26e 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1564,6 +1564,18 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformOPL::getPan(int ch) { + if (totalOutputs<=1) return 0; + if (chan[ch&(~1)].fourOp) { + if (ch&1) { + return ((chan[ch-1].pan<<7)&1)|(chan[ch-1].pan&1); + } else { + return ((chan[ch+1].pan<<7)&1)|(chan[ch+1].pan&1); + } + } + return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); +} + DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { if (oplType==759 || chipType==8950) { if (ch>=totalChans+1) return NULL; diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index d7f908f5b..5c98990b4 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -623,6 +623,10 @@ DivMacroInt* DivPlatformQSound::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformQSound::getPan(int ch) { + return parent->convertPanLinearToSplit(chan[ch].panning,8,32); +} + DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index 84522c74f..9319de5d3 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -322,6 +322,10 @@ DivMacroInt* DivPlatformRF5C68::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformRF5C68::getPan(int ch) { + return ((chan[ch].panning&15)<<8)|((chan[ch].panning&0xf0)>>4); +} + DivDispatchOscBuffer* DivPlatformRF5C68::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 76aa62fcb..7aa0455ca 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -452,6 +452,12 @@ DivMacroInt* DivPlatformSMS::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformSMS::getPan(int ch) { + if (!stereo) return 0; + unsigned char p=lastPan&(0x11<convertPanLinearToSplit(chan[ch].pan,8,255); +} + DivDispatchOscBuffer* DivPlatformSoundUnit::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index fa0446ca6..57d91b3b5 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -444,6 +444,10 @@ DivMacroInt* DivPlatformVERA::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformVERA::getPan(int ch) { + return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); +} + DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index e95d543a4..d1422439e 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -864,6 +864,7 @@ DivMacroInt* DivPlatformX1_010::getChanMacroInt(int ch) { } unsigned short DivPlatformX1_010::getPan(int ch) { + if (!stereo) return 0; return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); } diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index d88c1437f..7ca626d0f 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1461,6 +1461,11 @@ DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) { return &chan[ch].std; } +unsigned short DivPlatformYM2608::getPan(int ch) { + if (ch>=psgChanOffs && ch=psgChanOffs && ch=psgChanOffs && chconvertPanLinearToSplit(chan[ch].panning,8,15); +} + DivDispatchOscBuffer* DivPlatformYMZ280B::getOscBuffer(int ch) { return oscBuf[ch]; } From d240066df8f06d0bfe20efb326527f8629803133 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Aug 2023 03:25:38 -0500 Subject: [PATCH 10/11] fix issue #1371 --- src/engine/engine.cpp | 5 +++++ src/engine/engine.h | 3 +++ src/engine/platform/fmshared_OPN.h | 2 ++ src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesisext.cpp | 13 +++++++++++-- src/engine/platform/opl.cpp | 10 +++++----- src/engine/platform/ym2608.cpp | 2 +- src/engine/platform/ym2608ext.cpp | 13 +++++++++++-- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- src/engine/platform/ym2610bext.cpp | 15 ++++++++++++--- src/engine/platform/ym2610ext.cpp | 13 +++++++++++-- src/gui/pattern.cpp | 7 ++++--- 13 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b86e11ad6..3b1d9d6c5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1279,6 +1279,11 @@ DivChannelState* DivEngine::getChanState(int ch) { return &chan[ch]; } +unsigned short DivEngine::getChanPan(int ch) { + if (ch<0 || ch>=chans) return 0; + return disCont[dispatchOfChan[ch]].dispatch->getPan(dispatchChanOfChan[ch]); +} + void* DivEngine::getDispatchChanState(int ch) { if (ch<0 || ch>=chans) return NULL; return disCont[dispatchOfChan[ch]].dispatch->getChanState(dispatchChanOfChan[ch]); diff --git a/src/engine/engine.h b/src/engine/engine.h index 683289edd..653ca8ce3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -976,6 +976,9 @@ class DivEngine { // get macro interpreter DivMacroInt* getMacroInt(int chan); + // get channel panning + unsigned short getChanPan(int chan); + // get sample position DivSamplePos getSamplePos(int chan); diff --git a/src/engine/platform/fmshared_OPN.h b/src/engine/platform/fmshared_OPN.h index 8d3d059c4..32ea4c002 100644 --- a/src/engine/platform/fmshared_OPN.h +++ b/src/engine/platform/fmshared_OPN.h @@ -155,6 +155,7 @@ class DivPlatformOPN: public DivPlatformFMBase { unsigned int ayDiv; unsigned char csmChan; unsigned char lfoValue; + unsigned char lastExtChPan; unsigned short ssgVol; unsigned short fmVol; bool extSys, useCombo, fbAllOps; @@ -175,6 +176,7 @@ class DivPlatformOPN: public DivPlatformFMBase { ayDiv(a), csmChan(cc), lfoValue(0), + lastExtChPan(3), ssgVol(128), fmVol(256), extSys(isExtSys), diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index e8f3410fd..1564fa0cc 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1276,7 +1276,7 @@ DivMacroInt* DivPlatformGenesis::getChanMacroInt(int ch) { unsigned short DivPlatformGenesis::getPan(int ch) { if (ch>5) ch=5; - return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); + return ((chan[ch].pan&2)<<7)|(chan[ch].pan&1); } DivSamplePos DivPlatformGenesis::getSamplePos(int ch) { diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index a1dacdc3d..a147545cb 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -159,6 +159,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } } rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); + lastExtChPan=opChan[ch].pan; break; } case DIV_CMD_PITCH: { @@ -756,7 +757,7 @@ void DivPlatformGenesisExt::forceIns() { } rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); if (i==2) { - rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[0].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(lastExtChPan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } else { rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } @@ -803,7 +804,13 @@ DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) { unsigned short DivPlatformGenesisExt::getPan(int ch) { if (ch==csmChan) return 0; if (ch>=4+extChanOffs) return DivPlatformGenesis::getPan(ch-3); - if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + if (ch>=extChanOffs) { + if (extMode) { + return ((lastExtChPan&2)<<7)|(lastExtChPan&1); + } else { + return DivPlatformGenesis::getPan(extChanOffs); + } + } return DivPlatformGenesis::getPan(ch); } @@ -823,6 +830,8 @@ void DivPlatformGenesisExt::reset() { opChan[i].outVol=127; } + lastExtChPan=3; + // channel 3 mode immWrite(0x27,0x40); extMode=true; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 1bb9dd26e..c8c5c944c 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1566,14 +1566,14 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { unsigned short DivPlatformOPL::getPan(int ch) { if (totalOutputs<=1) return 0; - if (chan[ch&(~1)].fourOp) { + /*if (chan[ch&(~1)].fourOp) { if (ch&1) { - return ((chan[ch-1].pan<<7)&1)|(chan[ch-1].pan&1); + return ((chan[ch-1].pan&2)<<7)|(chan[ch-1].pan&1); } else { - return ((chan[ch+1].pan<<7)&1)|(chan[ch+1].pan&1); + return ((chan[ch+1].pan&2)<<7)|(chan[ch+1].pan&1); } - } - return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); + }*/ + return ((chan[ch].pan&1)<<8)|((chan[ch].pan&2)>>1); } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 7ca626d0f..93c09fd67 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1463,7 +1463,7 @@ DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) { unsigned short DivPlatformYM2608::getPan(int ch) { if (ch>=psgChanOffs && ch=4+extChanOffs) return DivPlatformYM2608::getPan(ch-3); - if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + if (ch>=extChanOffs) { + if (extMode) { + return ((lastExtChPan&2)<<7)|(lastExtChPan&1); + } else { + return DivPlatformYM2608::getPan(extChanOffs); + } + } return DivPlatformYM2608::getPan(ch); } @@ -772,7 +779,9 @@ void DivPlatformYM2608Ext::reset() { opChan[i].outVol=127; } - // channel 2 mode + lastExtChPan=3; + + // channel 3 mode immWrite(0x27,0x40); extMode=true; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index ea4a3bdeb..b7dcdb231 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -1423,7 +1423,7 @@ DivMacroInt* DivPlatformYM2610::getChanMacroInt(int ch) { unsigned short DivPlatformYM2610::getPan(int ch) { if (ch>=psgChanOffs && ch=psgChanOffs && ch=4+extChanOffs) return DivPlatformYM2610B::getPan(ch-3); - if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + if (ch>=extChanOffs) { + if (extMode) { + return ((lastExtChPan&2)<<7)|(lastExtChPan&1); + } else { + return DivPlatformYM2610B::getPan(extChanOffs); + } + } return DivPlatformYM2610B::getPan(ch); } @@ -762,7 +769,9 @@ void DivPlatformYM2610BExt::reset() { opChan[i].outVol=127; } - // channel 2 mode + lastExtChPan=3; + + // channel 3 mode immWrite(0x27,0x40); extMode=true; } diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index bf17659be..f0ab46990 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -152,6 +152,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { } } rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + lastExtChPan=opChan[ch].pan; break; } case DIV_CMD_PITCH: { @@ -695,7 +696,7 @@ void DivPlatformYM2610Ext::forceIns() { } rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); if (i==extChanOffs) { - rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[0].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(lastExtChPan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } else { rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } @@ -742,7 +743,13 @@ DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) { unsigned short DivPlatformYM2610Ext::getPan(int ch) { if (ch>=4+extChanOffs) return DivPlatformYM2610::getPan(ch-3); - if (ch>=extChanOffs) return ((opChan[0].pan<<7)&1)|(opChan[0].pan&1); + if (ch>=extChanOffs) { + if (extMode) { + return ((lastExtChPan&2)<<7)|(lastExtChPan&1); + } else { + return DivPlatformYM2610::getPan(extChanOffs); + } + } return DivPlatformYM2610::getPan(ch); } @@ -762,6 +769,8 @@ void DivPlatformYM2610Ext::reset() { opChan[i].outVol=127; } + lastExtChPan=3; + // channel 2 mode immWrite(0x27,0x40); extMode=true; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 63b575c6c..6c84c8d76 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -800,13 +800,14 @@ void FurnaceGUI::drawPattern() { if (e->isRunning()) { DivChannelState* cs=e->getChanState(i); - float stereoPan=(float)(e->convertPanSplitToLinearLR(cs->panL,cs->panR,256)-128)/128.0; + unsigned short chanPan=e->getChanPan(i); + float stereoPan=(float)(e->convertPanSplitToLinear(chanPan,8,256)-128)/128.0; switch (settings.channelVolStyle) { case 1: // simple - xRight=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f); + xRight=((float)(cs->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f); break; case 2: { // stereo - float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f); + float amount=((float)(cs->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f); xRight=0.5+amount*(1.0+MIN(0.0,stereoPan)); xLeft=0.5-amount*(1.0-MAX(0.0,stereoPan)); break; From d36b43415fc14b8b49d403bde7ce0878757036f7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Aug 2023 03:39:05 -0500 Subject: [PATCH 11/11] fix getPan() on VERA and MSM6258 --- src/engine/platform/msm6258.cpp | 2 +- src/engine/platform/vera.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 92d38a980..cb0ed05ed 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -283,7 +283,7 @@ DivMacroInt* DivPlatformMSM6258::getChanMacroInt(int ch) { } unsigned short DivPlatformMSM6258::getPan(int ch) { - return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); + return ((chan[ch].pan&2)<<7)|(chan[ch].pan&1); } DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 57d91b3b5..6d97c9c4e 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -445,7 +445,7 @@ DivMacroInt* DivPlatformVERA::getChanMacroInt(int ch) { } unsigned short DivPlatformVERA::getPan(int ch) { - return ((chan[ch].pan<<7)&1)|(chan[ch].pan&1); + return ((chan[ch].pan&1)<<8)|((chan[ch].pan&2)>>1); } DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) {