Sample Algorithmic Script

As a proof of concept that this was possible, I wrote a Dorico Lua script that generates 10 random major triads in root position.

This is nothing of value except to encourage folks to write scripts that do more than regurgitate Dorico commands. Please forgive my Lua expertise - it’s very rough and I’ve not played with it for many years.

I’ve not taken significant time to explore what’s possible, for I’m sure that a manual will come out some time soon. I wrote this after recording a script of inputting MIDI notes and chords and then looking at what code was generated.

Having said all of that, it would be fabulous if some sort of community library evolved that used appropriate namespace, objects, etc. so that we could share. My personal goal is from the point of view of an educator - I’d like to be able to generate, for my students, random worksheets based upon constraints I set.

To play with this script:

  • Copy the code and pop it into a xxx.lua file located somewhere you can find it.
  • In your Dorico score, Go into write mode, note input mode, put the cursor where you want it, and set the note value to what you want it to be.
  • Do Script->Run Script… and choose your script.
  • Edit it at will, and use the console to debug your changes

Cheers!
Henry

--[[
	Requirements:
	
	- Be in Write Mode
	- Be in Note Entry mode
	- Have the position where you want the notes entered
	- Have the note value already chosen
	
	Run the macro to get 10 major triads in root position.  I have no control over how Dorico will spell them.
	
	Notes:
	
	This is a simple little test of Dorico for algorithmic generation.
	My personal interest is in creating worksheets for students.
	The goal of this test was to verify that this was a fully functionling Lua that I could
	use to generate notes and to get enough of it working that I might start to think of ways to create worksheets
	for students.
	
	Warning - this is not well crafted code! Just a quick proof of concept. With that caveat, feel free to
	use any part of this freely.
--]]

local app=DoApp.DoApp()

-- Info for the console.
local info = debug.getinfo(1,'S')
print("Running script: " .. info.source)
print("Dorico Lua Version: " .. _VERSION)

MIDI_NOTE_MIN = 0
MIDI_NOTE_MAX = 127

-- Seed the random number and run through a couple of times to avoid first or more repeated numbers
-- Don't know if needed...
-- see https://stackoverflow.com/questions/20154991/generating-uniform-random-numbers-in-lua
math.randomseed(os.time())
math.random(); math.random(); math.random()

-- For debugging
function print_r(arr, indentLevel)
    local str = ""
    local indentStr = "#"

    if(indentLevel == nil) then
        print(print_r(arr, 0))
        return
    end

    for i = 0, indentLevel do
        indentStr = indentStr.."\t"
    end

    for index,value in pairs(arr) do
        if type(value) == "table" then
            str = str..indentStr..index..": \n"..print_r(value, (indentLevel + 1))
        else 
            str = str..indentStr..index..": "..value.."\n"
        end
    end
    return str
end


-- Does "MIDI Input" of a chord, assuming Dorico is in Note Input mode and has a given duration
-- No input checking at the moment.
-- Returns nothing.
function  noteInputChord(array)

	pitches = ""

	for index, value in next, array do
		if (index > 1) then pitches = pitches .. "\\," end
		pitches = pitches .. value
	end
	
	app:doCommand("NoteInput.MIDINoteInput?MIDIPitches=" .. pitches)
	
end

-- Pass in a root (MIDI number).  Will limit the root to usable values if needed
-- Returns array of major triad (MIDI notes) in root position
function majorChord(root)
	root = limitRange( root, MIDI_NOTE_MIN, MIDI_NOTE_MAX - 7)
	return { root, root+4, root+7 }
end

-- Limits the range of a number to two
function limitRange(num, min, max)
	if num < min then num = min elseif num > max then num = max end
	return num
end

-- Limits the range of a MIID NOTE to a valid number
function limitNoteRange(num)
	-- limit to integer
	num = math.floor(num)
	
	-- limit to valid range
	return limitRange(num, MIDI_NOTE_MIN, MIDI_NOTE_MAX)
end



-- Accepts array with min/max keys.
-- Returns array of major triad (MIDI notes) in root position, root being passed in as MIDI number
function randomMajorChord(arr)
	local function _randomMajorChord(min, max)
		-- Flip min/max if in wrong order
		if (min > max) then 
			min, max = max, min 
			print ("Warning: min/max flipped in randomMajorChord() call")
		end
		-- print("Random: " .. min .. ", " .. max .. "\n")
		return majorChord( math.random(min, max) )
	end
	
	-- Verify the arguments and correct if needed
	if type(arr) ~= "table" then 
		arr = { min = 60, max = 71 } 
	else
		if type(arr.min) ~= "number" then arr.min = 60 else arr.min = limitNoteRange(arr.min) end
		if type(arr.max) ~= "number" then arr.max = 71 else arr.max = limitNoteRange(arr.max) end
	end

	-- print_r(arr)	
	return _randomMajorChord( arr.min or 60, arr.max or 71 )
end

-- noteInputChord({48, 55, 52})
-- noteInputChord({60, 64, 67})

-- Change this loop
for i = 1, 10 do

	-- Change this to set different min / max notes
	-- 60 = middle C
	noteInputChord(randomMajorChord({min = 60, max = 79}))
	
end



print("Success!\n")

It’s great that you’ve been able to get creative with the scripting API, but please do be aware that it will change substantially in the future. We had hoped to get the scripting more fully formed in version 1 but there were many other demands on our time. As the design of the application and its modes has settled we’re in a better state to re-evaluate how best to expose scripting in a future version and give access to a wider variety of objects.

This shouldn’t require too much editing of existing scripts as long as you separate out your calculation logic from the point of calling a Dorico function, as you have done here.

Wonderful to see some people starting to materialize their interest in the scripting capabilities of the software. I first came across Alexander Plötz’s automation of slash notation yesterday, in Scoring Notes, and now this. I should try my hand at this when I have the time. Great to see that it can be used for creative purposes!

Thank you for the inspiration!

For those who havn’t yet seen it: