diff --git a/TODO.md b/TODO.md index 5b7d52d1e..d65f7da47 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,6 @@ - volume commands should work on Game Boy - add another FM editor layout - if macros have release, note off should release them -- add ability to select a column by double clicking - add ability to move selection by dragging - Apply button in settings - find and replace diff --git a/extern/imgui_patched/imgui_internal.h b/extern/imgui_patched/imgui_internal.h index 3d6a3a1d0..be3ab9222 100644 --- a/extern/imgui_patched/imgui_internal.h +++ b/extern/imgui_patched/imgui_internal.h @@ -566,9 +566,8 @@ struct ImBitArray void SetBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); } void ClearBit(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); } void SetBitRange(int n, int n2) { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2) - bool operator[](int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } - ImBitArray& operator|=(int n) { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); return *this; } - bool operator&(int n) const { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } + bool operator[](int n) const { return TestBit(n); } + bool operator&(int n) const { return TestBit(n); } bool operator==(ImBitArray const &a) const { for (int i = 0; i < ((BITCOUNT + 31) >> 5); ++i) @@ -614,23 +613,17 @@ struct IMGUI_API ImBitVector void SetBit(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArraySetBit(Storage.Data, n); } void SetBitRange(int n, int n2) { IM_ASSERT(n >= 0 && n < BitCount && n2 > n && n2 <= BitCount); ImBitArraySetBitRange(Storage.Data, n, n2); } // Works on range [n..n2) void ClearBit(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArrayClearBit(Storage.Data, n); } - bool operator[](int n) const { IM_ASSERT(n >= 0 && n < BitCount); return ImBitArrayTestBit(Storage.Data, n); } - ImBitVector& operator|=(int n) { IM_ASSERT(n >= 0 && n < BitCount); ImBitArraySetBit(Storage.Data, n); return *this; } - bool operator&(int n) const { IM_ASSERT(n >= 0 && n < BitCount); return ImBitArrayTestBit(Storage.Data, n); } - bool operator==(ImBitVector const &a) const + bool CheckEqual(ImBitVector const &a) const { for (int i = 0; i < ImMin((a.BitCount + 31) >> 5, (BitCount + 31) >> 5); ++i) if (Storage[i] != a.Storage[i]) return false; return true; } - bool operator!=(ImBitVector const &a) const - { - for (int i = 0; i < ImMin((a.BitCount + 31) >> 5, (BitCount + 31) >> 5); ++i) - if (Storage[i] == a.Storage[i]) - return false; - return true; - } + bool operator[](int n) const { return TestBit(n); } + bool operator&(int n) const { return TestBit(n); } + bool operator==(ImBitVector const &a) const { return CheckEqual(a); } + bool operator!=(ImBitVector const &a) const { return !CheckEqual(a); } }; // Helper: ImSpan<> @@ -2503,8 +2496,8 @@ struct IMGUI_API ImGuiTabBar #define IMGUI_TABLE_DRAW_CHANNELS(c) (4 + (c) * 2) // See TableSetupDrawChannels() // Our current column maximum is IMGUI_TABLE_MAX_COLUMNS but we may raise that in the future. -typedef ImS32 ImGuiTableColumnIdx; -typedef ImU32 ImGuiTableDrawChannelIdx; +typedef ImS16 ImGuiTableColumnIdx; +typedef ImU16 ImGuiTableDrawChannelIdx; // [Internal] sizeof() ~ 104 // We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. @@ -2563,7 +2556,7 @@ struct ImGuiTableColumn PrevEnabledColumn = NextEnabledColumn = -1; SortOrder = -1; SortDirection = ImGuiSortDirection_None; - DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU32)-1; + DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU16)-1; } }; diff --git a/extern/imgui_patched/imgui_tables.cpp b/extern/imgui_patched/imgui_tables.cpp index 6aea054eb..5a21a0b13 100644 --- a/extern/imgui_patched/imgui_tables.cpp +++ b/extern/imgui_patched/imgui_tables.cpp @@ -797,8 +797,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) else table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n; column->IndexWithinEnabledSet = table->ColumnsEnabledCount++; - table->EnabledMaskByIndex |= column_n; - table->EnabledMaskByDisplayOrder |= column->DisplayOrder; + table->EnabledMaskByIndex.SetBit(column_n); + table->EnabledMaskByDisplayOrder.SetBit(column->DisplayOrder); prev_visible_column_idx = column_n; IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder); @@ -846,7 +846,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { - if (!(table->EnabledMaskByIndex & column_n)) + if (!table->EnabledMaskByIndex.TestBit(column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; @@ -862,7 +862,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!) if (column->AutoFitQueue != 0x00) column->WidthRequest = width_auto; - else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex & column_n)) + else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && (table->RequestOutputMaskByIndex.TestBit(column_n))) column->WidthRequest = width_auto; // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets @@ -909,7 +909,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { - if (!(table->EnabledMaskByIndex & column_n)) + if (!table->EnabledMaskByIndex.TestBit(column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; @@ -936,7 +936,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths)) for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--) { - if (!(table->EnabledMaskByDisplayOrder & order_n)) + if (!table->EnabledMaskByDisplayOrder.TestBit(order_n)) continue; ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]]; if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch)) @@ -977,7 +977,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Clear status flags column->Flags &= ~ImGuiTableColumnFlags_StatusMask_; - if ((table->EnabledMaskByDisplayOrder & order_n) == 0) + if (!table->EnabledMaskByDisplayOrder.TestBit(order_n)) { // Hidden column: clear a few fields and we are done with it for the remainder of the function. // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper. @@ -1030,12 +1030,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y); const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY; if (is_visible) - table->VisibleMaskByIndex |= column_n; + table->VisibleMaskByIndex.SetBit(column_n); // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output. column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; if (column->IsRequestOutput) - table->RequestOutputMaskByIndex |= column_n; + table->RequestOutputMaskByIndex.SetBit(column_n); // Mark column as SkipItems (ignoring all items/layout) column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; @@ -1163,7 +1163,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { - if (!(table->EnabledMaskByDisplayOrder & order_n)) + if (!table->EnabledMaskByDisplayOrder.TestBit(order_n)) continue; const int column_n = table->DisplayOrderToIndex[order_n]; @@ -1299,7 +1299,7 @@ void ImGui::EndTable() float auto_fit_width_for_stretched = 0.0f; float auto_fit_width_for_stretched_min = 0.0f; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - if (table->EnabledMaskByIndex & column_n) + if (table->EnabledMaskByIndex.TestBit(column_n)) { ImGuiTableColumn* column = &table->Columns[column_n]; float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column); @@ -1645,7 +1645,7 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n return; if (column_n == -1) column_n = table->CurrentColumn; - if ((table->VisibleMaskByIndex & column_n) == 0) + if (!table->VisibleMaskByIndex.TestBit(column_n)) return; if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n) table->RowCellDataCurrent++; @@ -1920,7 +1920,7 @@ bool ImGui::TableSetColumnIndex(int column_n) // Return whether the column is visible. User may choose to skip submitting items based on this return value, // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. - return (table->RequestOutputMaskByIndex & column_n) != 0; + return table->RequestOutputMaskByIndex.TestBit(column_n); } // [Public] Append into the next column, wrap and create a new row when already on last column @@ -1946,7 +1946,7 @@ bool ImGui::TableNextColumn() // Return whether the column is visible. User may choose to skip submitting items based on this return value, // however they shouldn't skip submitting for columns that may have the tallest contribution to row height. int column_n = table->CurrentColumn; - return (table->RequestOutputMaskByIndex & column_n) != 0; + return table->RequestOutputMaskByIndex.TestBit(column_n); } @@ -1998,11 +1998,6 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - if (column->DrawChannelCurrent==(ImGuiTableDrawChannelIdx)-1) { - // temporary workaround for #502 - //printf("sorry!\n"); - column->DrawChannelCurrent=column_n; - } table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } @@ -2380,7 +2375,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { - if ((table->VisibleMaskByIndex & column_n) == 0) + if (!table->VisibleMaskByIndex.TestBit(column_n)) continue; ImGuiTableColumn* column = &table->Columns[column_n]; @@ -2544,7 +2539,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { - if (!(table->EnabledMaskByDisplayOrder & order_n)) + if (!table->EnabledMaskByDisplayOrder.TestBit(order_n)) continue; const int column_n = table->DisplayOrderToIndex[order_n]; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index d3d1ff23b..1c6655921 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2160,6 +2160,7 @@ void DivEngine::delSample(int index) { void DivEngine::addOrder(bool duplicate, bool where) { unsigned char order[DIV_MAX_CHANS]; if (curSubSong->ordersLen>=0xff) return; + memset(order,0,DIV_MAX_CHANS); BUSY_BEGIN_SOFT; if (duplicate) { for (int i=0; i>4)<<3)); break; } + case DIV_CMD_FM_FB: { + chan[2].state.fb=c.value&7; + rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + break; + } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 285e350de..441640b45 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -155,6 +155,11 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); break; } + case DIV_CMD_FM_FB: { + chan[2].state.fb=c.value&7; + rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + break; + } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 20b56c018..cd7e494b9 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -155,6 +155,11 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); break; } + case DIV_CMD_FM_FB: { + chan[2].state.fb=c.value&7; + rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + break; + } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index a3d5df21d..bf6df44e7 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -155,6 +155,11 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); break; } + case DIV_CMD_FM_FB: { + chan[1].state.fb=c.value&7; + rWrite(chanOffs[1]+ADDR_FB_ALG,(chan[1].state.alg&7)|(chan[1].state.fb<<3)); + break; + } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); diff --git a/src/engine/song.h b/src/engine/song.h index 4d75d8e15..86f670102 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -409,7 +409,7 @@ struct DivSong { std::vector subsong; - DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound; + DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound; DivWavetable nullWave; DivSample nullSample; @@ -552,6 +552,9 @@ struct DivSong { nullInsOPL.fm.op[1].rr=12; nullInsOPL.fm.op[1].mult=1; nullInsOPL.name="This is a bug! Report!"; + nullInsOPL.fm.kickFreq=0x520; + nullInsOPL.fm.snareHatFreq=0x550; + nullInsOPL.fm.tomTopFreq=0x1c0; nullInsQSound.std.panLMacro.mode=true; } diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 5ca49f79b..a2f29da3b 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -19,6 +19,7 @@ #include "dispatch.h" #include "engine.h" +#include "instrument.h" #include "song.h" #include "../ta-log.h" @@ -1579,11 +1580,11 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef( "Yamaha YM3526 (OPL) with drums", NULL, 0xa2, 0, 11, true, false, 0x151, false, "the OPL chip but with drums mode enabled.", - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick/FM 7", "Snare", "Tom", "Top", "HiHat"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, - {}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, oplDrumsEffectHandler, fmOPLPostEffectHandler ); @@ -1591,11 +1592,11 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef( "Yamaha YM3812 (OPL2) with drums", NULL, 0xa3, 0, 11, true, false, 0x151, false, "the OPL2 chip but with drums mode enabled.", - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick/FM 7", "Snare", "Tom", "Top", "HiHat"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, - {}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, oplDrumsEffectHandler, fmOPLPostEffectHandler ); @@ -1603,11 +1604,11 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef( "Yamaha YMF262 (OPL3) with drums", NULL, 0xa4, 0, 20, true, false, 0x151, false, "the OPL3 chip but with drums mode enabled.", - {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick/FM 16", "Snare", "Tom", "Top", "HiHat"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, - {}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, oplDrumsEffectHandler, fmOPLPostEffectHandler ); @@ -1820,7 +1821,7 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef( "Yamaha OPL4 with drums", NULL, 0xaf, 0, 44, true, true, 0, false, "the OPL4 but with drums mode turned on.", - {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick/FM 16", "Snare", "Tom", "Top", "HiHat", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} @@ -1928,11 +1929,11 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef( "Yamaha Y8950 with drums", NULL, 0xb3, 0, 12, true, false, 0x151, false, "the Y8950 chip, in drums mode.", - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM"}, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick/FM 7", "Snare", "Tom", "Top", "HiHat", "PCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH", "PCM"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA}, - {}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_AMIGA}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_NULL}, oplEffectHandler, fmOPLPostEffectHandler ); diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 244880735..e47dfd629 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -27,6 +27,21 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) { if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { curNibble=false; } + + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && !fullRow && settings.doubleClickColumn) { + if (cursor.xCoarse==xCoarse && cursor.xFine==xFine && cursor.y==y) { + // select entire channel + selStart.xCoarse=xCoarse; + selStart.xFine=0; + selStart.y=0; + selEnd.xCoarse=xCoarse; + selEnd.xFine=2+e->curPat[selEnd.xCoarse].effectCols*2; + selEnd.y=e->curSubSong->patLen-1; + + finishSelection(); + return; + } + } if (fullRow) { selStart.xCoarse=firstChannel; @@ -314,4 +329,4 @@ void FurnaceGUI::editAdvance() { selStart=cursor; selEnd=cursor; updateScroll(cursor.y); -} \ No newline at end of file +} diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 4e0979972..62b93479b 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -313,6 +313,10 @@ void FurnaceGUI::drawInsList() { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NAMCO]); name=fmt::sprintf(ICON_FA_PIE_CHART " %.2X: %s##_INS%d",i,ins->name,i); break; + case DIV_INS_OPL_DRUMS: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL_DRUMS]); + name=fmt::sprintf(ICON_FA_COFFEE " %.2X: %s##_INS%d",i,ins->name,i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 40f00e91e..50729a155 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3927,6 +3927,9 @@ bool FurnaceGUI::init() { if (orderEditMode<0) orderEditMode=0; if (orderEditMode>3) orderEditMode=3; + oscZoom=e->getConfFloat("oscZoom",0.5f); + oscWindowSize=e->getConfFloat("oscWindowSize",20.0f); + pianoOctaves=e->getConfInt("pianoOctaves",pianoOctaves); pianoOctavesEdit=e->getConfInt("pianoOctavesEdit",pianoOctavesEdit); pianoOptions=e->getConfBool("pianoOptions",pianoOptions); @@ -4137,6 +4140,10 @@ bool FurnaceGUI::finish() { e->setConf("followPattern",followPattern); e->setConf("orderEditMode",orderEditMode); + // commit oscilloscope state + e->setConf("oscZoom",oscZoom); + e->setConf("oscWindowSize",oscWindowSize); + // commit piano state e->setConf("pianoOctaves",pianoOctaves); e->setConf("pianoOctavesEdit",pianoOctavesEdit); @@ -4422,6 +4429,7 @@ FurnaceGUI::FurnaceGUI(): openSampleFilterOpt(false), oscTotal(0), oscZoom(0.5f), + oscWindowSize(20.0f), oscZoomSlider(false), chanOscCols(3), chanOscWindowSize(20.0f), diff --git a/src/gui/gui.h b/src/gui/gui.h index 05d49e58e..167e0ad2e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -151,6 +151,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_SNES, GUI_COLOR_INSTR_SU, GUI_COLOR_INSTR_NAMCO, + GUI_COLOR_INSTR_OPL_DRUMS, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_FM, @@ -950,6 +951,7 @@ class FurnaceGUI { int volCellSpacing; int effectCellSpacing; int effectValCellSpacing; + int doubleClickColumn; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -1048,6 +1050,7 @@ class FurnaceGUI { volCellSpacing(0), effectCellSpacing(0), effectValCellSpacing(0), + doubleClickColumn(1), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -1238,6 +1241,7 @@ class FurnaceGUI { int oscTotal; float oscValues[512]; float oscZoom; + float oscWindowSize; bool oscZoomSlider; // per-channel oscilloscope diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 08f71eb8e..54e5c6d61 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -112,6 +112,7 @@ const char* insTypes[DIV_INS_MAX+1]={ "SNES", "Sound Unit", "Namco WSG", + "OPL (drums)", NULL }; @@ -765,6 +766,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_SNES,"",ImVec4(0.8f,0.7f,1.0f,1.0f)), D(GUI_COLOR_INSTR_SU,"",ImVec4(0.95f,0.98f,1.0f,1.0f)), D(GUI_COLOR_INSTR_NAMCO,"",ImVec4(1.0f,1.0f,0.0f,1.0f)), + D(GUI_COLOR_INSTR_OPL_DRUMS,"",ImVec4(0.3f,1.0f,0.9f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f80730c55..279d1c576 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -75,6 +75,10 @@ const char* opzWaveforms[8]={ "Sine", "Triangle", "Cut Sine", "Cut Triangle", "Squished Sine", "Squished Triangle", "Squished AbsSine", "Squished AbsTriangle" }; +const char* oplDrumNames[4]={ + "Snare", "Tom", "Top", "HiHat" +}; + const bool opIsOutput[8][4]={ {false,false,false,true}, {false,false,false,true}, @@ -1537,7 +1541,7 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::BeginTabBar("insEditTab")) { std::vector macroList; - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ) { + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL_DRUMS) { char label[32]; int opCount=4; if (ins->type==DIV_INS_OPLL) opCount=2; @@ -1579,22 +1583,27 @@ void FurnaceGUI::drawInsEdit() { showError("Coming soon!"); } break; - case DIV_INS_OPL: { - bool fourOp=(ins->fm.ops==4); + case DIV_INS_OPL: + case DIV_INS_OPL_DRUMS: { + bool fourOp=(ins->fm.ops==4 || ins->type==DIV_INS_OPL_DRUMS); bool drums=ins->fm.opllPreset==16; int algMax=fourOp?3:1; ImGui::TableNextColumn(); ins->fm.alg&=algMax; P(CWSliderScalar(FM_NAME(FM_FB),ImGuiDataType_U8,&ins->fm.fb,&_ZERO,&_SEVEN)); rightClickable - ImGui::BeginDisabled(ins->fm.opllPreset==16); - if (ImGui::Checkbox("4-op",&fourOp)) { PARAMETER - ins->fm.ops=fourOp?4:2; + if (ins->type==DIV_INS_OPL) { + ImGui::BeginDisabled(ins->fm.opllPreset==16); + if (ImGui::Checkbox("4-op",&fourOp)) { PARAMETER + ins->fm.ops=fourOp?4:2; + } + ImGui::EndDisabled(); } - ImGui::EndDisabled(); ImGui::TableNextColumn(); P(CWSliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&algMax)); rightClickable - if (ImGui::Checkbox("Drums",&drums)) { PARAMETER - ins->fm.opllPreset=drums?16:0; + if (ins->type==DIV_INS_OPL) { + if (ImGui::Checkbox("Drums",&drums)) { PARAMETER + ins->fm.opllPreset=drums?16:0; + } } ImGui::TableNextColumn(); drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); @@ -1639,7 +1648,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } - if ((ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) && ins->fm.opllPreset==16) { + if (((ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) && ins->fm.opllPreset==16) || ins->type==DIV_INS_OPL_DRUMS) { ins->fm.ops=2; P(ImGui::Checkbox("Fixed frequency mode",&ins->fm.fixedDrums)); if (ImGui::IsItemHovered()) { @@ -1674,7 +1683,7 @@ void FurnaceGUI::drawInsEdit() { if (willDisplayOps) { if (settings.fmLayout==0) { int numCols=16; - if (ins->type==DIV_INS_OPL) numCols=13; + if (ins->type==DIV_INS_OPL ||ins->type==DIV_INS_OPL_DRUMS) numCols=13; if (ins->type==DIV_INS_OPLL) numCols=12; if (ins->type==DIV_INS_OPZ) numCols=19; if (ImGui::BeginTable("FMOperators",numCols,ImGuiTableFlags_SizingStretchProp|ImGuiTableFlags_BordersH|ImGuiTableFlags_BordersOuterV)) { @@ -1785,7 +1794,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TextUnformatted("Other"); } ImGui::TableNextColumn(); - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { ImGui::TableNextColumn(); CENTER_TEXT(FM_NAME(FM_WS)); ImGui::TextUnformatted(FM_NAME(FM_WS)); @@ -1801,14 +1810,16 @@ void FurnaceGUI::drawInsEdit() { float sliderHeight=32.0f*dpiScale; for (int i=0; ifm.op[(opCount==4)?opOrder[i]:i]; + DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; ImGui::TableNextRow(); ImGui::TableNextColumn(); // push colors if (settings.separateFMColors) { bool mod=true; - if (opCount==4) { + if (ins->type==DIV_INS_OPL_DRUMS) { + mod=false; + } else if (opCount==4) { if (ins->type==DIV_INS_OPL) { if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; } else { @@ -1837,7 +1848,9 @@ void FurnaceGUI::drawInsEdit() { if (i==0) sliderHeight=(ImGui::GetContentRegionAvail().y/opCount)-ImGui::GetStyle().ItemSpacing.y; ImGui::PushID(fmt::sprintf("op%d",i).c_str()); - if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { + if (ins->type==DIV_INS_OPL_DRUMS) { + ImGui::Text("%s",oplDrumNames[i]); + } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { if (i==1) { ImGui::Text("Kick"); } else { @@ -1855,7 +1868,7 @@ void FurnaceGUI::drawInsEdit() { maxTl=63; } } - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { maxTl=63; } int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15; @@ -1989,7 +2002,7 @@ void FurnaceGUI::drawInsEdit() { } } - if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPZ) { + if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ) { ImGui::TableNextColumn(); ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); ImGui::TableNextColumn(); @@ -2024,7 +2037,7 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER op.ksr=ksrOn; } - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER op.sus=susOn; } @@ -2035,7 +2048,7 @@ void FurnaceGUI::drawInsEdit() { } } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { ImGui::TableNextColumn(); ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); ImGui::TableNextColumn(); @@ -2043,7 +2056,7 @@ void FurnaceGUI::drawInsEdit() { drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if (ins->type==DIV_INS_OPL && ImGui::IsItemHovered()) { + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); } } else if (ins->type==DIV_INS_OPLL) { @@ -2052,7 +2065,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::TableNextColumn(); - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); if (settings.separateFMColors) { popAccentColors(); @@ -2078,7 +2091,7 @@ void FurnaceGUI::drawInsEdit() { } if (ImGui::BeginTable("FMOperators",columns,ImGuiTableFlags_SizingStretchSame)) { for (int i=0; ifm.op[(opCount==4)?opOrder[i]:i]; + DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; if ((settings.fmLayout!=3 && ((i+1)&1)) || i==0 || settings.fmLayout==2) ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Separator(); @@ -2087,7 +2100,9 @@ void FurnaceGUI::drawInsEdit() { // push colors if (settings.separateFMColors) { bool mod=true; - if (opCount==4) { + if (ins->type==DIV_INS_OPL_DRUMS) { + mod=false; + } else if (opCount==4) { if (ins->type==DIV_INS_OPL) { if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; } else { @@ -2114,7 +2129,9 @@ void FurnaceGUI::drawInsEdit() { } ImGui::Dummy(ImVec2(dpiScale,dpiScale)); - if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { + if (ins->type==DIV_INS_OPL_DRUMS) { + ImGui::Text("%s",oplDrumNames[i]); + } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { if (i==1) { ImGui::Text("Envelope 2 (kick only)"); } else { @@ -2139,7 +2156,7 @@ void FurnaceGUI::drawInsEdit() { maxTl=63; } } - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { maxTl=63; } int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15; @@ -2149,7 +2166,7 @@ void FurnaceGUI::drawInsEdit() { bool vibOn=op.vib; bool susOn=op.sus; // don't you make fun of this one unsigned char ssgEnv=op.ssgEnv&7; - if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPZ) { + if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ) { ImGui::SameLine(); if (ImGui::Checkbox((ins->type==DIV_INS_OPLL)?FM_NAME(FM_EGS):"SSG On",&ssgOn)) { PARAMETER op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); @@ -2161,7 +2178,7 @@ void FurnaceGUI::drawInsEdit() { } } - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { ImGui::SameLine(); if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER op.sus=susOn; @@ -2169,7 +2186,7 @@ void FurnaceGUI::drawInsEdit() { } //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ @@ -2292,12 +2309,12 @@ void FurnaceGUI::drawInsEdit() { } } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if (ins->type==DIV_INS_OPL && ImGui::IsItemHovered()) { + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); } ImGui::TableNextColumn(); @@ -2307,7 +2324,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } - if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER op.vib=vibOn; } @@ -2338,7 +2355,7 @@ void FurnaceGUI::drawInsEdit() { } else { macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_ALG),&ins->std.algMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); - if (ins->type!=DIV_INS_OPL) { + if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS) { if (ins->type==DIV_INS_OPZ) { // TODO: FMS2/AMS2 macros macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FMS),&ins->std.fmsMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); @@ -2361,7 +2378,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } for (int i=0; itype==DIV_INS_OPL_DRUMS) { + snprintf(label,31,"%s Macros",oplDrumNames[i]); + } else { + snprintf(label,31,"OP%d Macros",i+1); + } if (ImGui::BeginTabItem(label)) { ImGui::PushID(i); int ordi=(opCount==4)?orderedOps[i]:i; @@ -2373,12 +2394,12 @@ void FurnaceGUI::drawInsEdit() { maxTl=63; } } - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { maxTl=63; } int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15; - if (ins->type==DIV_INS_OPL) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DR),&ins->std.opMacros[ordi].drMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index f85a323d6..406d3ccce 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -21,9 +21,6 @@ #include "imgui_internal.h" #include -// TODO: -// - potentially move oscilloscope seek position to the end, and read the last samples -// - this allows for setting up the window size void FurnaceGUI::readOsc() { int writePos=e->oscWritePos; int readPos=e->oscReadPos; @@ -47,8 +44,11 @@ void FurnaceGUI::readOsc() { total=oscTotal+(bias>>6); if (total>avail) total=avail; //printf("total: %d. avail: %d bias: %d\n",total,avail,bias); + + int winSize=e->getAudioDescGot().rate*(oscWindowSize/1000.0); + int oscReadPos=(writePos-winSize)&0x7fff; for (int i=0; i<512; i++) { - int pos=(readPos+(i*total/512))&0x7fff; + int pos=(oscReadPos+(i*winSize/512))&0x7fff; oscValues[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; if (oscValues[i]>0.001f || oscValues[i]<-0.001f) { WAKE_UP; @@ -97,6 +97,14 @@ void FurnaceGUI::drawOsc() { if (oscZoom>2.0) oscZoom=2.0; } ImGui::SameLine(); + if (ImGui::VSliderFloat("##OscWinSize",ImVec2(20.0f*dpiScale,ImGui::GetContentRegionAvail().y),&oscWindowSize,5.0,100.0)) { + if (oscWindowSize<5.0) oscWindowSize=5.0; + if (oscWindowSize>100.0) oscWindowSize=100.0; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) { + oscWindowSize=20.0; + } + ImGui::SameLine(); } ImDrawList* dl=ImGui::GetWindowDrawList(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 78c81e2cb..8176c6be6 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -404,6 +404,11 @@ void FurnaceGUI::drawSettings() { settings.cursorMoveNoScroll=cursorMoveNoScrollB; } + bool doubleClickColumnB=settings.doubleClickColumn; + if (ImGui::Checkbox("Double click selects entire column",&doubleClickColumnB)) { + settings.doubleClickColumn=doubleClickColumnB; + } + bool allowEditDockingB=settings.allowEditDocking; if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { settings.allowEditDocking=allowEditDockingB; @@ -2009,7 +2014,7 @@ void FurnaceGUI::syncSettings() { settings.insCellSpacing=e->getConfInt("insCellSpacing",0); settings.volCellSpacing=e->getConfInt("volCellSpacing",0); settings.effectCellSpacing=e->getConfInt("effectCellSpacing",0); - settings.effectValCellSpacing=e->getConfInt("effectValCellSpacing",0); + settings.doubleClickColumn=e->getConfInt("doubleClickColumn",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -2092,6 +2097,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.volCellSpacing,0,32); clampSetting(settings.effectCellSpacing,0,32); clampSetting(settings.effectValCellSpacing,0,32); + clampSetting(settings.doubleClickColumn,0,1); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2223,6 +2229,7 @@ void FurnaceGUI::commitSettings() { e->setConf("volCellSpacing",settings.volCellSpacing); e->setConf("effectCellSpacing",settings.effectCellSpacing); e->setConf("effectValCellSpacing",settings.effectValCellSpacing); + e->setConf("doubleClickColumn",settings.doubleClickColumn); // colors for (int i=0; i