Merge branch 'chanColorsEtc' into inf2

This commit is contained in:
tildearrow 2025-11-21 10:51:44 -05:00
commit fea3bab550
11 changed files with 207 additions and 92 deletions

View file

@ -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; i<DIV_MAX_CHANS; i++) {
theCopy->chanName[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; k<DIV_MAX_PATTERNS; k++) {
if (i->pat[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; j<tchans; j++) {
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
@ -1384,6 +1394,7 @@ void DivEngine::swapSystemUnsafe(int src, int dest, bool preserveOrder) {
prevChanShow[j]=song.subsong[i]->chanShow[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; j<tchans; j++) {
@ -1398,6 +1409,7 @@ void DivEngine::swapSystemUnsafe(int src, int dest, bool preserveOrder) {
song.subsong[i]->chanShow[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]];
}
}
}

View file

@ -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; k<DIV_MAX_CHANS; k++) {
theCopy->chanName[k]=i->chanName[k];

View file

@ -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;
}
}
};

View file

@ -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<DivDispatchOscBuffer*> oscBufs;
std::vector<ChanOscStatus*> oscFFTs;
std::vector<int> oscChans;
int chans=e->getTotalChannelCount();
for (int i=0; i<chans; i++) {
@ -143,10 +139,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 +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<DivDispatchOscBuffer*> oscBufs;
std::vector<ChanOscStatus*> oscFFTs;
std::vector<int> oscChans;
struct OscData {
DivDispatchOscBuffer* buf;
ChanOscStatus* fft;
int chan;
};
std::vector<OscData> oscData;
int chans=e->getTotalChannelCount();
ImGuiWindow* window=ImGui::GetCurrentWindow();
@ -420,18 +437,16 @@ void FurnaceGUI::drawChanOsc() {
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=e->getOscBuffer(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; i<oscBufs.size(); i++) {
ChanOscStatus* fft_=oscFFTs[i];
for (size_t i=0; i<oscData.size(); i++) {
ChanOscStatus* fft_=oscData[i].fft;
fft_->relatedBuf=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; i<oscBufs.size(); i++) {
for (size_t i=0; i<oscData.size(); i++) {
if (i%chanOscCols==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
DivDispatchOscBuffer* buf=oscBufs[i];
ChanOscStatus* fft=oscFFTs[i];
int ch=oscChans[i];
DivDispatchOscBuffer* buf=oscData[i].buf;
ChanOscStatus* fft=oscData[i].fft;
int ch=oscData[i].chan;
if (buf==NULL) {
ImGui::Text(_("Error!"));
} else {
@ -793,15 +794,38 @@ void FurnaceGUI::drawChanOsc() {
}
}
}
ImU32 color=ImGui::GetColorU32(chanOscColor);
ImU32 color;
switch (chanOscColorMode) {
case 0:
color=ImGui::GetColorU32(chanOscColor);
break;
case 1:
color=ImGui::GetColorU32(channelColor(oscData[i].chan));
break;
}
if (chanOscUseGrad) {
float xVal=computeGradPos(chanOscColorX,ch);
float yVal=computeGradPos(chanOscColorY,ch);
float xVal=computeGradPos(chanOscColorX,ch,oscData.size());
float yVal=computeGradPos(chanOscColorY,ch,oscData.size());
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) {
@ -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<char>* chan=(SharedChannel<char>*)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;

View file

@ -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; i<e->getTotalChannelCount(); 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();

View file

@ -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
}
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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);