From fa0e5494adcc1662f601035559a50854ac359e19 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Wed, 12 Nov 2025 19:03:48 +0400 Subject: [PATCH 1/9] per-channel colors cherry-picked from ffcfc51a086de91ed67a5b144fec82b010b5acb8, but without the tri-state checkbox sorry for being impatient again, im bored and have nothing to do --- src/engine/song.cpp | 1 + src/engine/song.h | 2 ++ src/gui/channels.cpp | 25 ++++++++++++++++++++++++- src/gui/gui.cpp | 2 +- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 798c5f1c4..ff6e0e10e 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -642,6 +642,7 @@ void DivSong::findSubSongs(int chans) { memcpy(theCopy->chanShow,i->chanShow,DIV_MAX_CHANS*sizeof(bool)); memcpy(theCopy->chanShowChanOsc,i->chanShowChanOsc,DIV_MAX_CHANS*sizeof(bool)); memcpy(theCopy->chanCollapse,i->chanCollapse,DIV_MAX_CHANS); + memcpy(theCopy->chanColor,i->chanColor,DIV_MAX_CHANS*sizeof(unsigned int)); for (int k=0; kchanName[k]=i->chanName[k]; diff --git a/src/engine/song.h b/src/engine/song.h index 4b6e1b7fc..38da9b7d1 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -219,6 +219,7 @@ struct DivSubSong { unsigned char chanCollapse[DIV_MAX_CHANS]; String chanName[DIV_MAX_CHANS]; String chanShortName[DIV_MAX_CHANS]; + unsigned int chanColor[DIV_MAX_CHANS]; // song timestamps DivSongTimestamps ts; @@ -249,6 +250,7 @@ struct DivSubSong { chanShow[i]=true; chanShowChanOsc[i]=true; chanCollapse[i]=0; + chanColor[i]=0; } } }; diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 091a4103a..a1cc0eb4a 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -38,12 +38,13 @@ void FurnaceGUI::drawChannels() { //ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH)); } if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags,_("Channels"))) { - if (ImGui::BeginTable("ChannelList",5)) { + if (ImGui::BeginTable("ChannelList",6)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.0); ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); + ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed,0.0f); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); ImGui::Text(_("Pat")); @@ -52,6 +53,9 @@ void FurnaceGUI::drawChannels() { ImGui::TableNextColumn(); ImGui::TableNextColumn(); ImGui::Text(_("Name")); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::Text(_("Color")); for (int i=0; igetTotalChannelCount(); i++) { ImGui::PushID(i); ImGui::TableNextRow(); @@ -106,6 +110,25 @@ void FurnaceGUI::drawChannels() { if (ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->curSubSong->chanShortName[i])) { MARK_MODIFIED; } + ImGui::TableNextColumn(); + ImVec4 curColor=e->curSubSong->chanColor[i]?ImGui::ColorConvertU32ToFloat4(e->curSubSong->chanColor[i]):uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(i)]; + ImGui::ColorButton("##ChanColor",curColor); + if (ImGui::BeginPopupContextItem("##ChanColorEditPopup", ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::ColorPicker4("##ChanColorEdit", (float*)&curColor); + e->curSubSong->chanColor[i]=ImGui::ColorConvertFloat4ToU32(curColor); + MARK_MODIFIED; + ImGui::EndPopup(); + } + ImGui::SameLine(); + ImGui::BeginDisabled(e->curSubSong->chanColor[i]==0); + if (ImGui::Button(ICON_FA_REFRESH "##ChanColorReset")) { + e->curSubSong->chanColor[i]=0; + MARK_MODIFIED; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("Reset color")); + } + ImGui::EndDisabled(); ImGui::PopID(); } ImGui::EndTable(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e80930911..16376718a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -942,7 +942,7 @@ ImVec4 FurnaceGUI::channelColor(int ch) { return uiColors[GUI_COLOR_CHANNEL_BG]; break; case 1: - return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)]; + return e->curSubSong->chanColor[ch]?ImGui::ColorConvertU32ToFloat4(e->curSubSong->chanColor[ch]):uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)]; break; case 2: return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)]; From c681ac507e1f7a38c968a0e854bc045bedaacdc6 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Wed, 12 Nov 2025 20:33:41 +0400 Subject: [PATCH 2/9] chan color on chanosc channels todo: gradient. storing a gradient per channel would be absurd. my idea is when the bg is set to chan color, make the gradient bg transparent, then mix the grad with the color in get() --- src/gui/chanOsc.cpp | 5 ++++- src/gui/gui.cpp | 3 +++ src/gui/gui.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 30c854520..429cef6c2 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -360,7 +360,10 @@ void FurnaceGUI::drawChanOsc() { } } else { ImGui::SetNextItemWidth(400.0f*dpiScale); + ImGui::BeginDisabled(chanOscUseChanColor); ImGui::ColorPicker4(_("Color"),(float*)&chanOscColor); + ImGui::EndDisabled(); + ImGui::Checkbox(_("Set to channel color"), &chanOscUseChanColor); } ImGui::AlignTextToFramePadding(); @@ -793,7 +796,7 @@ void FurnaceGUI::drawChanOsc() { } } } - ImU32 color=ImGui::GetColorU32(chanOscColor); + ImU32 color=ImGui::GetColorU32(chanOscUseChanColor?channelColor(oscChans[i]):chanOscColor); if (chanOscUseGrad) { float xVal=computeGradPos(chanOscColorX,ch); float yVal=computeGradPos(chanOscColorY,ch); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 16376718a..db0c78d0e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8402,6 +8402,7 @@ void FurnaceGUI::syncState() { chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f); chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f); chanOscUseGrad=e->getConfBool("chanOscUseGrad",false); + chanOscUseChanColor=e->getConfBool("chanOscUseChanColor", false); chanOscGrad.fromString(e->getConfString("chanOscGrad","")); chanOscGrad.render(); @@ -8576,6 +8577,7 @@ void FurnaceGUI::commitState(DivConfig& conf) { conf.set("chanOscTextColorA",chanOscTextColor.w); conf.set("chanOscUseGrad",chanOscUseGrad); conf.set("chanOscGrad",chanOscGrad.toString()); + conf.set("chanOscUseChanColor",chanOscUseChanColor); // commit x-y osc state conf.set("xyOscXChannel",xyOscXChannel); @@ -9200,6 +9202,7 @@ FurnaceGUI::FurnaceGUI(): chanOscUseGrad(false), chanOscNormalize(false), chanOscRandomPhase(false), + chanOscUseChanColor(false), chanOscTextFormat("%c"), chanOscColor(1.0f,1.0f,1.0f,1.0f), chanOscTextColor(1.0f,1.0f,1.0f,0.75f), diff --git a/src/gui/gui.h b/src/gui/gui.h index ec2cb5766..78049de8d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2698,7 +2698,7 @@ class FurnaceGUI { // per-channel oscilloscope int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY, chanOscCenterStrat; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify, chanOscLineSize; - bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize, chanOscRandomPhase; + bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize, chanOscRandomPhase, chanOscUseChanColor; String chanOscTextFormat; ImVec4 chanOscColor, chanOscTextColor; Gradient2D chanOscGrad; From b5fc6ff39dcdb94d303aba0dc3f2babfd78223de Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Thu, 13 Nov 2025 00:32:29 +0400 Subject: [PATCH 3/9] fix oscref_channel not taking into account hidden channels i love when children annoy me then i get ignored for the rest of the day --- src/gui/chanOsc.cpp | 8 ++++---- src/gui/gui.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 429cef6c2..0c696c314 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -56,7 +56,7 @@ static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) { } } -float FurnaceGUI::computeGradPos(int type, int chan) { +float FurnaceGUI::computeGradPos(int type, int chan, int totalChans) { switch (type) { case GUI_OSCREF_NONE: return 0.0f; @@ -74,7 +74,7 @@ float FurnaceGUI::computeGradPos(int type, int chan) { return chanOscVol[chan]; break; case GUI_OSCREF_CHANNEL: - return (float)chan/(float)(e->getTotalChannelCount()-1); + return (float)chan/(float)(totalChans-1); break; case GUI_OSCREF_BRIGHT: return chanOscBright[chan]; @@ -798,8 +798,8 @@ void FurnaceGUI::drawChanOsc() { } ImU32 color=ImGui::GetColorU32(chanOscUseChanColor?channelColor(oscChans[i]):chanOscColor); if (chanOscUseGrad) { - float xVal=computeGradPos(chanOscColorX,ch); - float yVal=computeGradPos(chanOscColorY,ch); + float xVal=computeGradPos(chanOscColorX,ch,oscBufs.size()); + float yVal=computeGradPos(chanOscColorY,ch,oscBufs.size()); xVal=CLAMP(xVal,0.0f,1.0f); yVal=CLAMP(yVal,0.0f,1.0f); diff --git a/src/gui/gui.h b/src/gui/gui.h index 78049de8d..03060ba6e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -3102,7 +3102,7 @@ class FurnaceGUI { bool importConfig(String path); bool exportConfig(String path); - float computeGradPos(int type, int chan); + float computeGradPos(int type, int chan, int totalChans); void resetColors(); void resetKeybinds(); From ae4476bd7f5d2b00b365882082137d67938469a1 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Thu, 13 Nov 2025 13:48:36 +0400 Subject: [PATCH 4/9] auto column types begone what was i thinking??? --- src/gui/chanOsc.cpp | 41 +++++++++-------------------------------- src/gui/gui.cpp | 6 +++--- src/gui/gui.h | 5 +++-- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 0c696c314..49aaceecc 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -77,7 +77,7 @@ float FurnaceGUI::computeGradPos(int type, int chan, int totalChans) { return (float)chan/(float)(totalChans-1); break; case GUI_OSCREF_BRIGHT: - return chanOscBright[chan]; + return chanOscBright[chan]; // this array is set to only 0 (???) break; case GUI_OSCREF_NOTE_TRIGGER: return keyHit1[chan]; @@ -143,10 +143,12 @@ void FurnaceGUI::drawChanOsc() { ImGui::Text(_("Columns")); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::BeginDisabled(chanOscAutoCols); if (ImGui::InputInt("##COSColumns",&chanOscCols,1,3)) { if (chanOscCols<1) chanOscCols=1; if (chanOscCols>64) chanOscCols=64; } + ImGui::EndDisabled(); ImGui::TableNextColumn(); ImGui::Text(_("Size (ms)")); @@ -160,18 +162,7 @@ void FurnaceGUI::drawChanOsc() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); - ImGui::Text(_("Automatic columns")); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - const char* previewColType=autoColsTypes[chanOscAutoColsType&3]; - if (ImGui::BeginCombo("##AutoCols",previewColType)) { - for (int j=0; j<4; j++) { - const bool isSelected=(chanOscAutoColsType==j); - if (ImGui::Selectable(autoColsTypes[j],isSelected)) chanOscAutoColsType=j; - if (isSelected) ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } + ImGui::Checkbox(_("Automatic columns"),&chanOscAutoCols); ImGui::TableNextColumn(); if (ImGui::Checkbox(_("Center waveform"),&chanOscWaveCorr)) { @@ -423,7 +414,7 @@ void FurnaceGUI::drawChanOsc() { for (int i=0; igetOscBuffer(i); if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) { - oscBufs.push_back(buf); + oscBufs.push_back(buf); // isnt this odd how there are 3 vectors of the same size? oscFFTs.push_back(&chanOscChan[i]); oscChans.push_back(i); } @@ -593,25 +584,11 @@ void FurnaceGUI::drawChanOsc() { } } chanOscWorkPool->wait(); - - // 0: none - // 1: sqrt(chans) - // 2: sqrt(chans+1) - // 3: sqrt(chans)+1 - switch (chanOscAutoColsType) { - case 1: - chanOscCols=sqrt(oscChans.size()); - break; - case 2: - chanOscCols=sqrt(oscChans.size()+1); - break; - case 3: - chanOscCols=sqrt(oscChans.size())+1; - break; - } - if (chanOscCols<1) chanOscCols=1; - if (chanOscCols>64) chanOscCols=64; + if (chanOscAutoCols) { + chanOscCols=sqrt(oscBufs.size()); + if (chanOscCols>64) chanOscCols=64; + } int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; // render diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index db0c78d0e..2284d0ea1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8379,7 +8379,7 @@ void FurnaceGUI::syncState() { pianoLabelsMode=e->getConfInt("pianoLabelsMode",pianoLabelsMode); chanOscCols=e->getConfInt("chanOscCols",3); - chanOscAutoColsType=e->getConfInt("chanOscAutoColsType",0); + chanOscAutoCols=e->getConfBool("chanOscAutoColsType",0); chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER); chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER); chanOscCenterStrat=e->getConfInt("chanOscCenterStrat",1); @@ -8553,7 +8553,7 @@ void FurnaceGUI::commitState(DivConfig& conf) { // commit per-chan osc state conf.set("chanOscCols",chanOscCols); - conf.set("chanOscAutoColsType",chanOscAutoColsType); + conf.set("chanOscAutoColsType",chanOscAutoCols); conf.set("chanOscColorX",chanOscColorX); conf.set("chanOscColorY",chanOscColorY); conf.set("chanOscCenterStrat",chanOscCenterStrat); @@ -9187,7 +9187,6 @@ FurnaceGUI::FurnaceGUI(): oscInput1(0.0f), oscZoomSlider(false), chanOscCols(3), - chanOscAutoColsType(0), chanOscColorX(GUI_OSCREF_CENTER), chanOscColorY(GUI_OSCREF_CENTER), chanOscCenterStrat(1), @@ -9203,6 +9202,7 @@ FurnaceGUI::FurnaceGUI(): chanOscNormalize(false), chanOscRandomPhase(false), chanOscUseChanColor(false), + chanOscAutoCols(false), chanOscTextFormat("%c"), chanOscColor(1.0f,1.0f,1.0f,1.0f), chanOscTextColor(1.0f,1.0f,1.0f,0.75f), diff --git a/src/gui/gui.h b/src/gui/gui.h index 03060ba6e..024d40c33 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2696,9 +2696,10 @@ class FurnaceGUI { bool oscZoomSlider; // per-channel oscilloscope - int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY, chanOscCenterStrat; + int chanOscCols, chanOscColorX, chanOscColorY, chanOscCenterStrat; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify, chanOscLineSize; - bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize, chanOscRandomPhase, chanOscUseChanColor; + bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad; + bool chanOscNormalize, chanOscRandomPhase, chanOscUseChanColor, chanOscAutoCols; String chanOscTextFormat; ImVec4 chanOscColor, chanOscTextColor; Gradient2D chanOscGrad; From 44ec4f70da65c6e1a3a28633f80a1dd1cc7aa249 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Thu, 13 Nov 2025 18:21:25 +0400 Subject: [PATCH 5/9] channel color for gradient part 1 very not work --- src/gui/chanOsc.cpp | 62 ++++++++++++++++++++++++++++++++++++++------ src/gui/gradient.cpp | 17 ++++++++---- src/gui/gui.cpp | 6 ++--- src/gui/gui.h | 4 +-- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 49aaceecc..8e421d23b 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -341,9 +341,30 @@ void FurnaceGUI::drawChanOsc() { } ImGui::TableNextColumn(); - if (ImGui::ColorEdit4(_("Background"),(float*)&chanOscGrad.bgColor)) { + ImGui::Text(_("Background:")); + ImGui::Indent(); + if (ImGui::RadioButton(_("Solid color"),chanOscColorMode==0)) { + chanOscColorMode=0; updateChanOscGradTex=true; } + if (chanOscColorMode==0) { + ImGui::Indent(); + if (ImGui::ColorEdit4(_("Color"),(float*)&chanOscGrad.bgColor)) { + updateChanOscGradTex=true; + } + ImGui::Unindent(); + } + if (ImGui::RadioButton(_("Channel color"),chanOscColorMode==1)) { + chanOscColorMode=1; + chanOscGrad.bgColor.x=0.0f; + chanOscGrad.bgColor.y=0.0f; + chanOscGrad.bgColor.z=0.0f; + chanOscGrad.bgColor.w=0.0f; + updateChanOscGradTex=true; + } + // in preparation of image texture + ImGui::Unindent(); + ImGui::Combo(_("X Axis##AxisX"),&chanOscColorX,LocalizedComboGetter,chanOscRefs,GUI_OSCREF_MAX); ImGui::Combo(_("Y Axis##AxisY"),&chanOscColorY,LocalizedComboGetter,chanOscRefs,GUI_OSCREF_MAX); @@ -351,10 +372,13 @@ void FurnaceGUI::drawChanOsc() { } } else { ImGui::SetNextItemWidth(400.0f*dpiScale); - ImGui::BeginDisabled(chanOscUseChanColor); - ImGui::ColorPicker4(_("Color"),(float*)&chanOscColor); + bool chanOscColorModeB=chanOscColorMode; + ImGui::BeginDisabled(chanOscColorModeB); + ImGui::ColorEdit4(_("Color"),(float*)&chanOscColor); ImGui::EndDisabled(); - ImGui::Checkbox(_("Set to channel color"), &chanOscUseChanColor); + if (ImGui::Checkbox(_("Set to channel color"), &chanOscColorModeB)) { + chanOscColorMode=chanOscColorModeB; + } } ImGui::AlignTextToFramePadding(); @@ -773,7 +797,15 @@ void FurnaceGUI::drawChanOsc() { } } } - ImU32 color=ImGui::GetColorU32(chanOscUseChanColor?channelColor(oscChans[i]):chanOscColor); + ImU32 color; + switch (chanOscColorMode) { + case 0: + color=ImGui::GetColorU32(chanOscColor); + break; + case 1: + color=ImGui::GetColorU32(channelColor(oscChans[i])); + break; + } if (chanOscUseGrad) { float xVal=computeGradPos(chanOscColorX,ch,oscBufs.size()); float yVal=computeGradPos(chanOscColorY,ch,oscBufs.size()); @@ -781,7 +813,22 @@ void FurnaceGUI::drawChanOsc() { xVal=CLAMP(xVal,0.0f,1.0f); yVal=CLAMP(yVal,0.0f,1.0f); - color=chanOscGrad.get(xVal,1.0f-yVal); + switch (chanOscColorMode) { + case 0: + color=chanOscGrad.get(xVal,1.0f-yVal); + break; + case 1: + color=ImAlphaBlendColors(color,chanOscGrad.get(xVal,1.0f-yVal)); + break; + } + // char buf[256]; + // snprintf(buf, 256, "%f:%f",xVal,yVal); + // dl->AddText(inRect.Min,-1,buf); + // dl->AddCircleFilled( + // ImVec2( + // ImLerp(inRect.Min.x,inRect.Max.x,xVal), + // ImLerp(inRect.Min.y,inRect.Max.y,1.0f-yVal) + // ), 2, 0xffff0000); } if (rend->supportsDrawOsc() && settings.shaderOsc) { @@ -886,10 +933,9 @@ void FurnaceGUI::drawChanOsc() { text+=fmt::sprintf("%s",noteName(chanState->note+60)); break; } - case 'l': { + case 'l': text+='\n'; break; - } case '%': text+='%'; break; diff --git a/src/gui/gradient.cpp b/src/gui/gradient.cpp index b33564f0b..be4e08eb1 100644 --- a/src/gui/gradient.cpp +++ b/src/gui/gradient.cpp @@ -109,21 +109,28 @@ void Gradient2D::render() { if (dist>1) dist=1; ImU32 shadeColor=ImGui::ColorConvertFloat4ToU32( + // ps: multiplying dist to the color channels fixes old + // mixing, but breaks if the bg is 0. and vice versa (not + // multiplying fixes 0 bg mixing) ImVec4( i.color.x*i.color.w*dist, i.color.y*i.color.w*dist, i.color.z*i.color.w*dist, - 1.0f + 1.0f*dist // this idk ) ); ImU32 origColor=g[j*width+k]; + // note: this really breaks the color mixing if theres a background + // and the bitshifts are necessary to avoid overflow (but prob only for alpha) + // this needs to be redone, its a temporary proof-of-concept solution anyway g[j*width+k]=( - (MIN( 0xff, (origColor&0xff) + (shadeColor&0xff) )) | // R - (MIN( 0xff00, (origColor&0xff00) + (shadeColor&0xff00) )) | // G - (MIN(0xff0000,(origColor&0xff0000)+(shadeColor&0xff0000))) | // B - (origColor&0xff000000) // A + (MIN(0xff,(origColor &0xff)+(shadeColor &0xff)) ) | // R + (MIN(0xff,(origColor>> 8&0xff)+(shadeColor>> 8&0xff))<< 8) | // G + (MIN(0xff,(origColor>>16&0xff)+(shadeColor>>16&0xff))<<16) | // B + (MIN(0xff,(origColor>>24&0xff)+(shadeColor>>24&0xff))<<24) // A ); + // ps 2: replacing this with ImAlphaBlendColors doesnt work } } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2284d0ea1..ab04f7830 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8402,7 +8402,7 @@ void FurnaceGUI::syncState() { chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f); chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f); chanOscUseGrad=e->getConfBool("chanOscUseGrad",false); - chanOscUseChanColor=e->getConfBool("chanOscUseChanColor", false); + chanOscColorMode=e->getConfInt("chanOscColorMode",0); chanOscGrad.fromString(e->getConfString("chanOscGrad","")); chanOscGrad.render(); @@ -8577,7 +8577,7 @@ void FurnaceGUI::commitState(DivConfig& conf) { conf.set("chanOscTextColorA",chanOscTextColor.w); conf.set("chanOscUseGrad",chanOscUseGrad); conf.set("chanOscGrad",chanOscGrad.toString()); - conf.set("chanOscUseChanColor",chanOscUseChanColor); + conf.set("chanOscColorMode",chanOscColorMode); // commit x-y osc state conf.set("xyOscXChannel",xyOscXChannel); @@ -9190,6 +9190,7 @@ FurnaceGUI::FurnaceGUI(): chanOscColorX(GUI_OSCREF_CENTER), chanOscColorY(GUI_OSCREF_CENTER), chanOscCenterStrat(1), + chanOscColorMode(0), chanOscWindowSize(20.0f), chanOscTextX(0.0f), chanOscTextY(0.0f), @@ -9201,7 +9202,6 @@ FurnaceGUI::FurnaceGUI(): chanOscUseGrad(false), chanOscNormalize(false), chanOscRandomPhase(false), - chanOscUseChanColor(false), chanOscAutoCols(false), chanOscTextFormat("%c"), chanOscColor(1.0f,1.0f,1.0f,1.0f), diff --git a/src/gui/gui.h b/src/gui/gui.h index 024d40c33..0a0076e57 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2696,10 +2696,10 @@ class FurnaceGUI { bool oscZoomSlider; // per-channel oscilloscope - int chanOscCols, chanOscColorX, chanOscColorY, chanOscCenterStrat; + int chanOscCols, chanOscColorX, chanOscColorY, chanOscCenterStrat, chanOscColorMode; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify, chanOscLineSize; bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad; - bool chanOscNormalize, chanOscRandomPhase, chanOscUseChanColor, chanOscAutoCols; + bool chanOscNormalize, chanOscRandomPhase, chanOscAutoCols; String chanOscTextFormat; ImVec4 chanOscColor, chanOscTextColor; Gradient2D chanOscGrad; From f93dd893c87cd58e3d210036cd4b11ffa9e03b63 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Fri, 14 Nov 2025 18:37:23 +0400 Subject: [PATCH 6/9] chan color in piano and real volume chan feedback (w/ gamma correction) now i can recreate the bee video! :> --- src/gui/gui.cpp | 2 +- src/gui/gui.h | 8 +++++++- src/gui/pattern.cpp | 20 +++++++++++++++++--- src/gui/piano.cpp | 16 ++++++++-------- src/gui/settings.cpp | 18 +++++++++++++++++- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ab04f7830..f6861edca 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -9385,7 +9385,7 @@ FurnaceGUI::FurnaceGUI(): memset(lastAudioLoads,0,sizeof(float)*120); - memset(pianoKeyHit,0,sizeof(float)*180); + memset(pianoKeyHit,0,sizeof(pianoKeyState)*180); // posiblly repace with a for loop memset(pianoKeyPressed,0,sizeof(bool)*180); memset(queryReplaceEffectMode,0,sizeof(int)*8); diff --git a/src/gui/gui.h b/src/gui/gui.h index 0a0076e57..e83bf0900 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2090,6 +2090,7 @@ class FurnaceGUI { int sampleImportInstDetune; int mixerStyle; int mixerLayout; + float channelFeedbackGamma; String mainFontPath; String headFontPath; String patFontPath; @@ -2344,6 +2345,7 @@ class FurnaceGUI { sampleImportInstDetune(0), mixerStyle(1), mixerLayout(0), + channelFeedbackGamma(1.0f), mainFontPath(""), headFontPath(""), patFontPath(""), @@ -2839,7 +2841,11 @@ class FurnaceGUI { int pianoOctaves, pianoOctavesEdit; bool pianoOptions, pianoSharePosition, pianoOptionsSet; - float pianoKeyHit[180]; + struct pianoKeyState { + float value; + int chan; + }; + pianoKeyState pianoKeyHit[180]; bool pianoKeyPressed[180]; bool pianoReadonly; int pianoOffset, pianoOffsetEdit; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 46f799c27..65f45cb0c 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -666,7 +666,8 @@ void FurnaceGUI::drawPattern() { if (!muted) { int note=e->getChanState(i)->note+60; if (note>=0 && note<180) { - pianoKeyHit[note]=1.0; + pianoKeyHit[note].value=1.0; + pianoKeyHit[note].chan=i; } } } @@ -679,7 +680,8 @@ void FurnaceGUI::drawPattern() { if (!muted) { int note=e->getChanState(i)->note+60; if (note>=0 && note<180) { - pianoKeyHit[note]=amount; + pianoKeyHit[note].value=amount; + pianoKeyHit[note].chan=i; } } } else if (settings.channelFeedbackStyle==3 && e->isRunning()) { @@ -688,7 +690,19 @@ void FurnaceGUI::drawPattern() { if (!muted) { int note=e->getChanState(i)->note+60; if (note>=0 && note<180) { - pianoKeyHit[note]=active?1.0f:0.0f; + pianoKeyHit[note].value=active?1.0f:0.0f; + pianoKeyHit[note].chan=i; + } + } + } else if (settings.channelFeedbackStyle==4 && e->isRunning()) { + float amount=powf(chanOscVol[i],settings.channelFeedbackGamma); + if (!e->getChanState(i)->keyOn) amount=0.0f; + keyHit[i]=amount*0.2f; + if (!muted) { + int note=e->getChanState(i)->note+60; + if (note>=0 && note<180) { + pianoKeyHit[note].value=amount; + pianoKeyHit[note].chan=i; } } } diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index 6bcfbdda1..3df1c187e 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -297,12 +297,12 @@ void FurnaceGUI::drawPiano() { int note=i+12*off; if (note<0) continue; if (note>=180) continue; - float pkh=pianoKeyHit[note]; + float pkh=pianoKeyHit[note].value; ImVec4 color=isTopKey[i%12]?uiColors[GUI_COLOR_PIANO_KEY_TOP]:uiColors[GUI_COLOR_PIANO_KEY_BOTTOM]; if (pianoKeyPressed[note]) { color=isTopKey[i%12]?uiColors[GUI_COLOR_PIANO_KEY_TOP_ACTIVE]:uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE]; } else { - ImVec4 colorHit=isTopKey[i%12]?uiColors[GUI_COLOR_PIANO_KEY_TOP_HIT]:uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_HIT]; + ImVec4 colorHit=1?channelColor(pianoKeyHit[note].chan):(isTopKey[i%12]?uiColors[GUI_COLOR_PIANO_KEY_TOP_HIT]:uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_HIT]); color.x+=(colorHit.x-color.x)*pkh; color.y+=(colorHit.y-color.y)*pkh; color.z+=(colorHit.z-color.z)*pkh; @@ -355,12 +355,12 @@ void FurnaceGUI::drawPiano() { if (note<0) continue; if (note>=180) continue; - float pkh=pianoKeyHit[note]; + float pkh=pianoKeyHit[note].value; ImVec4 color=uiColors[GUI_COLOR_PIANO_KEY_BOTTOM]; if (pianoKeyPressed[note]) { color=uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE]; } else { - ImVec4 colorHit=uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_HIT]; + ImVec4 colorHit=1?channelColor(pianoKeyHit[note].chan):uiColors[GUI_COLOR_PIANO_KEY_BOTTOM_HIT]; color.x+=(colorHit.x-color.x)*pkh; color.y+=(colorHit.y-color.y)*pkh; color.z+=(colorHit.z-color.z)*pkh; @@ -383,12 +383,12 @@ void FurnaceGUI::drawPiano() { int note=topKeyNotes[j]+12*(i+off); if (note<0) continue; if (note>=180) continue; - float pkh=pianoKeyHit[note]; + float pkh=pianoKeyHit[note].value; ImVec4 color=uiColors[GUI_COLOR_PIANO_KEY_TOP]; if (pianoKeyPressed[note]) { color=uiColors[GUI_COLOR_PIANO_KEY_TOP_ACTIVE]; } else { - ImVec4 colorHit=uiColors[GUI_COLOR_PIANO_KEY_TOP_HIT]; + ImVec4 colorHit=1?channelColor(pianoKeyHit[note].chan):uiColors[GUI_COLOR_PIANO_KEY_TOP_HIT]; color.x+=(colorHit.x-color.x)*pkh; color.y+=(colorHit.y-color.y)*pkh; color.z+=(colorHit.z-color.z)*pkh; @@ -407,8 +407,8 @@ void FurnaceGUI::drawPiano() { const float reduction=ImGui::GetIO().DeltaTime*60.0f*0.12; for (int i=0; i<180; i++) { - pianoKeyHit[i]-=reduction; - if (pianoKeyHit[i]<0) pianoKeyHit[i]=0; + pianoKeyHit[i].value-=reduction; + if (pianoKeyHit[i].value<0) pianoKeyHit[i].value=0; } } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 7c785d169..ea614b76e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3505,6 +3505,19 @@ void FurnaceGUI::drawSettings() { settings.channelFeedbackStyle=3; settingsChanged=true; } + if (ImGui::RadioButton(_("Volume (Real)##CHF4"),settings.channelFeedbackStyle==4)) { + settings.channelFeedbackStyle=4; + settingsChanged=true; + } + if (settings.channelFeedbackStyle==4) { + ImGui::Indent(); + if (ImGui::SliderFloat(_("Gamma##CHF"),&settings.channelFeedbackGamma,0.0f,2.0f)) { + if (settings.channelFeedbackGamma<0.0f) settings.channelFeedbackGamma=0.0f; + if (settings.channelFeedbackGamma>2.0f) settings.channelFeedbackGamma=2.0f; + settingsChanged=true; + } + ImGui::Unindent(); + } ImGui::Unindent(); ImGui::Text(_("Channel font:")); @@ -5123,6 +5136,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { settings.channelStyle=conf.getInt("channelStyle",1); settings.channelVolStyle=conf.getInt("channelVolStyle",0); settings.channelFeedbackStyle=conf.getInt("channelFeedbackStyle",1); + settings.channelFeedbackGamma=conf.getFloat("channelFeedbackGamma",1.0f); settings.channelFont=conf.getInt("channelFont",1); settings.channelTextCenter=conf.getInt("channelTextCenter",1); @@ -5399,7 +5413,8 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { clampSetting(settings.channelTextColors,0,2); clampSetting(settings.channelStyle,0,5); clampSetting(settings.channelVolStyle,0,4); - clampSetting(settings.channelFeedbackStyle,0,3); + clampSetting(settings.channelFeedbackStyle,0,4); + clampSetting(settings.channelFeedbackGamma,0.0f,2.0f); clampSetting(settings.channelFont,0,1); clampSetting(settings.channelTextCenter,0,1); clampSetting(settings.maxRecentFile,0,30); @@ -5711,6 +5726,7 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { conf.set("channelStyle",settings.channelStyle); conf.set("channelVolStyle",settings.channelVolStyle); conf.set("channelFeedbackStyle",settings.channelFeedbackStyle); + conf.set("channelFeedbackGamma",settings.channelFeedbackGamma); conf.set("channelFont",settings.channelFont); conf.set("channelTextCenter",settings.channelTextCenter); From 14fd246edb882e8cf808fb26578b5c4890760888 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Sun, 16 Nov 2025 11:15:21 +0400 Subject: [PATCH 7/9] improve chanosc note text and chan volume feedback --- src/gui/chanOsc.cpp | 13 ++++++++++--- src/gui/pattern.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 8e421d23b..b298f3dd1 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -928,9 +928,16 @@ void FurnaceGUI::drawChanOsc() { } case 'n': { DivChannelState* chanState=e->getChanState(ch); - if (chanState==NULL || !(chanState->keyOn)) break; - // no more conversion necessary after the note/octave unification :> - text+=fmt::sprintf("%s",noteName(chanState->note+60)); + // ik its pretty hacky but it works + // the templated stuff is after the member we need to access so it shouldnt matter + // and no segfaults should occur + SharedChannel* chan=(SharedChannel*)e->getDispatchChanState(ch); + if (chanState==NULL || chan==NULL || !chan->active) { + text+="---"; + } else { + // no more conversion necessary after the note/octave unification :> + text+=fmt::sprintf("%s",noteName(chanState->note+60)); + } break; } case 'l': diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 65f45cb0c..b152a4f87 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -675,9 +675,9 @@ void FurnaceGUI::drawPattern() { } if (settings.channelFeedbackStyle==2 && e->isRunning()) { float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i)); - if (!e->getChanState(i)->keyOn) amount=0.0f; + if (e->getChanState(i)->keyOff) amount=0.0f; keyHit[i]=amount*0.2f; - if (!muted) { + if (!muted && e->getChanState(i)->keyOn) { int note=e->getChanState(i)->note+60; if (note>=0 && note<180) { pianoKeyHit[note].value=amount; @@ -696,9 +696,9 @@ void FurnaceGUI::drawPattern() { } } else if (settings.channelFeedbackStyle==4 && e->isRunning()) { float amount=powf(chanOscVol[i],settings.channelFeedbackGamma); - if (!e->getChanState(i)->keyOn) amount=0.0f; + if (e->getChanState(i)->keyOff) amount=0.0f; keyHit[i]=amount*0.2f; - if (!muted) { + if (!muted && e->getChanState(i)->keyOn) { int note=e->getChanState(i)->note+60; if (note>=0 && note<180) { pianoKeyHit[note].value=amount; From afb9b07f3bfcbada66d20ee348c6485e6f0ee0a7 Mon Sep 17 00:00:00 2001 From: Eknous-P Date: Wed, 19 Nov 2025 14:05:00 +0400 Subject: [PATCH 8/9] join the vectors into one --- src/gui/chanOsc.cpp | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index b298f3dd1..325d4c746 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -87,10 +87,6 @@ float FurnaceGUI::computeGradPos(int type, int chan, int totalChans) { } void FurnaceGUI::calcChanOsc() { - std::vector oscBufs; - std::vector oscFFTs; - std::vector oscChans; - int chans=e->getTotalChannelCount(); for (int i=0; i oscBufs; - std::vector oscFFTs; - std::vector oscChans; + struct OscData { + DivDispatchOscBuffer* buf; + ChanOscStatus* fft; + int chan; + }; + std::vector oscData; int chans=e->getTotalChannelCount(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -438,18 +437,16 @@ void FurnaceGUI::drawChanOsc() { for (int i=0; igetOscBuffer(i); if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) { - oscBufs.push_back(buf); // isnt this odd how there are 3 vectors of the same size? - oscFFTs.push_back(&chanOscChan[i]); - oscChans.push_back(i); + oscData.push_back({buf,&chanOscChan[i],i}); } } // process - for (size_t i=0; irelatedBuf=oscBufs[i]; - fft_->relatedCh=oscChans[i]; + fft_->relatedBuf=oscData[i].buf; + fft_->relatedCh=oscData[i].chan; if (fft_->relatedBuf!=NULL) { // prepare @@ -610,19 +607,19 @@ void FurnaceGUI::drawChanOsc() { chanOscWorkPool->wait(); if (chanOscAutoCols) { - chanOscCols=sqrt(oscBufs.size()); + chanOscCols=sqrt(oscData.size()); if (chanOscCols>64) chanOscCols=64; } - int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; + int rows=(oscData.size()+(chanOscCols-1))/chanOscCols; // render - for (size_t i=0; i Date: Fri, 21 Nov 2025 10:51:03 -0500 Subject: [PATCH 9/9] consider channel color for the rest of functions --- src/engine/engine.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 0d00756ec..70ee0a559 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -642,6 +642,7 @@ void DivEngine::copyChannel(int src, int dest) { curSubSong->chanShow[dest]=curSubSong->chanShow[src]; curSubSong->chanShowChanOsc[dest]=curSubSong->chanShowChanOsc[src]; curSubSong->chanCollapse[dest]=curSubSong->chanCollapse[src]; + curSubSong->chanColor[dest]=curSubSong->chanColor[src]; } void DivEngine::swapChannels(int src, int dest) { @@ -670,17 +671,21 @@ void DivEngine::swapChannels(int src, int dest) { bool prevChanShow=curSubSong->chanShow[src]; bool prevChanShowChanOsc=curSubSong->chanShowChanOsc[src]; unsigned char prevChanCollapse=curSubSong->chanCollapse[src]; + unsigned int prevChanColor=curSubSong->chanColor[src]; curSubSong->chanName[src]=curSubSong->chanName[dest]; curSubSong->chanShortName[src]=curSubSong->chanShortName[dest]; curSubSong->chanShow[src]=curSubSong->chanShow[dest]; curSubSong->chanShowChanOsc[src]=curSubSong->chanShowChanOsc[dest]; curSubSong->chanCollapse[src]=curSubSong->chanCollapse[dest]; + curSubSong->chanColor[src]=curSubSong->chanColor[dest]; + curSubSong->chanName[dest]=prevChanName; curSubSong->chanShortName[dest]=prevChanShortName; curSubSong->chanShow[dest]=prevChanShow; curSubSong->chanShowChanOsc[dest]=prevChanShowChanOsc; curSubSong->chanCollapse[dest]=prevChanCollapse; + curSubSong->chanColor[dest]=prevChanColor; } void DivEngine::stompChannel(int ch) { @@ -695,6 +700,7 @@ void DivEngine::stompChannel(int ch) { curSubSong->chanShow[ch]=true; curSubSong->chanShowChanOsc[ch]=true; curSubSong->chanCollapse[ch]=false; + curSubSong->chanColor[ch]=0; } void DivEngine::changeSong(size_t songIndex) { @@ -860,6 +866,8 @@ int DivEngine::duplicateSubSong(int index) { memcpy(theCopy->chanShowChanOsc,theOrig->chanShowChanOsc,DIV_MAX_CHANS*sizeof(bool)); memcpy(theCopy->chanCollapse,theOrig->chanCollapse,DIV_MAX_CHANS); + memcpy(theCopy->chanColor,theOrig->chanColor,DIV_MAX_CHANS*sizeof(unsigned int)); + for (int i=0; ichanName[i]=theOrig->chanName[i]; theCopy->chanShortName[i]=theOrig->chanShortName[i]; @@ -1236,6 +1244,7 @@ bool DivEngine::duplicateSystem(int index, bool pat, bool end) { i->chanCollapse[destChan+j]=i->chanCollapse[srcChan+j]; i->chanName[destChan+j]=i->chanName[srcChan+j]; i->chanShortName[destChan+j]=i->chanShortName[srcChan+j]; + i->chanColor[destChan+j]=i->chanColor[srcChan+j]; for (int k=0; kpat[srcChan+j].data[k]!=NULL) { i->pat[srcChan+j].data[k]->copyOn(i->pat[destChan+j].getPattern(k,true)); @@ -1387,6 +1396,7 @@ void DivEngine::swapSystemUnsafe(int src, int dest, bool preserveOrder) { bool prevChanShow[DIV_MAX_CHANS]; bool prevChanShowChanOsc[DIV_MAX_CHANS]; unsigned char prevChanCollapse[DIV_MAX_CHANS]; + unsigned int prevChanColor[DIV_MAX_CHANS]; for (int j=0; jchanShow[j]; prevChanShowChanOsc[j]=song.subsong[i]->chanShowChanOsc[j]; prevChanCollapse[j]=song.subsong[i]->chanCollapse[j]; + prevChanColor[j]=song.subsong[i]->chanColor[j]; } for (int j=0; jchanShow[j]=prevChanShow[swappedChannels[j]]; song.subsong[i]->chanShowChanOsc[j]=prevChanShowChanOsc[swappedChannels[j]]; song.subsong[i]->chanCollapse[j]=prevChanCollapse[swappedChannels[j]]; + song.subsong[i]->chanColor[j]=prevChanColor[swappedChannels[j]]; } } }