diff --git a/demos/multichip/BONUS. Sonic 2 Boss.fur b/demos/multichip/BONUS. Sonic 2 Boss.fur deleted file mode 100644 index 8f5263f79..000000000 Binary files a/demos/multichip/BONUS. Sonic 2 Boss.fur and /dev/null differ diff --git a/demos/multichip/Boomer Kuwanger.fur b/demos/multichip/Boomer Kuwanger.fur deleted file mode 100644 index d20a28d62..000000000 Binary files a/demos/multichip/Boomer Kuwanger.fur and /dev/null differ diff --git a/demos/multichip/Cossack Stage Boss.fur b/demos/multichip/Cossack Stage Boss.fur new file mode 100644 index 000000000..26abb8b4c Binary files /dev/null and b/demos/multichip/Cossack Stage Boss.fur differ diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index e58de184b..ea507c2fc 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -101,11 +101,6 @@ namespace IGFD #ifndef FILTER_COMBO_WIDTH #define FILTER_COMBO_WIDTH 150.0f #endif // FILTER_COMBO_WIDTH -// for lets you define your button widget -// if you have like me a special bi-color button -#ifndef IMGUI_PATH_BUTTON -#define IMGUI_PATH_BUTTON ImGui::Button -#endif // IMGUI_PATH_BUTTON #ifndef IMGUI_BUTTON #define IMGUI_BUTTON ImGui::Button #endif // IMGUI_BUTTON @@ -2141,7 +2136,7 @@ namespace IGFD if (itPathDecomp != prCurrentPathDecomposition.begin()) ImGui::SameLine(); ImGui::PushID(_id++); - bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str()); + bool click = ImGui::ButtonEx((*itPathDecomp).c_str(),ImVec2(0,0),ImGuiButtonFlags_NoHashTextHide); ImGui::PopID(); if (click) { @@ -3903,7 +3898,7 @@ namespace IGFD auto& fdi = prFileDialogInternal.puFileManager; static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | - ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth; + ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth | ImGuiSelectableFlags_NoHashTextHide; // TODO BUG?! // YES BUG: THIS JUST CRASHED FOR SOME REASON diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 14ad6e2dc..8b39f2f26 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -3426,7 +3426,7 @@ void ImGui::RenderTextWrappedNoHashHide(ImVec2 pos, const char* text, const char // FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList. // Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take // better advantage of the render function taking size into account for coarse clipping. -void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect, bool hide_after_hash) { // Perform CPU side clipping for single clipped element to avoid using scissor state ImVec2 pos = pos_min; @@ -3446,11 +3446,19 @@ void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, co if (need_clipping) { ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + if (hide_after_hash) { + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } else { + draw_list->AddTextNoHashHide(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } } else { - draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + if (hide_after_hash) { + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } else { + draw_list->AddTextNoHashHide(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } } } @@ -3479,7 +3487,7 @@ void ImGui::RenderTextClippedNoHashHide(const ImVec2& pos_min, const ImVec2& pos ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); + RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect, false); if (g.LogEnabled) LogRenderedText(&pos_min, text, text_display_end); } diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 94e1a6cdb..7baea3c1a 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -881,6 +881,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiButtonFlags_NoHashTextHide = 1 << 22, // tildearrow: Don't hide text after hash ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, }; @@ -3456,7 +3457,7 @@ namespace ImGui IMGUI_API void RenderTextWrappedNoHashHide(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClippedNoHashHide(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); - IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL, bool hide_after_hash = true); IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameDrawList(ImDrawList* dl, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); // MODIFIED - draw list version of RenderFrame diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index ec3412a96..e79dc1da9 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -816,7 +816,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImVec2 label_size = CalcTextSize(label, NULL, (flags&ImGuiButtonFlags_NoHashTextHide)?false:true); ImVec2 pos = window->DC.CursorPos; if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) @@ -838,7 +838,12 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (g.LogEnabled) LogSetNextTextDecoration("[", "]"); - RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + if (flags & ImGuiButtonFlags_NoHashTextHide) { + RenderTextClippedNoHashHide(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + } else { + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + } // Automatically close popups //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) diff --git a/papers/format.md b/papers/format.md index 4e0756f0e..406a07c02 100644 --- a/papers/format.md +++ b/papers/format.md @@ -247,8 +247,14 @@ size | description | - 0xd7: Game Boy Advance (direct) - 2 channels | - 0xd8: Game Boy Advance (MinMod) - 16 channels | - 0xd9: Bifurcator - 4 channels + | - 0xda: SCSP - 32 channels (UNAVAILABLE) + | - 0xdb: YMF271 (OPX) - 48 channels (UNAVAILABLE) + | - 0xdc: RF5C400 - 32 channels (UNAVAILABLE) + | - 0xdd: YM2612 XGM - 9 channels (UNAVAILABLE) | - 0xde: YM2610B extended - 19 channels + | - 0xdf: YM2612 XGM extended - 13 channels (UNAVAILABLE) | - 0xe0: QSound - 19 channels + | - 0xe1: PS1 - 24 channels (UNAVAILABLE) | - 0xf0: SID2 - 3 channels | - 0xf1: 5E01 - 5 channels | - 0xfc: Pong - 1 channel diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index ec4d11b2f..693a26c5f 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -697,6 +697,14 @@ class DivDispatch { */ virtual int mapVelocity(int ch, float vel); + /** + * map chip volume to gain. + * @param ch the chip channel. -1 means N/A. + * @param vol input volume. + * @return output gain fron 0.0 to 1.0. + */ + virtual float getGain(int ch, int vol); + /** * get the lowest note in a portamento. * @param ch the channel in question. diff --git a/src/engine/fileOps/it.cpp b/src/engine/fileOps/it.cpp index d36c83560..a3ed09c49 100644 --- a/src/engine/fileOps/it.cpp +++ b/src/engine/fileOps/it.cpp @@ -181,6 +181,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { unsigned short patLen[256]; unsigned char defVol[256]; + unsigned char defPan[256]; + unsigned char defPanIns[256]; unsigned char noteMap[256][128]; bool doesPitchSlide[64]; @@ -210,6 +212,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { memset(patLen,0,256*sizeof(unsigned short)); memset(defVol,0,256); + memset(defPan,0,256); + memset(defPanIns,128,256); memset(noteMap,0,256*128); try { @@ -436,9 +440,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { reader.readC(); insVol=reader.readC(); - unsigned char defPan=reader.readC(); - - logV("defPan: %d",defPan); + defPanIns[i]=reader.readC(); // vol/pan randomization reader.readC(); @@ -572,9 +574,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { s->name=reader.readStringLatin1(26); unsigned char convert=reader.readC(); - unsigned char defPan=reader.readC(); - - logV("defPan: %d",defPan); + defPan[i]=reader.readC(); if (flags&2) { s->depth=DIV_SAMPLE_DEPTH_16BIT; @@ -946,6 +946,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { bool panSliding[64]; bool panSlidingOld[64]; bool did[64]; + unsigned char lastRetrig[64]; if (patPtr[i]==0) continue; @@ -989,6 +990,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { memset(panSliding,0,64*sizeof(bool)); memset(panSlidingOld,0,64*sizeof(bool)); memset(did,0,64*sizeof(bool)); + memset(lastRetrig,0,64); memset(mask,0,64); memset(note,0,64); @@ -1118,7 +1120,6 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } readRow++; - memset(effectCol,4,64); memcpy(vibingOld,vibing,64*sizeof(bool)); memcpy(volSlidingOld,volSliding,64*sizeof(bool)); memcpy(portingOld,porting,64*sizeof(bool)); @@ -1155,6 +1156,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } break; } + memset(effectCol,4,64); continue; } @@ -1216,6 +1218,29 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } if (hasIns) { p->data[readRow][2]=ins[chan]-1; + if ((note[chan]<120 || ds.insLen==0) && ins[chan]>0) { + unsigned char targetPan=0; + if (ds.insLen==0) { + targetPan=defPan[(ins[chan]-1)&255]; + } else { + targetPan=defPan[noteMap[(ins[chan]-1)&255][note[chan]]]; + if (!(targetPan&128)) { + targetPan=defPanIns[(ins[chan]-1)&255]^0x80; + } + } + if (targetPan&128) { + p->data[readRow][effectCol[chan]++]=0x80; + p->data[readRow][effectCol[chan]++]=CLAMP((targetPan&127)<<2,0,255); + } + } + + if (hasNote && (note[chan]<120 || ds.insLen==0) && ins[chan]>0) { + if (ds.insLen==0) { + p->data[readRow][3]=defVol[(ins[chan]-1)&255]; + } else { + p->data[readRow][3]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]]; + } + } } if (hasVol) { if (vol[chan]<=64) { @@ -1227,7 +1252,23 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } else if (vol[chan]>=65 && vol[chan]<=74) { // fine vol up } else if (vol[chan]>=75 && vol[chan]<=84) { // fine vol down } else if (vol[chan]>=85 && vol[chan]<=94) { // vol slide up + if ((vol[chan]-85)!=0) { + volSlideStatus[chan]=(vol[chan]-85)<<4; + volSlideStatusChanged[chan]=true; + } + if (hasNote || hasIns) { + volSlideStatusChanged[chan]=true; + } + volSliding[chan]=true; } else if (vol[chan]>=95 && vol[chan]<=104) { // vol slide down + if ((vol[chan]-95)!=0) { + volSlideStatus[chan]=vol[chan]-95; + volSlideStatusChanged[chan]=true; + } + if (hasNote || hasIns) { + volSlideStatusChanged[chan]=true; + } + volSliding[chan]=true; } else if (vol[chan]>=105 && vol[chan]<=114) { // pitch down } else if (vol[chan]>=115 && vol[chan]<=124) { // pitch up } else if (vol[chan]>=193 && vol[chan]<=202) { // porta @@ -1242,14 +1283,14 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { portaType[chan]=3; porting[chan]=true; } else if (vol[chan]>=203 && vol[chan]<=212) { // vibrato + if ((vol[chan]-203)!=0) { + vibStatus[chan]&=0xf0; + vibStatus[chan]|=(vol[chan]-203); + vibStatusChanged[chan]=true; + } + vibing[chan]=true; } } - } else if (hasNote && hasIns && (note[chan]<120 || ds.insLen==0) && ins[chan]>0) { - if (ds.insLen==0) { - p->data[readRow][3]=defVol[(ins[chan]-1)&255]; - } else { - p->data[readRow][3]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]]; - } } if (hasEffect) { switch (effect[chan]+'A'-1) { @@ -1302,7 +1343,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { portaStatus[chan]=effectVal[chan]; portaStatusChanged[chan]=true; } - if (portaType[chan]!=3) { + if (portaType[chan]!=3 || hasNote) { portaStatusChanged[chan]=true; } portaType[chan]=3; @@ -1357,8 +1398,11 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { panSliding[chan]=true; break; case 'Q': // retrigger + if (effectVal[chan]!=0) { + lastRetrig[chan]=effectVal[chan]; + } p->data[readRow][effectCol[chan]++]=0x0c; - p->data[readRow][effectCol[chan]++]=effectVal[chan]&15; + p->data[readRow][effectCol[chan]++]=lastRetrig[chan]&15; break; case 'R': // tremolo if (effectVal[chan]!=0) { @@ -1369,6 +1413,10 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { break; case 'S': // special... switch (effectVal[chan]>>4) { + case 0x8: + p->data[readRow][effectCol[chan]++]=0x80; + p->data[readRow][effectCol[chan]++]=(effectVal[chan]&15)<<4; + break; case 0xc: p->data[readRow][effectCol[chan]++]=0xec; p->data[readRow][effectCol[chan]++]=effectVal[chan]&15; @@ -1380,8 +1428,10 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } break; case 'T': // tempo - p->data[readRow][effectCol[chan]++]=0xf0; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + if (effectVal[chan]>=0x20) { + p->data[readRow][effectCol[chan]++]=0xf0; + p->data[readRow][effectCol[chan]++]=effectVal[chan]; + } break; case 'U': // fine vibrato if (effectVal[chan]!=0) { @@ -1471,6 +1521,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } } ds.systemLen=(maxChan+32)>>5; + ds.systemName="PC"; // find subsongs ds.findSubSongs(maxChan); diff --git a/src/engine/fileOps/s3m.cpp b/src/engine/fileOps/s3m.cpp index f30e6c89b..d13c1c7c2 100644 --- a/src/engine/fileOps/s3m.cpp +++ b/src/engine/fileOps/s3m.cpp @@ -1075,6 +1075,10 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { break; case 'S': // special... switch (effectVal>>4) { + case 0x8: + p->data[readRow][effectCol[chan]++]=0x80; + p->data[readRow][effectCol[chan]++]=(effectVal&15)<<4; + break; case 0xc: p->data[readRow][effectCol[chan]++]=0xec; p->data[readRow][effectCol[chan]++]=effectVal&15; diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp index b44369007..d4b7c6ca0 100644 --- a/src/engine/fileOps/xm.cpp +++ b/src/engine/fileOps/xm.cpp @@ -87,7 +87,10 @@ void readEnvelope(DivInstrument* ins, int env, unsigned char flags, unsigned cha } } if ((point+1)>=numPoints) { - target->len=i; + target->len=i-1; + if (((flags&4) && (!(flags&2))) || ((flags&6)==0)) { + target->rel=i-2; + } //target->val[i]=p0; break; } @@ -103,7 +106,7 @@ void readEnvelope(DivInstrument* ins, int env, unsigned char flags, unsigned cha // split L/R if (env==1) { for (int i=0; istd.panLMacro.len; i++) { - int val=ins->std.panLMacro.val[i]; + int val=ins->std.panLMacro.val[i]-32; if (val==0) { ins->std.panLMacro.val[i]=4095; ins->std.panRMacro.val[i]=4095; @@ -166,7 +169,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { ds.noSlidesOnFirstTick=true; ds.rowResetsArpPos=true; ds.ignoreJumpAtEnd=false; - ds.pitchSlideSpeed=12; + ds.pitchSlideSpeed=8; logV("Extended Module"); @@ -421,6 +424,9 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { } } if (hasEffectVal) { + if (!hasEffect) { + doesArp[k]=true; + } effectVal=reader.readC(); if (effect==0xe) { switch (effectVal>>4) { @@ -525,21 +531,40 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { if (volType&1) { // add fade-out - int cur=64; - if (ins->std.volMacro.len>0) { - cur=ins->std.volMacro.val[ins->std.volMacro.len-1]; - } - for (int fadeOut=32767; fadeOut>0 && ins->std.volMacro.len<254; fadeOut-=volFade) { - ins->std.volMacro.val[ins->std.volMacro.len++]=(cur*fadeOut)>>15; - } - if (ins->std.volMacro.len<255) { - ins->std.volMacro.val[ins->std.volMacro.len++]=0; + if (volFade!=0) { + int cur=64; + int macroLen=ins->std.volMacro.len; + int curPos=ins->std.volMacro.len-1; + if (ins->std.volMacro.loopstd.volMacro.loop; + } + if (ins->std.volMacro.len>0) { + cur=ins->std.volMacro.val[curPos]; + } + for (int fadeOut=32767; fadeOut>0 && ins->std.volMacro.len<254; fadeOut-=volFade) { + cur=ins->std.volMacro.val[curPos]; + ins->std.volMacro.val[ins->std.volMacro.len++]=(cur*fadeOut)>>15; + if (++curPos>=macroLen) { + if (ins->std.volMacro.loopstd.volMacro.loop; + } else { + curPos=macroLen-1; + } + } + } + if (ins->std.volMacro.len<255) { + ins->std.volMacro.val[ins->std.volMacro.len++]=0; + } + if (ins->std.volMacro.relstd.volMacro.len && ins->std.volMacro.relstd.volMacro.loop) { + ins->std.volMacro.loop=255; + } } } else { // add a one-tick macro to make note release happy ins->std.volMacro.val[0]=64; ins->std.volMacro.val[1]=0; ins->std.volMacro.rel=0; + ins->std.volMacro.loop=255; ins->std.volMacro.len=2; } @@ -908,6 +933,15 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { panSliding[k]=true; break; case 0xf: // porta + if ((vol&15)!=0) { + portaStatus[k]=(vol&15)<<4; + portaStatusChanged[k]=true; + } + if (portaType[k]!=3 || (hasNote && note>0)) { + portaStatusChanged[k]=true; + } + portaType[k]=3; + porting[k]=true; break; } } @@ -955,7 +989,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { portaStatus[k]=effectVal; portaStatusChanged[k]=true; } - if (portaType[k]!=3) { + if (portaType[k]!=3 || (hasNote && note>0)) { portaStatusChanged[k]=true; } portaType[k]=3; @@ -1006,8 +1040,10 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { writePanning=false; break; case 9: // offset - p->data[j][effectCol[k]++]=0x91; - p->data[j][effectCol[k]++]=effectVal; + if (hasNote) { + p->data[j][effectCol[k]++]=0x91; + p->data[j][effectCol[k]++]=effectVal; + } break; case 0xa: // vol slide if (effectVal!=0) { @@ -1037,19 +1073,25 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { p->data[j][effectCol[k]++]=0xe5; p->data[j][effectCol[k]++]=(effectVal&15)<<4; break; + case 0x9: + p->data[j][effectCol[k]++]=0x0c; + p->data[j][effectCol[k]++]=(effectVal&15); + break; case 0xc: p->data[j][effectCol[k]++]=0xec; - p->data[j][effectCol[k]++]=effectVal&15; + p->data[j][effectCol[k]++]=MAX(1,effectVal&15); break; case 0xd: p->data[j][effectCol[k]++]=0xed; - p->data[j][effectCol[k]++]=effectVal&15; + p->data[j][effectCol[k]++]=MAX(1,effectVal&15); break; } break; - case 0xf: // speed/tempp + case 0xf: // speed/tempo if (effectVal>=0x20) { p->data[j][effectCol[k]++]=0xf0; + } else if (effectVal==0) { + p->data[j][effectCol[k]++]=0xff; } else { p->data[j][effectCol[k]++]=0x0f; } @@ -1128,14 +1170,8 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { } if (porting[k]!=portingOld[k] || portaStatusChanged[k]) { - if (portaStatus[k]>=0xe0 && portaType[k]!=3 && porting[k]) { - p->data[j][effectCol[k]++]=portaType[k]|0xf0; - p->data[j][effectCol[k]++]=(portaStatus[k]&15)*((portaStatus[k]>=0xf0)?1:1); - porting[k]=false; - } else { - p->data[j][effectCol[k]++]=portaType[k]; - p->data[j][effectCol[k]++]=porting[k]?portaStatus[k]:0; - } + p->data[j][effectCol[k]++]=portaType[k]; + p->data[j][effectCol[k]++]=porting[k]?portaStatus[k]:0; doesPitchSlide[k]=true; } else if (doesPitchSlide[k] && mustCommitInitial) { p->data[j][effectCol[k]++]=0x01; @@ -1183,7 +1219,6 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { } } - memset(effectCol,4,64); memcpy(vibingOld,vibing,64*sizeof(bool)); memcpy(volSlidingOld,volSliding,64*sizeof(bool)); memcpy(portingOld,porting,64*sizeof(bool)); @@ -1207,6 +1242,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)-1; } } + memset(effectCol,4,64); } logV("seeking to %x...",packedSeek); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 14370f297..441ec1bff 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -107,6 +107,11 @@ int DivDispatch::mapVelocity(int ch, float vel) { return round(vel*volMax); } +float DivDispatch::getGain(int ch, int vol) { + const float volMax=MAX(1,dispatch(DivCommand(DIV_CMD_GET_VOLMAX,MAX(ch,0)))); + return (float)vol/volMax; +} + int DivDispatch::getPortaFloor(int ch) { return 0x00; } diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index bdccea60e..54f4359ca 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -156,14 +156,14 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) { if (i<(int)e->song.ins.size()) { DivInstrument* ins=e->song.ins[i]; ImGui::SameLine(); - ImGui::Text("%.2X: %s",i,ins->name.c_str()); + ImGui::TextNoHashHide("%.2X: %s",i,ins->name.c_str()); } else { ImGui::SameLine(); - ImGui::Text("%.2X: ",i); + ImGui::TextNoHashHide("%.2X: ",i); } } else { ImGui::SameLine(); - ImGui::Text("- None -"); + ImGui::TextNoHashHide("- None -"); } ImGui::PopID(); ImGui::PopStyleColor(); @@ -219,7 +219,7 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { if (memWarning) break; } if (memWarning) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_SAMPLE_CHIP_WARNING]); - if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) { + if (ImGui::Selectable(fmt::sprintf("%d:##_SAM%d",i,i).c_str(),curSample==i)) { curSample=i; samplePos=0; updateSampleTex=true; @@ -235,6 +235,8 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) { DRAG_SOURCE(dir,asset,"FUR_SDIR"); DRAG_TARGET(dir,asset,e->song.sampleDir,"FUR_SDIR"); } + ImGui::SameLine(); + ImGui::TextNoHashHide("%s",sample->name.c_str()); if (memWarning) { ImGui::SameLine(); ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e9970a103..0444b33ab 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4281,6 +4281,19 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } } + bool hasTiunaCompat=false; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_TIA) { + hasTiunaCompat=true; + break; + } + } + if (hasTiunaCompat) { + if (ImGui::BeginMenu(_("export TIunA..."))) { + drawExportTiuna(); + ImGui::EndMenu(); + } + } int numAmiga=0; for (int i=0; isong.systemLen; i++) { if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; @@ -4322,6 +4335,19 @@ bool FurnaceGUI::loop() { displayExport=true; } } + bool hasTiunaCompat=false; + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_TIA) { + hasTiunaCompat=true; + break; + } + } + if (hasTiunaCompat) { + if (ImGui::MenuItem(_("export TIunA..."))) { + curExportType=GUI_EXPORT_TIUNA; + displayExport=true; + } + } int numAmiga=0; for (int i=0; isong.systemLen; i++) { if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 80d4c783f..fdc6bbdb7 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -282,7 +282,7 @@ void FurnaceGUI::drawOrders() { for (int i=0; igetTotalChannelCount(); i++) { if (!e->curSubSong->chanShow[i]) continue; ImGui::TableNextColumn(); - ImGui::Text("%s",e->getChannelShortName(i)); + ImGui::TextNoHashHide("%s",e->getChannelShortName(i)); } ImGui::PopStyleColor(); for (int i=0; icurSubSong->ordersLen; i++) { diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 3b95780db..a2568bd31 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -686,7 +686,7 @@ void FurnaceGUI::drawPattern() { bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0); ImU32 col=(hovered || (mobileUI && ImGui::IsMouseDown(ImGuiMouseButton_Left)))?ImGui::GetColorU32(ImGuiCol_HeaderHovered):ImGui::GetColorU32(ImGuiCol_Header); dl->AddRectFilled(rect.Min,rect.Max,col); - dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID); + dl->AddTextNoHashHide(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; case 1: { // line @@ -707,7 +707,7 @@ void FurnaceGUI::drawPattern() { )); dl->AddRectFilledMultiColor(rect.Min,rect.Max,fadeCol0,fadeCol0,fadeCol,fadeCol); dl->AddLine(ImVec2(rect.Min.x,rect.Max.y),ImVec2(rect.Max.x,rect.Max.y),ImGui::GetColorU32(chanHeadBase),2.0f*dpiScale); - dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); + dl->AddTextNoHashHide(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } @@ -734,14 +734,14 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=6.0f*dpiScale; dl->AddRectFilledMultiColor(rMin,rMax,fadeCol0,fadeCol0,fadeCol,fadeCol,4.0f*dpiScale); - dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); + dl->AddTextNoHashHide(ImVec2(minLabelArea.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } case 3: // split button ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); //ImGui::SetCursorPosX(minLabelArea.x); - ImGui::TextUnformatted(chanID); + ImGui::TextNoHashHide("%s",chanID); ImGui::SameLine(); ImGui::PushFont(mainFont); ImGui::SmallButton(muted?ICON_FA_VOLUME_OFF:ICON_FA_VOLUME_UP); @@ -764,7 +764,7 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=3.0f*dpiScale; dl->AddRect(rMin,rMax,fadeCol,0.0f,2.0*dpiScale); - dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); + dl->AddTextNoHashHide(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } @@ -785,7 +785,7 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=3.0f*dpiScale; dl->AddRect(rMin,rMax,fadeCol,4.0f*dpiScale,ImDrawFlags_RoundCornersAll,2.0*dpiScale); - dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); + dl->AddTextNoHashHide(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; }