Skip to content

Commit

Permalink
Update OneSmallStep 0.9.14 > 0.9.15 (#1432)
Browse files Browse the repository at this point in the history
  • Loading branch information
BenTalagan committed Sep 25, 2024
1 parent b9fadd0 commit acb633a
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 24 deletions.
95 changes: 91 additions & 4 deletions MIDI Editor/talagan_OneSmallStep.lua
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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[[^@?(.*[\/])[^\/]-$]]
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions MIDI Editor/talagan_OneSmallStep/classes/engine_lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -404,6 +405,7 @@ return {
ED = ED,
SNP = SNP,
N = N,
ART = ART,

atStart = atStart,
atExit = atExit,
Expand Down
50 changes: 50 additions & 0 deletions MIDI Editor/talagan_OneSmallStep/classes/modules/articulations.lua
Original file line number Diff line number Diff line change
@@ -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
}
92 changes: 72 additions & 20 deletions MIDI Editor/talagan_OneSmallStep/classes/modules/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 },

Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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;
Expand All @@ -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];

Expand All @@ -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
Expand Down Expand Up @@ -242,6 +291,9 @@ return {
resetSetting = resetSetting,
getSettingSpec = getSettingSpec,

getTrackSetting = getTrackSetting,
setTrackSetting = setTrackSetting,

setPlaybackMeasureCount = setPlaybackMeasureCount,
getPlaybackMeasureCount = getPlaybackMeasureCount,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit acb633a

Please sign in to comment.