diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 92d748df4..cb05f7678 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -649,6 +649,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) { @@ -677,17 +678,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) { @@ -702,6 +707,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) { @@ -789,6 +795,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]; @@ -1220,6 +1228,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)); @@ -1372,6 +1381,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]]; } } } diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 4045bd572..4bf04aebb 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -845,6 +845,7 @@ void DivSong::findSubSongs() { 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 eb6dfc804..d46cc71b2 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -122,6 +122,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; @@ -162,6 +163,7 @@ struct DivSubSong { chanShow[i]=true; chanShowChanOsc[i]=true; chanCollapse[i]=0; + chanColor[i]=0; } } }; diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 3bd6bf415..ee3ac3d27 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,10 +74,10 @@ 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]; + return chanOscBright[chan]; // this array is set to only 0 (???) break; case GUI_OSCREF_NOTE_TRIGGER: return keyHit1[chan]; @@ -87,10 +87,6 @@ float FurnaceGUI::computeGradPos(int type, int chan) { } void FurnaceGUI::calcChanOsc() { - std::vector oscBufs; - std::vector oscFFTs; - std::vector oscChans; - int chans=e->getTotalChannelCount(); for (int i=0; i64) chanOscCols=64; } + ImGui::EndDisabled(); ImGui::TableNextColumn(); ImGui::Text(_("Size (ms)")); @@ -160,18 +158,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)) { @@ -350,9 +337,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); @@ -360,7 +368,13 @@ void FurnaceGUI::drawChanOsc() { } } else { ImGui::SetNextItemWidth(400.0f*dpiScale); - ImGui::ColorPicker4(_("Color"),(float*)&chanOscColor); + bool chanOscColorModeB=chanOscColorMode; + ImGui::BeginDisabled(chanOscColorModeB); + ImGui::ColorEdit4(_("Color"),(float*)&chanOscColor); + ImGui::EndDisabled(); + if (ImGui::Checkbox(_("Set to channel color"), &chanOscColorModeB)) { + chanOscColorMode=chanOscColorModeB; + } } ImGui::AlignTextToFramePadding(); @@ -401,9 +415,12 @@ void FurnaceGUI::drawChanOsc() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); float availY=ImGui::GetContentRegionAvail().y; if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders|ImGuiTableFlags_NoClip)) { - std::vector 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(); @@ -420,18 +437,16 @@ void FurnaceGUI::drawChanOsc() { for (int i=0; igetOscBuffer(i); if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) { - oscBufs.push_back(buf); - 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 @@ -590,35 +605,21 @@ 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; - int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; + if (chanOscAutoCols) { + chanOscCols=sqrt(oscData.size()); + if (chanOscCols>64) chanOscCols=64; + } + int rows=(oscData.size()+(chanOscCols-1))/chanOscCols; // render - for (size_t i=0; iAddText(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) { @@ -901,15 +925,21 @@ 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': { + case 'l': text+='\n'; break; - } case '%': text+='%'; break; diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 34f7caab3..948972005 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/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 5573b94fe..abf7dcd17 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)]; @@ -8377,7 +8377,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); @@ -8400,6 +8400,7 @@ void FurnaceGUI::syncState() { chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f); chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f); chanOscUseGrad=e->getConfBool("chanOscUseGrad",false); + chanOscColorMode=e->getConfInt("chanOscColorMode",0); chanOscGrad.fromString(e->getConfString("chanOscGrad","")); chanOscGrad.render(); @@ -8550,7 +8551,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); @@ -8574,6 +8575,7 @@ void FurnaceGUI::commitState(DivConfig& conf) { conf.set("chanOscTextColorA",chanOscTextColor.w); conf.set("chanOscUseGrad",chanOscUseGrad); conf.set("chanOscGrad",chanOscGrad.toString()); + conf.set("chanOscColorMode",chanOscColorMode); // commit x-y osc state conf.set("xyOscXChannel",xyOscXChannel); @@ -9183,10 +9185,10 @@ FurnaceGUI::FurnaceGUI(): oscInput1(0.0f), oscZoomSlider(false), chanOscCols(3), - chanOscAutoColsType(0), chanOscColorX(GUI_OSCREF_CENTER), chanOscColorY(GUI_OSCREF_CENTER), chanOscCenterStrat(1), + chanOscColorMode(0), chanOscWindowSize(20.0f), chanOscTextX(0.0f), chanOscTextY(0.0f), @@ -9198,6 +9200,7 @@ FurnaceGUI::FurnaceGUI(): chanOscUseGrad(false), chanOscNormalize(false), chanOscRandomPhase(false), + chanOscAutoCols(false), chanOscTextFormat("%c"), chanOscColor(1.0f,1.0f,1.0f,1.0f), chanOscTextColor(1.0f,1.0f,1.0f,0.75f), @@ -9380,7 +9383,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 7b727eec1..84c87ad38 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(""), @@ -2696,9 +2698,10 @@ class FurnaceGUI { bool oscZoomSlider; // per-channel oscilloscope - int chanOscCols, chanOscAutoColsType, chanOscColorX, chanOscColorY, chanOscCenterStrat; + int chanOscCols, chanOscColorX, chanOscColorY, chanOscCenterStrat, chanOscColorMode; float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify, chanOscLineSize; - bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize, chanOscRandomPhase; + bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad; + bool chanOscNormalize, chanOscRandomPhase, chanOscAutoCols; String chanOscTextFormat; ImVec4 chanOscColor, chanOscTextColor; Gradient2D chanOscGrad; @@ -2838,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; @@ -3102,7 +3109,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(); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index e905d8762..53fb0338e 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; } } } @@ -674,12 +675,13 @@ 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]=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)->keyOff) amount=0.0f; + keyHit[i]=amount*0.2f; + if (!muted && e->getChanState(i)->keyOn) { + 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 cbef9ce30..0bd032d88 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);