From acb633a369981d906a147763b222f1cc32b0519e Mon Sep 17 00:00:00 2001 From: Ben Talagan Babut Date: Wed, 25 Sep 2024 14:46:58 +0200 Subject: [PATCH] Update OneSmallStep 0.9.14 > 0.9.15 (#1432) --- MIDI Editor/talagan_OneSmallStep.lua | 95 ++++++++++++++++++- .../classes/engine_lib.lua | 2 + .../classes/modules/articulations.lua | 50 ++++++++++ .../classes/modules/settings.lua | 92 ++++++++++++++---- .../classes/operations/insert.lua | 6 ++ .../classes/operations/navigate.lua | 1 + .../classes/operations/repitch.lua | 6 ++ .../classes/operations/replace.lua | 6 ++ .../classes/operations/stretch.lua | 6 ++ .../classes/operations/stuff.lua | 6 ++ .../classes/operations/write.lua | 7 ++ 11 files changed, 253 insertions(+), 24 deletions(-) create mode 100644 MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua diff --git a/MIDI Editor/talagan_OneSmallStep.lua b/MIDI Editor/talagan_OneSmallStep.lua index 7516f9c17..a83982a31 100644 --- a/MIDI Editor/talagan_OneSmallStep.lua +++ b/MIDI Editor/talagan_OneSmallStep.lua @@ -1,6 +1,6 @@ --[[ @description One Small Step : Alternative Step Input -@version 0.9.14 +@version 0.9.15 @author Ben 'Talagan' Babut @license MIT @metapackage @@ -57,8 +57,7 @@ @screenshot https://stash.reaper.fm/48269/oss_094.png @changelog - - [Feature] Added note highlighting during play - - [Feature] Reintroduced "Disarm OSS" mechanism + - [Feature] [Experimental] Markup Articulation Manager @about # Purpose @@ -89,7 +88,7 @@ --]] -VERSION = "0.9.14" +VERSION = "0.9.15" DOC_URL = "https://bentalagan.github.io/onesmallstep-doc/index.html?ver=" .. VERSION PATH = debug.getinfo(1,"S").source:match[[^@?(.*[\/])[^\/]-$]] @@ -138,6 +137,7 @@ local MK = E.MK local TGT = E.TGT local F = E.F local ED = E.ED +local ART = E.ART -- Get the debugger setting at launch local DEBUGGER_IS_ON = S.getSetting("UseDebugger") @@ -1482,6 +1482,55 @@ function SettingsPanel() reaper.ImGui_EndTabItem(ctx) end + if reaper.ImGui_BeginTabItem(ctx, 'Articulations') then + ImGui_VerticalSpacer(ctx,5) + + local track = nil; + local take = TGT.TakeForEdition() + + if take then + track = reaper.GetMediaItemTake_Track(take) + else + track = TGT.TrackForEditionIfNoItemFound() + end + + local help = "The articulation markup manager is an experimental feature that will create text events\n\z + in the \"Text Events\" CC Lane automatically to match notes that are KeySwitches.\n\z + Basically, it aims to translate KeySwitches from a note representation to a more\n\z + compact/friendly/readable horizontal representation.\n\z + \n\z + Those events will be created/synced after each note input, but also when OSS is running and\n\z + and you're mouse editing the notes in the MIDI Editor (a background task is watching those changes\n\z + and automatically converts them to the \"Text Events\" CC Lane).\n\z + \n\z + Inversely, notes that are suppressed will see their corresponding text events disappear.\n\z + \n\z + The condition for creating such a text event is that a note name is attached to the note/key\n\z + that was pressed/modified. For this to work, you need to load a Note Name map file on your track's\n\z + piano roll (note that this can be done per channel).\n\z" + + if track then + local curval = S.getTrackSetting(track, "OSSArticulationManagerEnabled") + + if reaper.ImGui_Checkbox(ctx, "Use articulation markup manager", curval) then + S.setTrackSetting(track, "OSSArticulationManagerEnabled", not curval); + end + + SL(); + reaper.ImGui_TextColored(ctx, 0xB0B0B0FF, "(?)"); + TT(help) + SL(); + reaper.ImGui_TextColored(ctx, 0x00EEFFFF, "[Per track setting]") + else + reaper.ImGui_TextColored(ctx, 0x00EEFFFF, "Please select a track to access the articulation manager") + SL(); + reaper.ImGui_TextColored(ctx, 0xB0B0B0FF, "(?)"); + TT(help) + end + + reaper.ImGui_EndTabItem(ctx) + end + if reaper.ImGui_TabItemButton(ctx, "?##go_to_help") then reaper.CF_ShellExecute(DOC_URL) end @@ -1623,7 +1672,45 @@ local function UpdateToolbarButtonState(v) reaper.RefreshToolbar2(sectionID, cmdID); end + +local lastArtUpdateProjState = nil; +local lastArtUpdateTake = nil; +local lastArtUpdateWatch = nil; + +local function WatchForArticulationsToUpdate() + if not lastArtUpdateWatch or reaper.time_precise() - lastArtUpdateWatch > 0.1 then + lastArtUpdateWatch = reaper.time_precise(); + + local take = TGT.TakeForEdition(); + + if not take then + return + end + + local track = reaper.GetMediaItemTake_Track(take) + + if not S.getTrackSetting(track, "OSSArticulationManagerEnabled") then + -- Nothing to do, but reset stuff + lastArtUpdateTake = nil + return + end + + local pscc = reaper.GetProjectStateChangeCount(); + + if take ~= lastArtUpdateTake or pscc ~= lastArtUpdateProjState then + lastArtUpdateTake = take; + lastArtUpdateProjState = pscc; + + ART.UpdateArticulationTextEventsIfNeeded(track, take) + end + + end +end + function MainLoop() + + WatchForArticulationsToUpdate(); + local engine_ret = E.atLoop(); if engine_ret == -42 then diff --git a/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua b/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua index 459e4877f..e3a43c6f4 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua @@ -25,6 +25,7 @@ local TGT = require "modules/target" local F = require "modules/focus" local MOD = require "modules/modifiers" local ED = require "modules/edition" +local ART = require "modules/articulations" local NAVIGATE = require "operations/navigate" local REPITCH = require "operations/repitch" @@ -404,6 +405,7 @@ return { ED = ED, SNP = SNP, N = N, + ART = ART, atStart = atStart, atExit = atExit, diff --git a/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua b/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua new file mode 100644 index 000000000..de9b792fb --- /dev/null +++ b/MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua @@ -0,0 +1,50 @@ +-- @noindex +-- @author Ben 'Talagan' Babut +-- @license MIT +-- @description This is part of One Small Step + +local MU = require "lib/MIDIUtils" +local S = require "modules/settings" + +local TEXT_CHANNEL = 1 -- This is the one which is called "Text", I hesitate with "Program", which is 8 + +local function UpdateArticulationTextEventsIfNeeded(track, take) + + if not track or not take then + return + end + + if not S.getTrackSetting(track, "OSSArticulationManagerEnabled") then + return + end + + MU.MIDI_InitializeTake(take) + MU.MIDI_OpenWriteTransaction(take) + + local _, nc, _, tsc = MU.MIDI_CountEvts(take) + for ti = tsc, 1, -1 do + local _, _, _, _, type, msg = MU.MIDI_GetTextSysexEvt(take, ti - 1) + + -- Avoid destroying texts from other tool/people, we're not alone here + -- Beware, we use an unicode diamond char and string.sub is picky + if type == TEXT_CHANNEL and string.sub(msg,1,4) == "◈ " then + MU.MIDI_DeleteTextSysexEvt(take, ti - 1) + end + end + + for ni = 1, nc do + local _, _, _, startppqpos, _, chan, pitch, _ = MU.MIDI_GetNote(take, ni - 1) + + local note_name = reaper.GetTrackMIDINoteNameEx(0, track, pitch, chan) + + if note_name ~= nil and note_name ~= "" then + MU.MIDI_InsertTextSysexEvt(take, false, false, startppqpos, 1, "◈ " .. note_name) + end + end + + MU.MIDI_CommitWriteTransaction(take); +end + +return { + UpdateArticulationTextEventsIfNeeded= UpdateArticulationTextEventsIfNeeded +} diff --git a/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua b/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua index 3e2e15b2f..02c7291de 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua @@ -5,6 +5,10 @@ local D = require "modules/defines" +local TrackSettingDefs = { + OSSArticulationManagerEnabled = { type = "bool", default = false } +} + local SettingDefs = { StepBackModifierKey = { type = "int", default = D.IsMacOs and 16 or 16 }, @@ -63,7 +67,6 @@ local SettingDefs = { Disarmed = { type = "bool", default = false }, NoteHiglightingDuringPlay = { type = "bool", default = false }, - UseDebugger = { type = "bool", default = false }, VelocityLimiterEnabled = { type = "bool", default = false }, @@ -101,14 +104,8 @@ local function unsafestr(str) return str end -local function getSetting(setting) - local spec = SettingDefs[setting]; - - if spec == nil then - error("Trying to get unknown setting " .. setting); - end - - local val = unsafestr(reaper.GetExtState("OneSmallStep", setting)); +local function serializedStringToValue(str, spec) + local val = unsafestr(str); if val == nil then val = spec.default; @@ -123,8 +120,37 @@ local function getSetting(setting) -- No conversion needed end end - return val; + + return val +end + +local function valueToSerializedString(val, spec) + local str = '' + if spec.type == 'bool' then + str = (val == true) and "true" or "false"; + elseif spec.type == 'int' then + str = tostring(val); + elseif spec.type == 'double' then + str = tostring(val); + elseif spec.type == "string" then + -- No conversion needed + str = val + end + return str +end + +local function getSetting(setting) + local spec = SettingDefs[setting]; + + if spec == nil then + error("Trying to get unknown setting " .. setting); + end + + local str = reaper.GetExtState("OneSmallStep", setting) + + return serializedStringToValue(str, spec) end + local function setSetting(setting, val) local spec = SettingDefs[setting]; @@ -135,18 +161,41 @@ local function setSetting(setting, val) if val == nil then reaper.DeleteExtState("OneSmallStep", setting, true); else - if spec.type == 'bool' then - val = (val == true) and "true" or "false"; - elseif spec.type == 'int' then - val = tostring(val); - elseif spec.type == 'double' then - val = tostring(val); - elseif spec.type == "string" then - -- No conversion needed - end - reaper.SetExtState("OneSmallStep", setting, val, true); + local str = valueToSerializedString(val, spec); + reaper.SetExtState("OneSmallStep", setting, str, true); + end +end + +local function getTrackSetting(track, setting) + local spec = TrackSettingDefs[setting]; + + if track == nil then + error("Trying to get setting " .. setting .. " from nil track ") + end + + if spec == nil then + error("Trying to get unknown track setting " .. setting); + end + + local succ, str = reaper.GetSetMediaTrackInfo_String(track, "P_EXT:OneSmallStep:" .. setting, '', false); + + return serializedStringToValue(str or '', spec) +end + +local function setTrackSetting(track, setting, val) + local spec = TrackSettingDefs[setting]; + + if track == nil then + error("Trying to set setting " .. setting .. " to nil track ") + end + if spec == nil then + error("Trying to set unknown track setting " .. setting); end + + local str = valueToSerializedString(val, spec); + reaper.GetSetMediaTrackInfo_String(track, "P_EXT:OneSmallStep:" .. setting, str, true) end + local function resetSetting(setting) setSetting(setting, SettingDefs[setting].default) end @@ -242,6 +291,9 @@ return { resetSetting = resetSetting, getSettingSpec = getSettingSpec, + getTrackSetting = getTrackSetting, + setTrackSetting = setTrackSetting, + setPlaybackMeasureCount = setPlaybackMeasureCount, getPlaybackMeasureCount = getPlaybackMeasureCount, diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua index 5389c2ff6..cff59f387 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/insert.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -111,6 +113,8 @@ local function Insert(km, track, take, new_notes, held_notes, triggered_by_key_e GEN.AddAndExtendNotes(c, new_notes, held_notes) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -140,6 +144,8 @@ local function InsertBack(km, track, take, notes_to_shorten, triggered_by_key_ev GEN.GenericDelete(c,notes_to_shorten, false, true) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua index 061a3a30c..421d95dc6 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/navigate.lua @@ -6,6 +6,7 @@ local SNP = require "modules/snap" local T = require "modules/time" local S = require "modules/settings" +local ART = require "modules/articulations" local function Navigate(track, direction) local ns = SNP.nextSnapFromCursor(track, direction) diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua index c7acf3ba1..e29ed7938 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/repitch.lua @@ -8,6 +8,8 @@ local T = require "modules/time" local S = require "modules/settings" local N = require "modules/notes" local D = require "modules/defines" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -140,6 +142,8 @@ local function Repitch(km, track, take, notes_to_add, notes_to_extend, triggered end end + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -162,6 +166,8 @@ local function RepitchBack(km, track, take, notes_to_add, notes_to_extend, trigg T.KeepEditCursorOnScreen() end + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua index 367da873a..54bde474f 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/replace.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -134,6 +136,8 @@ local function Replace(km, track, take, notes_to_add, notes_to_extend, triggered GEN.AddAndExtendNotes(c, notes_to_add, notes_to_extend) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1) end @@ -162,6 +166,8 @@ local function ReplaceBack(km, track, take, notes_to_shorten, triggered_by_key_e GEN.GenericDelete(c, notes_to_shorten, false, false) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua index a925dff32..addb626ea 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/stretch.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local D = require "modules/defines" local N = require "modules/notes" local MK = require "modules/markers" +local ART = require "modules/articulations" + local MU = require "lib/MIDIUtils" local GEN = require "operations/generic" @@ -125,6 +127,8 @@ local function Stretch(km, track, take, notes_to_add, notes_to_extend, triggered GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end @@ -241,6 +245,8 @@ function StretchBack(km, track, take, notes_to_shorten, triggered_by_key_event) GEN.BackwardOperationFinish(c, c.rewindTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua index 3ab9c77dc..30eb8fba5 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/stuff.lua @@ -8,6 +8,8 @@ local S = require "modules/settings" local D = require "modules/defines" local N = require "modules/notes" local MK = require "modules/markers" +local ART = require "modules/articulations" + local GEN = require "operations/generic" local MU = require "lib/MIDIUtils" @@ -191,6 +193,8 @@ local function Stuff(km, track, take, notes_to_add, notes_to_extend, triggered_b -- Pass nil as jump time, we don't want to jump in stuff mode GEN.ForwardOperationFinish(c, nil, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts),-1); end @@ -356,6 +360,8 @@ local function StuffBack(km, track, take, notes_to_shorten, triggered_by_key_eve GEN.BackwardOperationFinish(c, nil) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts),-1); end diff --git a/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua b/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua index 642b911f9..744c01e05 100644 --- a/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua +++ b/MIDI Editor/talagan_OneSmallStep/classes/operations/write.lua @@ -7,6 +7,8 @@ local T = require "modules/time" local S = require "modules/settings" local D = require "modules/defines" local AT = require "modules/action_triggers" +local ART = require "modules/articulations" + local MU = require "lib/MIDIUtils" local GEN = require "operations/generic" @@ -22,9 +24,12 @@ local function Write(km, track, take, notes_to_add, notes_to_extend, triggered_b MU.MIDI_InitializeTake(take) MU.MIDI_OpenWriteTransaction(take) + GEN.AddAndExtendNotes(c, notes_to_add, notes_to_extend) GEN.ForwardOperationFinish(c, c.advanceTime, newMaxQN) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(1, c.counts), -1); end @@ -77,6 +82,8 @@ local function WriteBack(km, track, take, notes_to_shorten, triggered_by_key_eve GEN.BackwardOperationFinish(c, jumpTime) + ART.UpdateArticulationTextEventsIfNeeded(track, take); + reaper.Undo_EndBlock(GEN.OperationSummary(-1, c.counts), -1) end