Human being with feelings
Join Date: Jul 2009
Posts: 3,714
|
Quote:
Originally Posted by lowellben
Of course I already requested that a while ago, that was my first idea.
I would love to be able to auto-assign-attach a dynamic marking depending on the velocity of a note, but no script has been created so far So, I didn't push the issue.
|
Here is a (beta version of) a script that can add either lyrics or dynamics to selected notes in all editable takes:
Code:
--[[
ReaScript name: js_Convert velocities of selected notes to dynamics or lyrics.lua
Version: 0.92
Author: juliansader
Website: http://forum.cockos.com/showthread.php?p=1851179#post1851179
Donation: https://www.paypal.me/juliansader
REAPER version: 5.30 or later
]]
--[[
Changelog:
* v0.90 (2017-06-01)
+ Initial beta release
* v0.91 (2017-06-02)
+ Somewhat improved speed
* v0.92 (2017-06-02)
+ Option to skip redundant dynamics
]]
-------------------------------------------------------------------
-- USER AREA
-- Settings that the user can customize
insertDynamicsOrLyrics = "Dynamics" -- Either "Dynamics" or "Lyrics"
-- In the case of dynamics, these two tables give the (lower) cut-off values and text for each dynamic
-- (The tables can be customized to any length, as long as both have the same number of entries.)
tableCutoffValues = {0, 25, 50, 75, 100}
tableDynamics = {"pp", "p", "mp", "f", "ff"}
-- Should redundant, repeated dynamics be skipped? If "false", each and every note will have a dynamic indication.
skipRedundantDynamics = true
-- End of USER AREA
-------------------------------------------------------------------
if not reaper.APIExists("MIDI_GetAllEvts") then
reaper.ShowMessageBox("This script requires REAPER v5.30 or higher.", "ERROR", 0)
return(false)
end
local s_unpack = string.unpack
local s_pack = string.pack
-- REAPER does not provide API functions to enumerate the editable takes in a MIDI editor.
-- This scripts uses a 'trick' to find these editable takes: apply a native Action that affect all editable takes,
-- and then check all takes' MIDI stings to find those that wer changed by the Action.
-- Find all takes' original, pre-Action MIDI data
local tableMIDIdata = {}
for i=0, reaper.CountMediaItems(0)-1 do
local curItem = reaper.GetMediaItem(0, i)
if reaper.ValidatePtr2(0, curItem, "MediaItem*") then
for t=0, reaper.CountTakes(curItem)-1 do
local curTake = reaper.GetTake(curItem, t)
if reaper.ValidatePtr2(0, curTake, "MediaItem_Take*") and reaper.TakeIsMIDI(curTake) then
-- Should we use MIDI string or MIDI hashes to compare pre- and post-Action?
-- Strangely, MIDI hashes may change even if the MIDI data didn't really change.
-- So rather use the MIDI strings.
gotDataOK, tableMIDIdata[curTake] = reaper.MIDI_GetAllEvts(curTake, "")
--gotDataOK, tableMIDIdata[curTake] = reaper.MIDI_GetHash(curTake, true, "")
end -- if reaper.ValidatePtr2(0, curItem, "MediaItem_Take*") and reaper.TakeIsMIDI(curTake)
end -- for t=0, numTakes-1
end -- if reaper.ValidatePtr2(0, curItem, "MediaItem*")
end -- for i=0, numItems-1
---------------------------------------------------------------------------------------------------------------
-- Now apply a native Action that is guaranteed to change some MIDI in all *editable* takes with selected notes
-- The script will replace the MIDI data of all takes that are changed by the native Action, so no need to
-- undo the Action.
-- To prevent the Action from creating an undo point, start an Undo Block before calling Action.
editor = reaper.MIDIEditor_GetActive()
reaper.Undo_BeginBlock2(0)
reaper.MIDIEditor_OnCommand(editor, 40909) -- Edit: Invert voicing downwards for selected notes (all editable)
-----------------------------------------------------------------------------------------------------------
-- Find all takes in which the MIDI data changed, by comparing their new MIDI data with the pre-Action data
for take, oldMIDIdata in pairs(tableMIDIdata) do
local gotDataOK, newMIDIdata = reaper.MIDI_GetAllEvts(take, "")
--local gotDataOK, newMIDIdata = reaper.MIDI_GetHash(take, true, "")
if newMIDIdata == oldMIDIdata then
tableMIDIdata[take] = nil
end
end
-----------------------------------------------------------------------------------------
-- Now iterate through all editable takes, inserting lyrics or dynamics at selected notes
for take, MIDIstring in pairs(tableMIDIdata) do
local tableMIDIevents = {} -- Each take's MIDI events will be stored one-by-one in this table, until concatenated again
local t = 0 -- Index inside tableMIDIevents
local MIDIlen = MIDIstring:len()
local stringPos = 1
local offset, flags, msg, velocityMsg -- velocityMsg will depend on whether lyrics or dynamics should be inserted
local previousDynamic
while stringPos < MIDIlen do
offset, flags, msg, stringPos = s_unpack("i4Bs4", MIDIstring, stringPos)
if msg ~= "" then
local velocity = msg:byte(3)
-- If note-on, insert new lyric or dynamic marker
if msg:byte(1)>>4 == 9 and velocity ~= 0 and flags&1==1 then
if insertDynamicsOrLyrics == "Dynamics" then
-- Code for inserting dynamics
for i = #tableCutoffValues, 1, -1 do
if velocity >= tableCutoffValues[i] then
if skipRedundantDynamics and i == previousDynamic then
velocityMsg = ""
else
velocityMsg = string.char(0xFF, 0x0F) .. "TRAC dynamic " .. tableDynamics[i]
end
previousDynamic = i
break
end
end
else
-- Code for inserting a lyric
velocityMsg = string.char(0xFF, 0x05) .. tostring(msg:byte(3))
end
t = t + 1
tableMIDIevents[t] = s_pack("i4Bs4", offset, 0, velocityMsg)
t = t + 1
tableMIDIevents[t] = s_pack("i4Bs4", 0, flags, msg)
else
t = t + 1
tableMIDIevents[t] = s_pack("i4Bs4", offset, flags, msg)
end
end
end -- while stringPos < MIDIlen
reaper.MIDI_SetAllEvts(take, table.concat(tableMIDIevents))
end
-------------------------------------------------------------------------
-- Creat Undo point
-- flag=4 should limit the information that is included in the undo point
-- to changes that were made to *items*, so the creation of the undo point is hopefully much faster.
if insertDynamicsOrLyrics == "Dynamics" then
undoText = "Convert note velocities to dynamics"
else
undoText = "Convert note velocities to lyrics"
end
reaper.Undo_EndBlock2(0, undoText, 4)
This code uses a newfangled "trick" to find all editable takes the MIDI editor, since REAPER's API does not provide this information natively. There might still be a bug in my trick, so this is just a beta version of the script.
In the script's USER AREA, you can select between lyrics or dynamics, and customize the dynamics.
EDIT: update 0.92: Option to skip redundant dynamics indications
-
Last edited by juliansader; 06-05-2017 at 10:47 AM.
|