-- @noindex chords = { { name = 'major', code = 'major', display = '', pattern = '10001001' }, { name = 'minor', code = 'minor', display = 'm', pattern = '10010001' }, { name = 'power chord', code = 'power', display = '5', pattern = '10000001' }, { name = 'suspended second', code = 'sus2', display = 'sus2', pattern = '10100001' }, { name = 'suspended fourth', code = 'sus4', display = 'sus4', pattern = '10000101' }, { name = 'diminished', code = 'dim', display = 'dim', pattern = '1001001' }, { name = 'augmented', code = 'aug', display = 'aug', pattern = '100010001' }, { name = 'major sixth', code = 'maj6', display = '6', pattern = '1000100101' }, { name = 'minor sixth', code = 'min6', display = 'm6', pattern = '1001000101' }, { name = 'dominant seventh', code = '7', display = '7', pattern = '10001001001' }, { name = 'major seventh', code = 'maj7', display = 'maj7', pattern = '100010010001' }, { name = 'minor seventh', code = 'min7', display = 'm7', pattern = '10010001001' }, { name = 'flat fifth', code = 'flat5', display = '5-', pattern = '10000010' }, } local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" defaultScaleTonicNoteValue = 1 defaultScaleTypeValue = 1 defaultScaleNotesTextValue = "" defaultChordTextValue = "" defaultSelectedScaleNote = 1 defaultOctave = 3 defaultSelectedChordTypes = {} for i = 1, 7 do table.insert(defaultSelectedChordTypes, 1) end defaultInversionStates = {} for i = 1, 7 do table.insert(defaultInversionStates, 0) end defaultScaleNoteNames = {'C', 'D', 'E', 'F', 'G', 'A', 'B'} defaultScaleDegreeHeaders = {'I', 'ii', 'iii', 'IV', 'V', 'vi', 'viio'} defaultNotesThatArePlaying = {} defaultDockState = 0x0201 defaultWindowShouldBeDocked = tostring(false) local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local activeProjectIndex = 0 local sectionName = "com.pandabot.ChordGun" local scaleTonicNoteKey = "scaleTonicNote" local scaleTypeKey = "scaleType" local scaleNotesTextKey = "scaleNotesText" local chordTextKey = "chordText" local chordInversionStatesKey = "chordInversionStates" local selectedScaleNoteKey = "selectedScaleNote" local octaveKey = "octave" local selectedChordTypesKey = "selectedChordTypes" local scaleNoteNamesKey = "scaleNoteNames" local scaleDegreeHeadersKey = "scaleDegreeHeaders" local notesThatArePlayingKey = "notesThatArePlaying" local dockStateKey = "dockState" local windowShouldBeDockedKey = "shouldBeDocked" -- local function setValue(key, value) reaper.SetProjExtState(activeProjectIndex, sectionName, key, value) end local function getValue(key, defaultValue) local valueExists, value = reaper.GetProjExtState(activeProjectIndex, sectionName, key) if valueExists == 0 then setValue(key, defaultValue) return defaultValue end return value end -- local function getTableFromString(arg) local output = {} for match in arg:gmatch("([^,%s]+)") do output[#output + 1] = match end return output end local function setTableValue(key, value) reaper.SetProjExtState(activeProjectIndex, sectionName, key, table.concat(value, ",")) end local function getTableValue(key, defaultValue) local valueExists, value = reaper.GetProjExtState(activeProjectIndex, sectionName, key) if valueExists == 0 then setTableValue(key, defaultValue) return defaultValue end return getTableFromString(value) end --[[ ]]-- function getScaleTonicNote() return tonumber(getValue(scaleTonicNoteKey, defaultScaleTonicNoteValue)) end function setScaleTonicNote(arg) setValue(scaleTonicNoteKey, arg) end -- function getScaleType() return tonumber(getValue(scaleTypeKey, defaultScaleTypeValue)) end function setScaleType(arg) setValue(scaleTypeKey, arg) end -- function getScaleNotesText() return getValue(scaleNotesTextKey, defaultScaleNotesTextValue) end function setScaleNotesText(arg) setValue(scaleNotesTextKey, arg) end -- function getChordText() return getValue(chordTextKey, defaultChordTextValue) end function setChordText(arg) setValue(chordTextKey, arg) end -- function getChordInversionMin() return -8 end -- function getChordInversionMax() return 8 end -- function getSelectedScaleNote() return tonumber(getValue(selectedScaleNoteKey, defaultSelectedScaleNote)) end function setSelectedScaleNote(arg) setValue(selectedScaleNoteKey, arg) end -- function getOctave() return tonumber(getValue(octaveKey, defaultOctave)) end function setOctave(arg) setValue(octaveKey, arg) end -- function getOctaveMin() return -1 end -- function getOctaveMax() return 8 end -- function getSelectedChordTypes() return getTableValue(selectedChordTypesKey, defaultSelectedChordTypes) end function getSelectedChordType(index) local temp = getTableValue(selectedChordTypesKey, defaultSelectedChordTypes) return tonumber(temp[index]) end function setSelectedChordType(index, arg) local temp = getSelectedChordTypes() temp[index] = arg setTableValue(selectedChordTypesKey, temp) end -- function getScaleNoteNames() return getTableValue(scaleNoteNamesKey, defaultScaleNoteNames) end function getScaleNoteName(index) local temp = getTableValue(scaleNoteNamesKey, defaultScaleNoteNames) return temp[index] end function setScaleNoteName(index, arg) local temp = getScaleNoteNames() temp[index] = arg setTableValue(scaleNoteNamesKey, temp) end -- function getScaleDegreeHeaders() return getTableValue(scaleDegreeHeadersKey, defaultScaleDegreeHeaders) end function getScaleDegreeHeader(index) local temp = getTableValue(scaleDegreeHeadersKey, defaultScaleDegreeHeaders) return temp[index] end function setScaleDegreeHeader(index, arg) local temp = getScaleDegreeHeaders() temp[index] = arg setTableValue(scaleDegreeHeadersKey, temp) end -- function getChordInversionStates() return getTableValue(chordInversionStatesKey, defaultInversionStates) end function getChordInversionState(index) local temp = getTableValue(chordInversionStatesKey, defaultInversionStates) return tonumber(temp[index]) end function setChordInversionState(index, arg) local temp = getChordInversionStates() temp[index] = arg setTableValue(chordInversionStatesKey, temp) end -- function resetSelectedChordTypes() local numberOfSelectedChordTypes = 7 for i = 1, numberOfSelectedChordTypes do setSelectedChordType(i, 1) end end function resetChordInversionStates() local numberOfChordInversionStates = 7 for i = 1, numberOfChordInversionStates do setChordInversionState(i, 0) end end -- function getNotesThatArePlaying() return getTableValue(notesThatArePlayingKey, defaultNotesThatArePlaying) end function setNotesThatArePlaying(arg) setTableValue(notesThatArePlayingKey, arg) end -- function getDockState() return getValue(dockStateKey, defaultDockState) end function setDockState(arg) setValue(dockStateKey, arg) end function windowShouldBeDocked() return getValue(windowShouldBeDockedKey, defaultWindowShouldBeDocked) == tostring(true) end function setWindowShouldBeDocked(arg) setValue(windowShouldBeDockedKey, tostring(arg)) end function mouseIsHoveringOver(element) local x = gfx.mouse_x local y = gfx.mouse_y local isInHorizontalRegion = (x >= element.x and x < element.x+element.width) local isInVerticalRegion = (y >= element.y and y < element.y+element.height) return isInHorizontalRegion and isInVerticalRegion end function setPositionAtMouseCursor() gfx.x = gfx.mouse_x gfx.y = gfx.mouse_y end function leftMouseButtonIsHeldDown() return gfx.mouse_cap & 1 == 1 end function leftMouseButtonIsNotHeldDown() return gfx.mouse_cap & 1 ~= 1 end function rightMouseButtonIsHeldDown() return gfx.mouse_cap & 2 == 2 end function clearConsoleWindow() reaper.ShowConsoleMsg("") end function print(arg) reaper.ShowConsoleMsg(tostring(arg) .. "\n") end function getScreenWidth() local _, _, screenWidth, _ = reaper.my_getViewport(0, 0, 0, 0, 0, 0, 0, 0, true) return screenWidth end function getScreenHeight() local _, _, _, screenHeight = reaper.my_getViewport(0, 0, 0, 0, 0, 0, 0, 0, true) return screenHeight end function windowIsDocked() return gfx.dock(-1) > 0 end function windowIsNotDocked() return not windowIsDocked() end function notesAreSelected() local activeMidiEditor = reaper.MIDIEditor_GetActive() local activeTake = reaper.MIDIEditor_GetTake(activeMidiEditor) local noteIndex = 0 local noteExists = true local noteIsSelected = false while noteExists do noteExists, noteIsSelected = reaper.MIDI_GetNote(activeTake, noteIndex) if noteIsSelected then return true end noteIndex = noteIndex + 1 end return false end function startUndoBlock() reaper.Undo_BeginBlock() end function endUndoBlock(actionDescription) reaper.Undo_OnStateChange(actionDescription) reaper.Undo_EndBlock(actionDescription, -1) end function emptyFunctionToPreventAutomaticCreationOfUndoPoint() end Timer = {} Timer.__index = Timer function Timer:new(numberOfSeconds) local self = {} setmetatable(self, Timer) self.startingTime = reaper.time_precise() self.numberOfSeconds = numberOfSeconds self.timerIsStopped = true return self end function Timer:start() self.timerIsStopped = false self.startingTime = reaper.time_precise() end function Timer:stop() self.timerIsStopped = true end function Timer:timeHasElapsed() local currentTime = reaper.time_precise() if self.timerIsStopped then return false end if currentTime - self.startingTime > self.numberOfSeconds then return true else return false end end function Timer:timeHasNotElapsed() return not self:timeHasElapsed() end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" mouseButtonIsNotPressedDown = true currentWidth = 0 scaleTonicNote = getScaleTonicNote() scaleType = getScaleType() guiShouldBeUpdated = false scales = { { name = "Major", pattern = "101011010101" }, { name = "Natural Minor", pattern = "101101011010" }, { name = "Harmonic Minor", pattern = "101101011001" }, { name = "Melodic Minor", pattern = "101101010101" }, { name = "Pentatonic", pattern = "101010010100" }, { name = "Ionian", pattern = "101011010101" }, { name = "Aeolian", pattern = "101101011010" }, { name = "Dorian", pattern = "101101010110" }, { name = "Mixolydian", pattern = "101011010110" }, { name = "Phrygian", pattern = "110101011010" }, { name = "Lydian", pattern = "101010110101" }, { name = "Locrian", pattern = "110101101010" } } local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" notes = { 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B' }; flatNotes = { 'C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B' }; function getScalePattern(scaleTonicNote, scale) local scalePatternString = scale['pattern'] local scalePattern = {false,false,false,false,false,false,false,false,false,false,false} for i = 0, #scalePatternString do local note = getNotesIndex(scaleTonicNote+i) if scalePatternString:sub(i+1, i+1) == '1' then scalePattern[note] = true end end return scalePattern end function getNotesIndex(note) return ((note - 1) % 12) + 1 end function getNoteName(note) local noteName = getSharpNoteName(note) if not string.match(getScaleNotesText(), noteName) then return getFlatNoteName(note) else return noteName end end function getSharpNoteName(note) local notesIndex = getNotesIndex(note) return notes[notesIndex] end function getFlatNoteName(note) local notesIndex = getNotesIndex(note) return flatNotes[notesIndex] end function chordIsNotAlreadyIncluded(scaleChordsForRootNote, chordCode) for chordIndex, chord in ipairs(scaleChordsForRootNote) do if chord.code == chordCode then return false end end return true end function getNumberOfScaleChordsForScaleNoteIndex(scaleNoteIndex) local chordCount = 0 local scaleChordsForRootNote = {} for chordIndex, chord in ipairs(chords) do if chordIsInScale(scaleNotes[scaleNoteIndex], chordIndex) then chordCount = chordCount + 1 scaleChordsForRootNote[chordCount] = chord end end return chordCount end function getScaleChordsForRootNote(rootNote) local chordCount = 0 local scaleChordsForRootNote = {} for chordIndex, chord in ipairs(chords) do if chordIsInScale(rootNote, chordIndex) then chordCount = chordCount + 1 scaleChordsForRootNote[chordCount] = chord end end --[[ if preferences.enableModalMixtureCheckbox.value then for chordIndex, chord in ipairs(chords) do if chordIsNotAlreadyIncluded(scaleChordsForRootNote, chord.code) and chordIsInModalMixtureScale(rootNote, chordIndex) then chordCount = chordCount + 1 scaleChordsForRootNote[chordCount] = chord end end end ]]-- -- here is where you color the chord buttons differently for chordIndex, chord in ipairs(chords) do if chordIsNotAlreadyIncluded(scaleChordsForRootNote, chord.code) then chordCount = chordCount + 1 scaleChordsForRootNote[chordCount] = chord end end return scaleChordsForRootNote end function noteIsInScale(note) return scalePattern[getNotesIndex(note)] end function noteIsNotInScale(note) return not noteIsInScale(note) end function chordIsInScale(rootNote, chordIndex) local chord = chords[chordIndex] local chordPattern = chord['pattern'] for i = 0, #chordPattern do local note = getNotesIndex(rootNote+i) if chordPattern:sub(i+1, i+1) == '1' and noteIsNotInScale(note) then return false end end return true end function noteIsInModalMixtureScale(note) local modalMixtureScaleType = 2 local modalMixtureScalePattern = getScalePattern(getScaleTonicNote(), scales[modalMixtureScaleType]) return modalMixtureScalePattern[getNotesIndex(note)] end function noteIsNotInModalMixtureScale(note) return not noteIsInModalMixtureScale(note) end function chordIsInModalMixtureScale(rootNote, chordIndex) local chord = chords[chordIndex] local chordPattern = chord['pattern'] for i = 0, #chordPattern do local note = getNotesIndex(rootNote+i) if chordPattern:sub(i+1, i+1) == '1' and noteIsNotInModalMixtureScale(note) then return false end end return true end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" function updateScaleDegreeHeaders() local minorSymbols = {'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii'} local majorSymbols = {'I', 'II', 'III', 'IV', 'V', 'VI', 'VII'} local diminishedSymbol = 'o' local augmentedSymbol = '+' local sixthSymbol = '6' local seventhSymbol = '7' local i = 1 for i = 1, #scaleNotes do local symbol = "" local chord = scaleChords[i][1] if string.match(chord.code, "major") or chord.code == '7' then symbol = majorSymbols[i] else symbol = minorSymbols[i] end if (chord.code == 'aug') then symbol = symbol .. augmentedSymbol end if (chord.code == 'dim') then symbol = symbol .. diminishedSymbol end if string.match(chord.code, "6") then symbol = symbol .. sixthSymbol end if string.match(chord.code, "7") then symbol = symbol .. seventhSymbol end setScaleDegreeHeader(i, symbol) end end --[[ setDrawColorToRed() gfx.setfont(1, "Arial") <-- default bitmap font does not support Unicode characters local degreeSymbolCharacter = 0x00B0 <-- this is the degree symbol for augmented chords, "°" gfx.drawchar(degreeSymbolCharacter) ]]-- local tolerance = 0.000001 function activeMidiEditor() return reaper.MIDIEditor_GetActive() end function activeTake() return reaper.MIDIEditor_GetTake(activeMidiEditor()) end function activeMediaItem() return reaper.GetMediaItemTake_Item(activeTake()) end function activeTrack() return reaper.GetMediaItemTake_Track(activeTake()) end function mediaItemStartPosition() return reaper.GetMediaItemInfo_Value(activeMediaItem(), "D_POSITION") end function mediaItemStartPositionPPQ() return reaper.MIDI_GetPPQPosFromProjTime(activeTake(), mediaItemStartPosition()) end function mediaItemStartPositionQN() return reaper.MIDI_GetProjQNFromPPQPos(activeTake(), mediaItemStartPositionPPQ()) end local function mediaItemLength() return reaper.GetMediaItemInfo_Value(activeMediaItem(), "D_LENGTH") end local function mediaItemEndPosition() return mediaItemStartPosition() + mediaItemLength() end local function cursorPosition() return reaper.GetCursorPosition() end local function loopStartPosition() local loopStartPosition, _ = reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) return loopStartPosition end local function loopEndPosition() local _, loopEndPosition = reaper.GetSet_LoopTimeRange2(0, false, false, 0, 0, false) return loopEndPosition end local function noteLengthOld() local noteLengthQN = getNoteLengthQN() local noteLengthPPQ = reaper.MIDI_GetPPQPosFromProjQN(activeTake(), noteLengthQN) return reaper.MIDI_GetProjTimeFromPPQPos(activeTake(), noteLengthPPQ) end local function noteLength() return gridUnitLength() end function notCurrentlyRecording() local activeProjectIndex = 0 return reaper.GetPlayStateEx(activeProjectIndex) & 4 ~= 4 end function setEditCursorPosition(arg) local activeProjectIndex = 0 local moveView = false local seekPlay = false reaper.SetEditCurPos2(activeProjectIndex, arg, moveView, seekPlay) end local function moveEditCursorPosition(arg) local moveTimeSelection = false reaper.MoveEditCursor(arg, moveTimeSelection) end local function repeatIsNotOn() return reaper.GetSetRepeat(-1) == 0 end local function loopIsActive() if repeatIsNotOn() then return false end if loopStartPosition() < mediaItemStartPosition() and loopEndPosition() < mediaItemStartPosition() then return false end if loopStartPosition() > mediaItemEndPosition() and loopEndPosition() > mediaItemEndPosition() then return false end if loopStartPosition() == loopEndPosition() then return false else return true end end function moveCursor() if loopIsActive() and loopEndPosition() < mediaItemEndPosition() then if cursorPosition() + noteLength() >= loopEndPosition() - tolerance then if loopStartPosition() > mediaItemStartPosition() then setEditCursorPosition(loopStartPosition()) else setEditCursorPosition(mediaItemStartPosition()) end else moveEditCursorPosition(noteLength()) end elseif loopIsActive() and mediaItemEndPosition() <= loopEndPosition() then if cursorPosition() + noteLength() >= mediaItemEndPosition() - tolerance then if loopStartPosition() > mediaItemStartPosition() then setEditCursorPosition(loopStartPosition()) else setEditCursorPosition(mediaItemStartPosition()) end else moveEditCursorPosition(noteLength()) end elseif cursorPosition() + noteLength() >= mediaItemEndPosition() - tolerance then setEditCursorPosition(mediaItemStartPosition()) else moveEditCursorPosition(noteLength()) end end -- function getCursorPositionPPQ() return reaper.MIDI_GetPPQPosFromProjTime(activeTake(), cursorPosition()) end local function getCursorPositionQN() return reaper.MIDI_GetProjQNFromPPQPos(activeTake(), getCursorPositionPPQ()) end function getNoteLengthQN() local gridLength = reaper.MIDI_GetGrid(activeTake()) return gridLength end function gridUnitLength() local gridLengthQN = reaper.MIDI_GetGrid(activeTake()) local mediaItemPlusGridLengthPPQ = reaper.MIDI_GetPPQPosFromProjQN(activeTake(), mediaItemStartPositionQN() + gridLengthQN) local mediaItemPlusGridLength = reaper.MIDI_GetProjTimeFromPPQPos(activeTake(), mediaItemPlusGridLengthPPQ) return mediaItemPlusGridLength - mediaItemStartPosition() end function getMidiEndPositionPPQ() local startPosition = reaper.GetCursorPosition() local startPositionPPQ = reaper.MIDI_GetPPQPosFromProjTime(activeTake(), startPosition) local endPositionPPQ = reaper.MIDI_GetPPQPosFromProjTime(activeTake(), startPosition+gridUnitLength()) return endPositionPPQ end function deselectAllNotes() local selectAllNotes = false reaper.MIDI_SelectAll(activeTake(), selectAllNotes) end function getCurrentNoteChannel(channelArg) if channelArg ~= nil then return channelArg end if activeMidiEditor() == nil then return 0 end return reaper.MIDIEditor_GetSetting_int(activeMidiEditor(), "default_note_chan") end function getCurrentVelocity() if activeMidiEditor() == nil then return 96 end return reaper.MIDIEditor_GetSetting_int(activeMidiEditor(), "default_note_vel") end function getNumberOfNotes() local _, numberOfNotes = reaper.MIDI_CountEvts(activeTake()) return numberOfNotes end function deleteNote(noteIndex) reaper.MIDI_DeleteNote(activeTake(), noteIndex) end function thereAreNotesSelected() if activeTake() == nil then return false end local numberOfNotes = getNumberOfNotes() for noteIndex = 0, numberOfNotes-1 do local _, noteIsSelected = reaper.MIDI_GetNote(activeTake(), noteIndex) if noteIsSelected then return true end end return false end function halveGridSize() if activeTake() == nil then return end local gridSize = reaper.MIDI_GetGrid(activeTake())/4 if gridSize <= 1/1024 then return end local activeProjectIndex = 0 reaper.SetMIDIEditorGrid(activeProjectIndex, gridSize/2) end function doubleGridSize() if activeTake() == nil then return end local gridSize = reaper.MIDI_GetGrid(activeTake())/4 if gridSize >= 1024 then return end local activeProjectIndex = 0 reaper.SetMIDIEditorGrid(activeProjectIndex, gridSize*2) end -- function deleteExistingNotesInNextInsertionTimePeriod() local insertionStartTime = cursorPosition() local insertionEndTime = insertionStartTime + noteLength() local numberOfNotes = getNumberOfNotes() for noteIndex = numberOfNotes-1, 0, -1 do local _, _, _, noteStartPositionPPQ = reaper.MIDI_GetNote(activeTake(), noteIndex) local noteStartTime = reaper.MIDI_GetProjTimeFromPPQPos(activeTake(), noteStartPositionPPQ) if noteStartTime + tolerance >= insertionStartTime and noteStartTime + tolerance <= insertionEndTime then deleteNote(noteIndex) end end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" function playMidiNote(midiNote) local virtualKeyboardMode = 0 local channel = getCurrentNoteChannel() local noteOnCommand = 0x90 + channel local velocity = getCurrentVelocity() reaper.StuffMIDIMessage(virtualKeyboardMode, noteOnCommand, midiNote, velocity) end function stopAllNotesFromPlaying() for midiNote = 0, 127 do local virtualKeyboardMode = 0 local channel = getCurrentNoteChannel() local noteOffCommand = 0x80 + channel local velocity = 0 reaper.StuffMIDIMessage(virtualKeyboardMode, noteOffCommand, midiNote, velocity) end end function stopNoteFromPlaying(midiNote) local virtualKeyboardMode = 0 local channel = getCurrentNoteChannel() local noteOffCommand = 0x80 + channel local velocity = 0 reaper.StuffMIDIMessage(virtualKeyboardMode, noteOffCommand, midiNote, velocity) end function stopNotesFromPlaying() local notesThatArePlaying = getNotesThatArePlaying() for noteIndex = 1, #notesThatArePlaying do stopNoteFromPlaying(notesThatArePlaying[noteIndex]) end setNotesThatArePlaying({}) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" function applyInversion(chord) local chordLength = #chord local selectedScaleNote = getSelectedScaleNote() local chordInversionValue = getChordInversionState(selectedScaleNote) local chord_ = chord local oct = 0 if chordInversionValue < 0 then oct = math.floor(chordInversionValue / chordLength) chordInversionValue = chordInversionValue + (math.abs(oct) * chordLength) end for i = 1, chordInversionValue do local r = table.remove(chord_, 1) r = r + 12 table.insert(chord_, #chord_ + 1, r ) end for i = 1, #chord_ do chord_[i] = chord_[i] + (oct * 12) end return chord_ end function getChordNotesArray(root, chord, octave) local chordLength = 0 local chordNotesArray = {} local chordPattern = chord["pattern"] for n = 0, #chordPattern-1 do if chordPattern:sub(n+1, n+1) == '1' then chordLength = chordLength + 1 local noteValue = root + n + ((octave+1) * 12) - 1 table.insert(chordNotesArray, noteValue) end end chordNotesArray = applyInversion(chordNotesArray) return chordNotesArray end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" function insertMidiNote(note, keepNotesSelected) local noteIsMuted = false local startPosition = getCursorPositionPPQ() local endPosition = getMidiEndPositionPPQ() local channel = getCurrentNoteChannel() local velocity = getCurrentVelocity() local noSort = false reaper.MIDI_InsertNote(activeTake(), keepNotesSelected, noteIsMuted, startPosition, endPosition, channel, note, velocity, noSort) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local function playScaleChord(chordNotesArray) stopNotesFromPlaying() for note = 1, #chordNotesArray do playMidiNote(chordNotesArray[note]) end setNotesThatArePlaying(chordNotesArray) end function previewScaleChord() local scaleNoteIndex = getSelectedScaleNote() local chordTypeIndex = getSelectedChordType(scaleNoteIndex) local root = scaleNotes[scaleNoteIndex] local chord = scaleChords[scaleNoteIndex][chordTypeIndex] local octave = getOctave() local chordNotesArray = getChordNotesArray(root, chord, octave) playScaleChord(chordNotesArray) updateChordText(root, chord, chordNotesArray) end function insertScaleChord(chordNotesArray, keepNotesSelected) deleteExistingNotesInNextInsertionTimePeriod() for note = 1, #chordNotesArray do insertMidiNote(chordNotesArray[note], keepNotesSelected) end moveCursor() end function playOrInsertScaleChord(actionDescription) local scaleNoteIndex = getSelectedScaleNote() local chordTypeIndex = getSelectedChordType(scaleNoteIndex) local root = scaleNotes[scaleNoteIndex] local chord = scaleChords[scaleNoteIndex][chordTypeIndex] local octave = getOctave() local chordNotesArray = getChordNotesArray(root, chord, octave) if activeTake() ~= nil and notCurrentlyRecording() then startUndoBlock() if thereAreNotesSelected() then changeSelectedNotesToScaleChords(chordNotesArray) else insertScaleChord(chordNotesArray, false) end endUndoBlock(actionDescription) end playScaleChord(chordNotesArray) updateChordText(root, chord, chordNotesArray) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local function playScaleNote(noteValue) stopNotesFromPlaying() playMidiNote(noteValue) setNotesThatArePlaying({noteValue}) setChordText("") end function insertScaleNote(noteValue, keepNotesSelected) deleteExistingNotesInNextInsertionTimePeriod() insertMidiNote(noteValue, keepNotesSelected) moveCursor() end function previewScaleNote(octaveAdjustment) local scaleNoteIndex = getSelectedScaleNote() local root = scaleNotes[scaleNoteIndex] local octave = getOctave() local noteValue = root + ((octave+1+octaveAdjustment) * 12) - 1 playScaleNote(noteValue) end function playOrInsertScaleNote(octaveAdjustment, actionDescription) local scaleNoteIndex = getSelectedScaleNote() local root = scaleNotes[scaleNoteIndex] local octave = getOctave() local noteValue = root + ((octave+1+octaveAdjustment) * 12) - 1 if activeTake() ~= nil and notCurrentlyRecording() then startUndoBlock() if thereAreNotesSelected() then changeSelectedNotesToScaleNotes(noteValue) else insertScaleNote(noteValue, false) end endUndoBlock(actionDescription) end playScaleNote(noteValue) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local function getNoteStartingPositions() local numberOfNotes = getNumberOfNotes() local previousNoteStartPositionPPQ = -1 local noteStartingPositions = {} for noteIndex = 0, numberOfNotes-1 do local _, noteIsSelected, noteIsMuted, noteStartPositionPPQ, noteEndPositionPPQ = reaper.MIDI_GetNote(activeTake(), noteIndex) if noteIsSelected then if noteStartPositionPPQ ~= previousNoteStartPositionPPQ then table.insert(noteStartingPositions, noteStartPositionPPQ) end previousNoteStartPositionPPQ = noteStartPositionPPQ end end return noteStartingPositions end local function deleteSelectedNotes() local numberOfNotes = getNumberOfNotes() for noteIndex = numberOfNotes-1, 0, -1 do local _, noteIsSelected = reaper.MIDI_GetNote(activeTake(), noteIndex) if noteIsSelected then deleteNote(noteIndex) end end end local function setEditCursorTo(arg) local cursorPosition = reaper.MIDI_GetProjTimeFromPPQPos(activeTake(), arg) setEditCursorPosition(cursorPosition) end function changeSelectedNotesToScaleChords(chordNotesArray) local noteStartingPositions = getNoteStartingPositions() deleteSelectedNotes() for i = 1, #noteStartingPositions do setEditCursorTo(noteStartingPositions[i]) insertScaleChord(chordNotesArray, true) end end function changeSelectedNotesToScaleNotes(noteValue) local noteStartingPositions = getNoteStartingPositions() deleteSelectedNotes() for i = 1, #noteStartingPositions do setEditCursorTo(noteStartingPositions[i]) insertScaleNote(noteValue, true) end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" scaleNotes = {} scaleChords = {} scalePattern = nil chordButtons = {} function getNotesString(chordNotesArray) local notesString = '' for i, note in ipairs(chordNotesArray) do local noteName = getNoteName(note+1) if i ~= #chordNotesArray then notesString = notesString .. noteName .. ',' else notesString = notesString .. noteName .. '' end end return notesString end -------------------------------------------------------------------------------- function updateScaleNotes() scaleNotes = {} local scaleNoteIndex = 1 for note = getScaleTonicNote(), getScaleTonicNote() + 11 do if noteIsInScale(note) then scaleNotes[scaleNoteIndex] = note scaleNoteIndex = scaleNoteIndex + 1 end end end function updateScaleChords() scaleChords = {} local scaleNoteIndex = 1 for note = getScaleTonicNote(), getScaleTonicNote() + 11 do if noteIsInScale(note) then scaleChords[scaleNoteIndex] = getScaleChordsForRootNote(note) scaleNoteIndex = scaleNoteIndex + 1 end end end function removeFlatsAndSharps(arg) return arg:gsub('b',''):gsub('#','') end function aNoteIsRepeated() local numberOfScaleNoteNames = 7 local previousScaleNoteName = getScaleNoteName(numberOfScaleNoteNames) local scaleNoteName = nil for scaleDegree = 1, numberOfScaleNoteNames do scaleNoteName = getScaleNoteName(scaleDegree) if removeFlatsAndSharps(scaleNoteName) == removeFlatsAndSharps(previousScaleNoteName) then return true end previousScaleNoteName = scaleNoteName end return false end function updateScaleNoteNames() local previousScaleNoteName = getSharpNoteName(getScaleTonicNote() + 11) local scaleNoteName = nil local scaleDegree = 1 for note = getScaleTonicNote(), getScaleTonicNote() + 11 do if scalePattern[getNotesIndex(note)] then scaleNoteName = getSharpNoteName(note) setScaleNoteName(scaleDegree, scaleNoteName) scaleDegree = scaleDegree + 1 previousScaleNoteName = scaleNoteName end end if aNoteIsRepeated() then local previousScaleNoteName = getFlatNoteName(getScaleTonicNote() + 11) local scaleNoteName = nil local scaleDegree = 1 for note = getScaleTonicNote(), getScaleTonicNote() + 11 do if scalePattern[getNotesIndex(note)] then scaleNoteName = getFlatNoteName(note) setScaleNoteName(scaleDegree, scaleNoteName) scaleDegree = scaleDegree + 1 previousScaleNoteName = scaleNoteName end end end end function updateScaleNotesText() local scaleNotesText = '' for i = 1, #scaleNotes do if scaleNotesText ~= '' then scaleNotesText = scaleNotesText .. ', ' end scaleNotesText = scaleNotesText .. getScaleNoteName(i) end setScaleNotesText(scaleNotesText) end function getChordInversionText(chordNotesArray) local selectedScaleNote = getSelectedScaleNote() local inversionValue = getChordInversionState(selectedScaleNote) if inversionValue == 0 then return '' end if math.fmod(inversionValue, #chordNotesArray) == 0 then return '' end return '/' .. getNoteName(chordNotesArray[1]+1) end function getChordInversionOctaveIndicator(numberOfChordNotes) local selectedScaleNote = getSelectedScaleNote() local inversionValue = getChordInversionState(selectedScaleNote) local octaveIndicator = nil if inversionValue > 0 then local offsetValue = math.floor(inversionValue / numberOfChordNotes) if offsetValue > 0 then return '+' .. offsetValue else return '+' end elseif inversionValue < 0 then local offsetValue = math.abs(math.ceil(inversionValue / numberOfChordNotes)) if offsetValue > 0 then return '-' .. offsetValue else return '-' end else return '' end end function updateChordText(root, chord, chordNotesArray) local rootNoteName = getNoteName(root) local chordInversionText = getChordInversionText(chordNotesArray) local chordInversionOctaveIndicator = getChordInversionOctaveIndicator(#chordNotesArray) local chordString = rootNoteName .. chord["display"] local notesString = getNotesString(chordNotesArray) local chordTextValue = '' if string.match(chordInversionOctaveIndicator, "-") then chordTextValue = ("%s%12s%s%12s"):format(chordInversionOctaveIndicator, chordString, chordInversionText, notesString) elseif string.match(chordInversionOctaveIndicator, "+") then chordTextValue = ("%s%12s%s%12s%12s"):format('', chordString, chordInversionText, notesString, chordInversionOctaveIndicator) else chordTextValue = ("%s%12s%s%12s"):format('', chordString, chordInversionText, notesString) end setChordText(chordTextValue) showChordText() end function showChordText() local chordText = getChordText() reaper.Help_Set(chordText, false) end function updateScaleData() scalePattern = getScalePattern(getScaleTonicNote(), scales[getScaleType()]) updateScaleNotes() updateScaleNoteNames() updateScaleNotesText() updateScaleChords() updateScaleDegreeHeaders() end function showScaleStatus() local scaleTonicText = notes[getScaleTonicNote()] local scaleTypeText = scales[getScaleType()].name local scaleNotesText = getScaleNotesText() reaper.Help_Set(("%s %s: %s"):format(scaleTonicText, scaleTypeText, scaleNotesText), false) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local function transposeSelectedNotes(numberOfSemitones) local numberOfNotes = getNumberOfNotes() for noteIndex = numberOfNotes-1, 0, -1 do local _, noteIsSelected, noteIsMuted, noteStartPositionPPQ, noteEndPositionPPQ, channel, pitch, velocity = reaper.MIDI_GetNote(activeTake(), noteIndex) if noteIsSelected then deleteNote(noteIndex) local noSort = false reaper.MIDI_InsertNote(activeTake(), noteIsSelected, noteIsMuted, noteStartPositionPPQ, noteEndPositionPPQ, channel, pitch+numberOfSemitones, velocity, noSort) end end end function transposeSelectedNotesUpOneOctave() transposeSelectedNotes(12) end function transposeSelectedNotesDownOneOctave() transposeSelectedNotes(-12) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local function decrementChordInversion() local selectedScaleNote = getSelectedScaleNote() local chordInversionMin = getChordInversionMin() local chordInversion = getChordInversionState(selectedScaleNote) if chordInversion <= chordInversionMin then return end setChordInversionState(selectedScaleNote, chordInversion-1) end function decrementChordInversionAction() local actionDescription = "decrement chord inversion" decrementChordInversion() playOrInsertScaleChord(actionDescription) end -- local function incrementChordInversion() local selectedScaleNote = getSelectedScaleNote() local chordInversionMax = getChordInversionMax() local chordInversion = getChordInversionState(selectedScaleNote) if chordInversion >= chordInversionMax then return end setChordInversionState(selectedScaleNote, chordInversion+1) end function incrementChordInversionAction() local actionDescription = "increment chord inversion" incrementChordInversion() playOrInsertScaleChord(actionDescription) end -- local function decrementChordType() local selectedScaleNote = getSelectedScaleNote() local selectedChordType = getSelectedChordType(selectedScaleNote) if selectedChordType <= 1 then return end setSelectedChordType(selectedScaleNote, selectedChordType-1) end function decrementChordTypeAction() local actionDescription = "decrement chord type" decrementChordType() playOrInsertScaleChord(actionDescription) end -- local function incrementChordType() local selectedScaleNote = getSelectedScaleNote() local selectedChordType = getSelectedChordType(selectedScaleNote) if selectedChordType >= #chords then return end setSelectedChordType(selectedScaleNote, selectedChordType+1) end function incrementChordTypeAction() local actionDescription = "increment chord type" incrementChordType() playOrInsertScaleChord(actionDescription) end -- function playTonicNote() local root = scaleNotes[1] local octave = getOctave() local noteValue = root + ((octave+1) * 12) - 1 stopNotesFromPlaying() playMidiNote(noteValue) setNotesThatArePlaying({noteValue}) end local function decrementOctave() local octave = getOctave() if octave <= getOctaveMin() then return end setOctave(octave-1) end function decrementOctaveAction() decrementOctave() if thereAreNotesSelected() then startUndoBlock() transposeSelectedNotesDownOneOctave() endUndoBlock("decrement octave") else playTonicNote() end end -- local function incrementOctave() local octave = getOctave() if octave >= getOctaveMax() then return end setOctave(octave+1) end function incrementOctaveAction() incrementOctave() if thereAreNotesSelected() then startUndoBlock() transposeSelectedNotesUpOneOctave() endUndoBlock("increment octave") else playTonicNote() end end -- local function decrementScaleTonicNote() local scaleTonicNote = getScaleTonicNote() if scaleTonicNote <= 1 then return end setScaleTonicNote(scaleTonicNote-1) end function decrementScaleTonicNoteAction() decrementScaleTonicNote() setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() showScaleStatus() end -- local function incrementScaleTonicNote() local scaleTonicNote = getScaleTonicNote() if scaleTonicNote >= #notes then return end setScaleTonicNote(scaleTonicNote+1) end function incrementScaleTonicNoteAction() incrementScaleTonicNote() setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() showScaleStatus() end -- local function decrementScaleType() local scaleType = getScaleType() if scaleType <= 1 then return end setScaleType(scaleType-1) end function decrementScaleTypeAction() decrementScaleType() setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() showScaleStatus() end -- local function incrementScaleType() local scaleType = getScaleType() if scaleType >= #scales then return end setScaleType(scaleType+1) end function incrementScaleTypeAction() incrementScaleType() setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() showScaleStatus() end ---- local function scaleIsPentatonic() local scaleType = getScaleType() local scaleTypeName = string.lower(scales[scaleType].name) return string.match(scaleTypeName, "pentatonic") end function scaleChordAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end setSelectedScaleNote(scaleNoteIndex) local selectedChordType = getSelectedChordType(scaleNoteIndex) local chord = scaleChords[scaleNoteIndex][selectedChordType] local actionDescription = "scale chord " .. scaleNoteIndex .. " (" .. chord.code .. ")" playOrInsertScaleChord(actionDescription) end function previewScaleChordAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end setSelectedScaleNote(scaleNoteIndex) previewScaleChord() end -- function scaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end setSelectedScaleNote(scaleNoteIndex) local actionDescription = "scale note " .. scaleNoteIndex playOrInsertScaleNote(0, actionDescription) end -- function lowerScaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end if getOctave() <= getOctaveMin() then return end setSelectedScaleNote(scaleNoteIndex) local actionDescription = "lower scale note " .. scaleNoteIndex playOrInsertScaleNote(-1, actionDescription) end -- function higherScaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end if getOctave() >= getOctaveMax() then return end setSelectedScaleNote(scaleNoteIndex) local actionDescription = "higher scale note " .. scaleNoteIndex playOrInsertScaleNote(1, actionDescription) end -- function previewScaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end setSelectedScaleNote(scaleNoteIndex) previewScaleNote(0) end function previewLowerScaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end if getOctave() <= getOctaveMin() then return end setSelectedScaleNote(scaleNoteIndex) previewScaleNote(-1) end function previewHigherScaleNoteAction(scaleNoteIndex) if scaleIsPentatonic() and scaleNoteIndex > 5 then return end if getOctave() >= getOctaveMax() then return end setSelectedScaleNote(scaleNoteIndex) previewScaleNote(1) end function drawDropdownIcon() local xOffset = gfx.x local yOffset = gfx.y gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.36078431372549, 0.39607843137255, 0.3843137254902) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = xOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 1 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 2 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 2 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.3843137254902, 0.4156862745098, 0.40392156862745) gfx.x = 2 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 2 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 2 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 2 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 3 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 3 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 3 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.35294117647059, 0.3843137254902, 0.37254901960784) gfx.x = 3 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.41176470588235, 0.43921568627451, 0.42745098039216) gfx.x = 3 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.67843137254902, 0.69411764705882, 0.69019607843137) gfx.x = 3 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.50196078431373, 0.52549019607843, 0.51764705882353) gfx.x = 3 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36078431372549, 0.39607843137255, 0.38039215686275) gfx.x = 3 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 3 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 3 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 3 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 3 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 3 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 3 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 4 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 4 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 4 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34509803921569, 0.37647058823529, 0.36470588235294) gfx.x = 4 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.43529411764706, 0.46274509803922, 0.45098039215686) gfx.x = 4 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.92156862745098, 0.92549019607843, 0.92549019607843) gfx.x = 4 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.85882352941176, 0.86274509803922, 0.86274509803922) gfx.x = 4 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.53725490196078, 0.56078431372549, 0.55294117647059) gfx.x = 4 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.4, 0.38823529411765) gfx.x = 4 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36078431372549, 0.3921568627451, 0.38039215686275) gfx.x = 4 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 4 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 4 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 4 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 4 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 5 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 5 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 5 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34509803921569, 0.38039215686275, 0.36470588235294) gfx.x = 5 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.43529411764706, 0.46274509803922, 0.45098039215686) gfx.x = 5 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.94901960784314, 0.95294117647059, 0.95294117647059) gfx.x = 5 + xOffset gfx.y = 6 + yOffset gfx.setpixel(1.0, 1.0, 1.0) gfx.x = 5 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.90196078431373, 0.90588235294118, 0.90196078431373) gfx.x = 5 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.6, 0.6156862745098, 0.61176470588235) gfx.x = 5 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.3921568627451, 0.42352941176471, 0.41176470588235) gfx.x = 5 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 5 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 5 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 5 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 5 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 6 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 6 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 6 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34509803921569, 0.38039215686275, 0.36862745098039) gfx.x = 6 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.43529411764706, 0.46274509803922, 0.45098039215686) gfx.x = 6 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.94509803921569, 0.94509803921569, 0.94509803921569) gfx.x = 6 + xOffset gfx.y = 6 + yOffset gfx.setpixel(1.0, 1.0, 1.0) gfx.x = 6 + xOffset gfx.y = 7 + yOffset gfx.setpixel(1.0, 1.0, 1.0) gfx.x = 6 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.87058823529412, 0.87843137254902, 0.87843137254902) gfx.x = 6 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.52156862745098, 0.54509803921569, 0.53725490196078) gfx.x = 6 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.38823529411765) gfx.x = 6 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 6 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 6 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 6 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 7 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 7 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 7 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34509803921569, 0.38039215686275, 0.36470588235294) gfx.x = 7 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.43529411764706, 0.46274509803922, 0.45098039215686) gfx.x = 7 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.94901960784314, 0.95294117647059, 0.95294117647059) gfx.x = 7 + xOffset gfx.y = 6 + yOffset gfx.setpixel(1.0, 1.0, 1.0) gfx.x = 7 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.90196078431373, 0.90588235294118, 0.90196078431373) gfx.x = 7 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.6, 0.6156862745098, 0.61176470588235) gfx.x = 7 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.3921568627451, 0.42352941176471, 0.41176470588235) gfx.x = 7 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 7 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 7 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 7 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 7 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 8 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 8 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 8 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34509803921569, 0.37647058823529, 0.36470588235294) gfx.x = 8 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.43529411764706, 0.46274509803922, 0.45490196078431) gfx.x = 8 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.92156862745098, 0.92549019607843, 0.92549019607843) gfx.x = 8 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.85490196078431, 0.86274509803922, 0.85882352941176) gfx.x = 8 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.53725490196078, 0.56078431372549, 0.55294117647059) gfx.x = 8 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.4, 0.38823529411765) gfx.x = 8 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36078431372549, 0.3921568627451, 0.38039215686275) gfx.x = 8 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 8 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 8 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 8 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 8 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 9 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.35294117647059, 0.3843137254902, 0.37254901960784) gfx.x = 9 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.41176470588235, 0.43921568627451, 0.42745098039216) gfx.x = 9 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.67843137254902, 0.69411764705882, 0.69019607843137) gfx.x = 9 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.49803921568627, 0.52549019607843, 0.51764705882353) gfx.x = 9 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 9 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 9 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 9 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 10 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 10 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 10 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.3843137254902, 0.4156862745098, 0.40392156862745) gfx.x = 10 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 10 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 10 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 10 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 11 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.36078431372549, 0.3921568627451, 0.38039215686275) gfx.x = 11 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.39607843137255, 0.3843137254902) gfx.x = 11 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36470588235294, 0.4, 0.38823529411765) gfx.x = 11 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34509803921569, 0.37647058823529, 0.36470588235294) gfx.x = 11 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 12 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.36470588235294, 0.4, 0.38823529411765) gfx.x = 12 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.36862745098039, 0.40392156862745, 0.3921568627451) gfx.x = 12 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.34901960784314, 0.38039215686275, 0.36862745098039) gfx.x = 12 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 13 + xOffset gfx.y = yOffset gfx.setpixel(0.36862745098039, 0.4, 0.38823529411765) gfx.y = 1 + yOffset gfx.setpixel(0.34901960784314, 0.38039215686275, 0.36862745098039) gfx.x = 13 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.34509803921569, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.34901960784314, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.34509803921569, 0.37647058823529, 0.36470588235294) gfx.x = 13 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.34901960784314, 0.38039215686275, 0.36862745098039) gfx.x = 13 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.32549019607843, 0.35294117647059, 0.34117647058824) gfx.x = 13 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 10 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 11 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 12 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 13 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 14 + xOffset gfx.y = 14 + yOffset gfx.setpixel(0.0, 0.0, 0.0) end function drawLeftArrow() local xOffset = gfx.x local yOffset = gfx.y gfx.x = 1 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.10196078431373, 0.10196078431373, 0.10196078431373) gfx.x = 1 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.18823529411765, 0.1921568627451, 0.1921568627451) gfx.x = 1 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.10196078431373, 0.10196078431373, 0.10196078431373) gfx.x = 1 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 2 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 2 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.074509803921569) gfx.x = 2 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.24313725490196, 0.25882352941176, 0.25882352941176) gfx.x = 2 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.45490196078431, 0.48627450980392, 0.49019607843137) gfx.x = 2 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.24313725490196, 0.25882352941176, 0.25882352941176) gfx.x = 2 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.074509803921569) gfx.x = 2 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 2 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 2 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 3 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.07843137254902) gfx.x = 3 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 3 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.47450980392157, 0.51372549019608, 0.51372549019608) gfx.x = 3 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.56078431372549, 0.61176470588235, 0.61176470588235) gfx.x = 3 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.47450980392157, 0.51372549019608, 0.51372549019608) gfx.x = 3 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 3 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.07843137254902) gfx.x = 3 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 3 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 4 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 4 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.44313725490196, 0.47843137254902, 0.47843137254902) gfx.x = 4 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.56862745098039, 0.6156862745098, 0.61176470588235) gfx.x = 4 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.57647058823529, 0.62352941176471, 0.62352941176471) gfx.x = 4 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.56862745098039, 0.6156862745098, 0.61176470588235) gfx.x = 4 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.44313725490196, 0.47843137254902, 0.47843137254902) gfx.x = 4 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 4 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 4 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 5 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.30196078431373, 0.32156862745098, 0.32156862745098) gfx.x = 5 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.49411764705882, 0.53725490196078, 0.53725490196078) gfx.x = 5 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.51764705882353, 0.56470588235294, 0.56470588235294) gfx.x = 5 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.51372549019608, 0.55686274509804, 0.55686274509804) gfx.x = 5 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.51764705882353, 0.56470588235294, 0.56470588235294) gfx.x = 5 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.49411764705882, 0.53725490196078, 0.53725490196078) gfx.x = 5 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.30196078431373, 0.32156862745098, 0.32156862745098) gfx.x = 5 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.098039215686275, 0.098039215686275, 0.098039215686275) gfx.x = 5 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 6 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.11764705882353, 0.12156862745098, 0.12156862745098) gfx.x = 6 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.13725490196078, 0.14117647058824, 0.14117647058824) gfx.x = 6 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 6 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 6 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 6 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.13725490196078, 0.14117647058824, 0.14117647058824) gfx.x = 6 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.11764705882353, 0.12156862745098, 0.12156862745098) gfx.x = 6 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 6 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 7 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.07843137254902, 0.074509803921569, 0.074509803921569) gfx.x = 7 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 7 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 7 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.070588235294118, 0.070588235294118, 0.070588235294118) gfx.x = 7 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 7 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 7 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.07843137254902, 0.074509803921569, 0.074509803921569) gfx.x = 7 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 8 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.090196078431373, 0.090196078431373) gfx.x = 8 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) end function drawRightArrow() local xOffset = gfx.x local yOffset = gfx.y gfx.x = 1 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.07843137254902, 0.074509803921569, 0.074509803921569) gfx.x = 1 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 1 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 1 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.070588235294118, 0.070588235294118, 0.070588235294118) gfx.x = 1 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 1 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.070588235294118, 0.066666666666667, 0.066666666666667) gfx.x = 1 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.07843137254902, 0.074509803921569, 0.074509803921569) gfx.x = 1 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 1 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 2 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.11764705882353, 0.12156862745098, 0.12156862745098) gfx.x = 2 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.13725490196078, 0.14117647058824, 0.14117647058824) gfx.x = 2 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 2 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 2 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.13333333333333, 0.13725490196078, 0.13725490196078) gfx.x = 2 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.13725490196078, 0.14117647058824, 0.14117647058824) gfx.x = 2 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.11764705882353, 0.12156862745098, 0.12156862745098) gfx.x = 2 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 2 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 3 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.30196078431373, 0.32156862745098, 0.32156862745098) gfx.x = 3 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.49411764705882, 0.53725490196078, 0.53725490196078) gfx.x = 3 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.51764705882353, 0.56470588235294, 0.56470588235294) gfx.x = 3 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.51372549019608, 0.55686274509804, 0.55686274509804) gfx.x = 3 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.51764705882353, 0.56470588235294, 0.56470588235294) gfx.x = 3 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.49411764705882, 0.53725490196078, 0.53725490196078) gfx.x = 3 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.30196078431373, 0.32156862745098, 0.32156862745098) gfx.x = 3 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.098039215686275, 0.098039215686275, 0.098039215686275) gfx.x = 3 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 4 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 4 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.44313725490196, 0.47843137254902, 0.47843137254902) gfx.x = 4 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.56862745098039, 0.6156862745098, 0.61176470588235) gfx.x = 4 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.57647058823529, 0.62352941176471, 0.62352941176471) gfx.x = 4 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.56862745098039, 0.6156862745098, 0.61176470588235) gfx.x = 4 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.44313725490196, 0.47843137254902, 0.47843137254902) gfx.x = 4 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 4 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 4 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 5 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.07843137254902) gfx.x = 5 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 5 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.47450980392157, 0.51372549019608, 0.51372549019608) gfx.x = 5 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.56078431372549, 0.61176470588235, 0.61176470588235) gfx.x = 5 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.47450980392157, 0.51372549019608, 0.51372549019608) gfx.x = 5 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.19607843137255, 0.2078431372549, 0.2078431372549) gfx.x = 5 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.07843137254902) gfx.x = 5 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.090196078431373, 0.090196078431373, 0.090196078431373) gfx.x = 5 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 6 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 6 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.074509803921569) gfx.x = 6 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.24313725490196, 0.25882352941176, 0.25882352941176) gfx.x = 6 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.45490196078431, 0.48627450980392, 0.49019607843137) gfx.x = 6 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.24313725490196, 0.25882352941176, 0.25882352941176) gfx.x = 6 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.07843137254902, 0.07843137254902, 0.074509803921569) gfx.x = 6 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 6 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 6 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 7 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.10196078431373, 0.10196078431373, 0.10196078431373) gfx.x = 7 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.18823529411765, 0.1921568627451, 0.1921568627451) gfx.x = 7 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.10196078431373, 0.10196078431373, 0.10196078431373) gfx.x = 7 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 7 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 8 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.086274509803922, 0.082352941176471, 0.082352941176471) gfx.x = 8 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.086274509803922, 0.086274509803922, 0.086274509803922) gfx.x = 8 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 1 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 2 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 3 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 4 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 5 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 6 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 7 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 8 + yOffset gfx.setpixel(0.0, 0.0, 0.0) gfx.x = 9 + xOffset gfx.y = 9 + yOffset gfx.setpixel(0.0, 0.0, 0.0) end local function hex2rgb(arg) local r, g, b = arg:match('(..)(..)(..)') r = tonumber(r, 16)/255 g = tonumber(g, 16)/255 b = tonumber(b, 16)/255 return r, g, b end local function setColor(hexColor) local r, g, b = hex2rgb(hexColor) gfx.set(r, g, b, 1) end --[[ window ]]-- function setDrawColorToBackground() setColor("242424") end --[[ buttons ]]-- function setDrawColorToNormalButton() setColor("2D2D2D") end function setDrawColorToHighlightedButton() setColor("474747") end -- function setDrawColorToSelectedChordTypeButton() setColor("474747") end function setDrawColorToHighlightedSelectedChordTypeButton() setColor("717171") end -- function setDrawColorToSelectedChordTypeAndScaleNoteButton() setColor("DCDCDC") end function setDrawColorToHighlightedSelectedChordTypeAndScaleNoteButton() setColor("FFFFFF") end -- function setDrawColorToOutOfScaleButton() setColor("121212") end function setDrawColorToHighlightedOutOfScaleButton() setColor("474747") end -- function setDrawColorToButtonOutline() setColor("1D1D1D") end --[[ button text ]]-- function setDrawColorToNormalButtonText() setColor("D7D7D7") end function setDrawColorToHighlightedButtonText() setColor("EEEEEE") end -- function setDrawColorToSelectedChordTypeButtonText() setColor("F1F1F1") end function setDrawColorToHighlightedSelectedChordTypeButtonText() setColor("FDFDFD") end -- function setDrawColorToSelectedChordTypeAndScaleNoteButtonText() setColor("121212") end function setDrawColorToHighlightedSelectedChordTypeAndScaleNoteButtonText() setColor("000000") end --[[ buttons ]]-- function setDrawColorToHeaderOutline() setColor("151515") end function setDrawColorToHeaderBackground() setColor("242424") end function setDrawColorToHeaderText() setColor("818181") end --[[ frame ]]-- function setDrawColorToFrameOutline() setColor("0D0D0D") end function setDrawColorToFrameBackground() setColor("181818") end --[[ dropdown ]]-- function setDrawColorToDropdownOutline() setColor("090909") end function setDrawColorToDropdownBackground() setColor("1D1D1D") end function setDrawColorToDropdownText() setColor("D7D7D7") end --[[ valuebox ]]-- function setDrawColorToValueBoxOutline() setColor("090909") end function setDrawColorToValueBoxBackground() setColor("161616") end function setDrawColorToValueBoxText() setColor("9F9F9F") end --[[ text ]]-- function setDrawColorToText() setColor("878787") end --[[ debug ]]-- function setDrawColorToRed() setColor("FF0000") end --[[ function setDrawColorToBackground() local r, g, b local backgroundColor = {36, 36, 36, 1} -- #242424 gfx.set(table.unpack(backgroundColor)) end function setDrawColorToNormalButton() local backgroundColor = {45, 45, 45, 1} -- #2D2D2D gfx.set(table.unpack(backgroundColor)) end function setDrawColorToHighlightedButton() local backgroundColor = {71, 71, 71, 1} -- #474747 gfx.set(table.unpack(backgroundColor)) end function setDrawColorToSelectedButton() local backgroundColor = {220, 220, 220, 1} -- #DCDCDC gfx.set(table.unpack(backgroundColor)) end ]]-- local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Docker = {} Docker.__index = Docker function Docker:new() local self = {} setmetatable(self, Docker) return self end local function dockWindow() local dockState = getDockState() gfx.dock(dockState) setWindowShouldBeDocked(true) guiShouldBeUpdated = true end function Docker:drawDockWindowContextMenu() setPositionAtMouseCursor() local selectedIndex = gfx.showmenu("dock window") if selectedIndex <= 0 then return end dockWindow() gfx.mouse_cap = 0 end local function undockWindow() setWindowShouldBeDocked(false) gfx.dock(0) guiShouldBeUpdated = true end function Docker:drawUndockWindowContextMenu() setPositionAtMouseCursor() local selectedIndex = gfx.showmenu("undock window") if selectedIndex <= 0 then return end undockWindow() gfx.mouse_cap = 0 end function Docker:update() if rightMouseButtonIsHeldDown() and windowIsDocked() then self:drawUndockWindowContextMenu() end if rightMouseButtonIsHeldDown() and windowIsNotDocked() then self:drawDockWindowContextMenu() end end HitArea = {} HitArea.__index = HitArea function HitArea:new(x, y, width, height) local self = {} setmetatable(self, HitArea) self.x = x self.y = y self.width = width self.height = height return self end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" OctaveValueBox = {} OctaveValueBox.__index = OctaveValueBox function OctaveValueBox:new(x, y, width, height) local self = {} setmetatable(self, OctaveValueBox) self.x = x self.y = y self.width = width self.height = height return self end function OctaveValueBox:drawRectangle() setDrawColorToValueBoxBackground() gfx.rect(self.x, self.y, self.width, self.height) end function OctaveValueBox:drawRectangleOutline() setDrawColorToValueBoxOutline() gfx.rect(self.x-1, self.y-1, self.width+1, self.height+1, false) end function OctaveValueBox:drawRectangles() self:drawRectangle() self:drawRectangleOutline() end function OctaveValueBox:drawLeftArrow() gfx.x = self.x + 2 gfx.y = self.y + 2 drawLeftArrow() end function OctaveValueBox:drawRightArrow() local imageWidth = 9 gfx.x = self.x + self.width - imageWidth - 3 gfx.y = self.y + 2 drawRightArrow() end function OctaveValueBox:drawImages() self:drawLeftArrow() self:drawRightArrow() end function OctaveValueBox:drawText() local octaveText = getOctave() setDrawColorToValueBoxText() local stringWidth, stringHeight = gfx.measurestr(octaveText) gfx.x = self.x + ((self.width - stringWidth) / 2) gfx.y = self.y + ((self.height - stringHeight) / 2) gfx.drawstr(octaveText) end local hitAreaWidth = 18 local function leftButtonHasBeenClicked(valueBox) local hitArea = HitArea:new(valueBox.x-1, valueBox.y-1, hitAreaWidth, valueBox.height+1) return mouseIsHoveringOver(hitArea) and leftMouseButtonIsHeldDown() end local function rightButtonHasBeenClicked(valueBox) local hitArea = HitArea:new(valueBox.x+valueBox.width-hitAreaWidth, valueBox.y-1, hitAreaWidth, valueBox.height+1) return mouseIsHoveringOver(hitArea) and leftMouseButtonIsHeldDown() end function OctaveValueBox:update() self:drawRectangles() self:drawImages() if mouseButtonIsNotPressedDown and leftButtonHasBeenClicked(self) then mouseButtonIsNotPressedDown = false decrementOctaveAction() end if mouseButtonIsNotPressedDown and rightButtonHasBeenClicked(self) then mouseButtonIsNotPressedDown = false incrementOctaveAction() end self:drawText() end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Label = {} Label.__index = Label function Label:new(x, y, width, height, getTextCallback) local self = {} setmetatable(self, Label) self.x = x self.y = y self.width = width self.height = height self.getTextCallback = getTextCallback return self end function Label:drawRedOutline() setDrawColorToRed() gfx.rect(self.x, self.y, self.width, self.height, false) end function Label:drawText(text) setDrawColorToText() local stringWidth, stringHeight = gfx.measurestr(text) gfx.x = self.x + ((self.width - stringWidth) / 2) gfx.y = self.y + ((self.height - stringHeight) / 2) gfx.drawstr(text) end function Label:update() --self:drawRedOutline() local text = self.getTextCallback() self:drawText(text) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Header = {} Header.__index = Header local radius = 5 function Header:new(x, y, width, height, getTextCallback) local self = {} setmetatable(self, Header) self.x = x self.y = y self.width = width self.height = height self.getTextCallback = getTextCallback return self end function Header:drawCorners(offset) gfx.circle(self.x + radius + offset, self.y + radius + offset, radius, true) gfx.circle(self.x + self.width - radius - offset, self.y + radius + offset, radius, true) end function Header:drawEnds(offset) gfx.rect(self.x + offset, self.y + radius + offset, radius, self.height - radius * 2 - 2 * offset, true) gfx.rect(self.x + self.width - radius - offset, self.y + radius + offset, radius + 1, self.height - radius * 2 - 2 * offset, true) end function Header:drawBodyAndSides(offset) gfx.rect(self.x + radius + offset, self.y + offset, self.width - radius * 2 - 2 * offset, self.height - radius - 2 * offset, true) end function Header:drawHeaderOutline() setDrawColorToHeaderOutline() self:drawCorners(0) self:drawEnds(0) self:drawBodyAndSides(0) end function Header:drawRoundedRectangle() setDrawColorToHeaderBackground() self:drawCorners(1) self:drawEnds(1) self:drawBodyAndSides(1) end function Header:drawRoundedRectangles() self:drawHeaderOutline() self:drawRoundedRectangle() end function Header:drawText(text) setDrawColorToHeaderText() local stringWidth, stringHeight = gfx.measurestr(text) gfx.x = self.x + ((self.width + 4 * 1 - stringWidth) / 2) gfx.y = self.y + ((self.height - 4 * 1 - stringHeight) / 2) gfx.drawstr(text) end function Header:update() self:drawRoundedRectangles() local text = self.getTextCallback() self:drawText(text) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Frame = {} Frame.__index = Frame local radius = 10 function Frame:new(x, y, width, height) local self = {} setmetatable(self, Frame) self.x = x self.y = y self.width = width self.height = height return self end function Frame:drawCorners(offset) gfx.circle(self.x + radius + offset, self.y + radius + offset, radius, true) gfx.circle(self.x + self.width - radius - offset, self.y + radius + offset, radius, true) gfx.circle(self.x + radius + offset, self.y + self.height - radius - offset, radius, true) gfx.circle(self.x + self.width - radius - offset, self.y + self.height - radius - offset, radius, true) end function Frame:drawEnds(offset) gfx.rect(self.x + offset, self.y + radius + offset, radius, self.height - radius * 2, true) gfx.rect(self.x + self.width - radius - offset, self.y + radius - offset, radius + 1, self.height - radius * 2, true) end function Frame:drawBodyAndSides(offset) gfx.rect(self.x + radius + offset, self.y + offset, self.width - radius * 2 - 2 * offset, self.height + 1 - 2 * offset, true) end function Frame:drawFrameOutline() setDrawColorToFrameOutline() self:drawCorners(0) self:drawEnds(0) self:drawBodyAndSides(0) end function Frame:drawRectangle() setDrawColorToFrameBackground() self:drawCorners(1) self:drawEnds(1) self:drawBodyAndSides(1) end function Frame:drawRectangles() self:drawFrameOutline() self:drawRectangle() end function Frame:update() self:drawRectangles() end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Dropdown = {} Dropdown.__index = Dropdown function Dropdown:new(x, y, width, height, options, defaultOptionIndex, onSelectionCallback) local self = {} setmetatable(self, Dropdown) self.x = x self.y = y self.width = width self.height = height self.options = options self.selectedIndex = defaultOptionIndex self.onSelectionCallback = onSelectionCallback self.dropdownList = {} self:updateDropdownList() return self end function Dropdown:drawRectangle() setDrawColorToDropdownBackground() gfx.rect(self.x, self.y, self.width, self.height) end function Dropdown:drawRectangleOutline() setDrawColorToDropdownOutline() gfx.rect(self.x-1, self.y-1, self.width+1, self.height+1, false) end function Dropdown:drawRectangles() self:drawRectangle() self:drawRectangleOutline() end function Dropdown:drawText() local text = self.options[self.selectedIndex] setDrawColorToDropdownText() local stringWidth, stringHeight = gfx.measurestr(text) gfx.x = self.x + 7 gfx.y = self.y + ((self.height - stringHeight) / 2) gfx.drawstr(text) end function Dropdown:drawImage() local imageWidth = 14 gfx.x = self.x + self.width - imageWidth - 1 gfx.y = self.y drawDropdownIcon() end local function dropdownHasBeenClicked(dropdown) return mouseIsHoveringOver(dropdown) and leftMouseButtonIsHeldDown() end function Dropdown:updateDropdownList() self.dropdownList = {} for index, option in pairs(self.options) do if (self.selectedIndex == index) then table.insert(self.dropdownList, "!" .. option) else table.insert(self.dropdownList, option) end end end function Dropdown:openMenu() setPositionAtMouseCursor() local selectedIndex = gfx.showmenu(table.concat(self.dropdownList,"|")) if selectedIndex <= 0 then return end self.selectedIndex = selectedIndex self.onSelectionCallback(selectedIndex) self:updateDropdownList() end function Dropdown:update() self:drawRectangles() self:drawText() self:drawImage() if mouseButtonIsNotPressedDown and dropdownHasBeenClicked(self) then mouseButtonIsNotPressedDown = false self:openMenu() end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" ChordInversionValueBox = {} ChordInversionValueBox.__index = ChordInversionValueBox function ChordInversionValueBox:new(x, y, width, height) local self = {} setmetatable(self, ChordInversionValueBox) self.x = x self.y = y self.width = width self.height = height return self end function ChordInversionValueBox:drawRectangle() setDrawColorToValueBoxBackground() gfx.rect(self.x, self.y, self.width, self.height) end function ChordInversionValueBox:drawRectangleOutline() setDrawColorToValueBoxOutline() gfx.rect(self.x-1, self.y-1, self.width+1, self.height+1, false) end function ChordInversionValueBox:drawRectangles() self:drawRectangle() self:drawRectangleOutline() end function ChordInversionValueBox:drawLeftArrow() gfx.x = self.x + 2 gfx.y = self.y + 2 drawLeftArrow() end function ChordInversionValueBox:drawRightArrow() local imageWidth = 9 gfx.x = self.x + self.width - imageWidth - 3 gfx.y = self.y + 2 drawRightArrow() end function ChordInversionValueBox:drawImages() self:drawLeftArrow() self:drawRightArrow() end function ChordInversionValueBox:drawText() local selectedScaleNote = getSelectedScaleNote() local chordInversionText = getChordInversionState(selectedScaleNote) if chordInversionText > -1 then chordInversionText = "0" .. chordInversionText end setDrawColorToValueBoxText() local stringWidth, stringHeight = gfx.measurestr(chordInversionText) gfx.x = self.x + ((self.width - stringWidth) / 2) gfx.y = self.y + ((self.height - stringHeight) / 2) gfx.drawstr(chordInversionText) end local hitAreaWidth = 18 local function leftButtonHasBeenClicked(valueBox) local hitArea = HitArea:new(valueBox.x-1, valueBox.y-1, hitAreaWidth, valueBox.height+1) return mouseIsHoveringOver(hitArea) and leftMouseButtonIsHeldDown() end local function rightButtonHasBeenClicked(valueBox) local hitArea = HitArea:new(valueBox.x+valueBox.width-hitAreaWidth, valueBox.y-1, hitAreaWidth, valueBox.height+1) return mouseIsHoveringOver(hitArea) and leftMouseButtonIsHeldDown() end local function shiftModifierIsHeldDown() return gfx.mouse_cap & 8 == 8 end function ChordInversionValueBox:onLeftButtonPress() decrementChordInversion() previewScaleChord() end function ChordInversionValueBox:onLeftButtonShiftPress() decrementChordInversionAction() end function ChordInversionValueBox:onRightButtonPress() incrementChordInversion() previewScaleChord() end function ChordInversionValueBox:onRightButtonShiftPress() incrementChordInversionAction() end function ChordInversionValueBox:update() self:drawRectangles() self:drawImages() if mouseButtonIsNotPressedDown and leftButtonHasBeenClicked(self) then mouseButtonIsNotPressedDown = false if shiftModifierIsHeldDown() then self:onLeftButtonShiftPress() else self:onLeftButtonPress() end end if mouseButtonIsNotPressedDown and rightButtonHasBeenClicked(self) then mouseButtonIsNotPressedDown = false if shiftModifierIsHeldDown() then self:onRightButtonShiftPress() else self:onRightButtonPress() end end self:drawText() end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" ChordButton = {} ChordButton.__index = ChordButton function ChordButton:new(text, x, y, width, height, scaleNoteIndex, chordTypeIndex, chordIsInScale) local self = {} setmetatable(self, ChordButton) self.text = text self.x = x self.y = y self.width = width self.height = height self.scaleNoteIndex = scaleNoteIndex self.chordTypeIndex = chordTypeIndex self.chordIsInScale = chordIsInScale return self end function ChordButton:isSelectedChordType() local selectedScaleNote = getSelectedScaleNote() local selectedChordType = getSelectedChordType(self.scaleNoteIndex) local chordTypeIsSelected = (tonumber(self.chordTypeIndex) == tonumber(selectedChordType)) local scaleNoteIsNotSelected = (tonumber(self.scaleNoteIndex) ~= tonumber(selectedScaleNote)) return chordTypeIsSelected and scaleNoteIsNotSelected end function ChordButton:isSelectedChordTypeAndSelectedScaleNote() local selectedScaleNote = getSelectedScaleNote() local selectedChordType = getSelectedChordType(self.scaleNoteIndex) local chordTypeIsSelected = (tonumber(self.chordTypeIndex) == tonumber(selectedChordType)) local scaleNoteIsSelected = (tonumber(self.scaleNoteIndex) == tonumber(selectedScaleNote)) return chordTypeIsSelected and scaleNoteIsSelected end function ChordButton:drawButtonRectangle() if self:isSelectedChordTypeAndSelectedScaleNote() then if mouseIsHoveringOver(self) then setDrawColorToHighlightedSelectedChordTypeAndScaleNoteButton() else setDrawColorToSelectedChordTypeAndScaleNoteButton() end elseif self:isSelectedChordType() then if mouseIsHoveringOver(self) then setDrawColorToHighlightedSelectedChordTypeButton() else setDrawColorToSelectedChordTypeButton() end else if mouseIsHoveringOver(self) then setDrawColorToHighlightedButton() else if self.chordIsInScale then setDrawColorToNormalButton() else setDrawColorToOutOfScaleButton() end end end gfx.rect(self.x, self.y, self.width, self.height) end function ChordButton:drawButtonOutline() setDrawColorToButtonOutline() gfx.rect(self.x-1, self.y-1, self.width+1, self.height+1, false) end function ChordButton:drawRectangles() self:drawButtonRectangle() self:drawButtonOutline() end function ChordButton:drawText() if self:isSelectedChordTypeAndSelectedScaleNote() then if mouseIsHoveringOver(self) then setDrawColorToHighlightedSelectedChordTypeAndScaleNoteButtonText() else setDrawColorToSelectedChordTypeAndScaleNoteButtonText() end elseif self:isSelectedChordType() then if mouseIsHoveringOver(self) then setDrawColorToHighlightedSelectedChordTypeButtonText() else setDrawColorToSelectedChordTypeButtonText() end else if mouseIsHoveringOver(self) then setDrawColorToHighlightedButtonText() else setDrawColorToNormalButtonText() end end local stringWidth, stringHeight = gfx.measurestr(self.text) gfx.x = self.x + ((self.width - stringWidth) / 2) gfx.y = self.y + ((self.height - stringHeight) / 2) gfx.drawstr(self.text) end local function buttonHasBeenClicked(button) return mouseIsHoveringOver(button) and leftMouseButtonIsHeldDown() end local function shiftModifierIsHeldDown() return gfx.mouse_cap & 8 == 8 end function ChordButton:onPress() previewScaleChord() end function ChordButton:onShiftPress() local chord = scaleChords[self.scaleNoteIndex][self.chordTypeIndex] local actionDescription = "scale chord " .. self.scaleNoteIndex .. " (" .. chord.code .. ")" playOrInsertScaleChord(actionDescription) end function ChordButton:update() self:drawRectangles() self:drawText() if mouseButtonIsNotPressedDown and buttonHasBeenClicked(self) then mouseButtonIsNotPressedDown = false setSelectedScaleNote(self.scaleNoteIndex) setSelectedChordType(self.scaleNoteIndex, self.chordTypeIndex) if shiftModifierIsHeldDown() then self:onShiftPress() else self:onPress() end end end inputCharacters = {} inputCharacters["0"] = 48 inputCharacters["1"] = 49 inputCharacters["2"] = 50 inputCharacters["3"] = 51 inputCharacters["4"] = 52 inputCharacters["5"] = 53 inputCharacters["6"] = 54 inputCharacters["7"] = 55 inputCharacters["a"] = 97 inputCharacters["b"] = 98 inputCharacters["c"] = 99 inputCharacters["d"] = 100 inputCharacters["e"] = 101 inputCharacters["f"] = 102 inputCharacters["g"] = 103 inputCharacters["h"] = 104 inputCharacters["i"] = 105 inputCharacters["j"] = 106 inputCharacters["k"] = 107 inputCharacters["l"] = 108 inputCharacters["m"] = 109 inputCharacters["n"] = 110 inputCharacters["o"] = 111 inputCharacters["p"] = 112 inputCharacters["q"] = 113 inputCharacters["r"] = 114 inputCharacters["s"] = 115 inputCharacters["t"] = 116 inputCharacters["u"] = 117 inputCharacters["v"] = 118 inputCharacters["w"] = 119 inputCharacters["x"] = 120 --inputCharacters["y"] = 121 --english --inputCharacters["z"] = 122 --english inputCharacters["y"] = 122 --german inputCharacters["z"] = 121 --german inputCharacters["!"] = 33 inputCharacters["@"] = 64 inputCharacters["#"] = 35 inputCharacters["$"] = 36 inputCharacters["%"] = 37 inputCharacters["^"] = 94 inputCharacters["&"] = 38 inputCharacters["A"] = 65 inputCharacters["B"] = 66 inputCharacters["C"] = 67 inputCharacters["D"] = 68 inputCharacters["E"] = 69 inputCharacters["F"] = 70 inputCharacters["G"] = 71 inputCharacters["H"] = 72 inputCharacters["I"] = 73 inputCharacters["J"] = 74 inputCharacters["K"] = 75 inputCharacters["L"] = 76 inputCharacters["M"] = 77 inputCharacters["N"] = 78 inputCharacters["O"] = 79 inputCharacters["P"] = 80 inputCharacters["Q"] = 81 inputCharacters["R"] = 82 inputCharacters["S"] = 83 inputCharacters["T"] = 84 inputCharacters["U"] = 85 inputCharacters["V"] = 86 inputCharacters["W"] = 87 inputCharacters["X"] = 88 --inputCharacters["Y"] = 89 -- english --inputCharacters["Z"] = 90 -- english inputCharacters["Y"] = 90 --german inputCharacters["Z"] = 89 --german inputCharacters[","] = 44 inputCharacters["."] = 46 inputCharacters["<"] = 60 inputCharacters[">"] = 62 inputCharacters["CMDQ"]=17 inputCharacters["CMDW"]=23.0 inputCharacters["CMDE"]=5.0 inputCharacters["CMDR"]=18.0 inputCharacters["CMDA"]=1.0 inputCharacters["CMDS"]=19.0 inputCharacters["CMDD"]=4.0 inputCharacters["CMDF"]=6.0 inputCharacters["CMDY"]=25.0 -- for german keyboard inputCharacters["CMDZ"]=26.0 -- for english keyboard inputCharacters["CMDX"]=24.0 inputCharacters["CMDC"]=3.0 inputCharacters["CMDV"]=22.0 inputCharacters["CMDG"]=7.0 inputCharacters["ARLEFT"]=1818584692.0 --right arrow inputCharacters["ARRIGHT"]=1919379572.0 --left arrow inputCharacters["HELP"]=26161.0 --F1 local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" function handleInput() inputCharacter = gfx.getchar() if inputCharacter == inputCharacters["0"] then stopAllNotesFromPlaying() end -- if inputCharacter == inputCharacters["1"] then previewScaleChordAction(1) end if inputCharacter == inputCharacters["2"] then previewScaleChordAction(2) end if inputCharacter == inputCharacters["3"] then previewScaleChordAction(3) end if inputCharacter == inputCharacters["4"] then previewScaleChordAction(4) end if inputCharacter == inputCharacters["5"] then previewScaleChordAction(5) end if inputCharacter == inputCharacters["6"] then previewScaleChordAction(6) end if inputCharacter == inputCharacters["7"] then previewScaleChordAction(7) end -- if inputCharacter == inputCharacters["!"] then scaleChordAction(1) end if inputCharacter == inputCharacters["@"] then scaleChordAction(2) end if inputCharacter == inputCharacters["#"] then scaleChordAction(3) end if inputCharacter == inputCharacters["$"] then scaleChordAction(4) end if inputCharacter == inputCharacters["%"] then scaleChordAction(5) end if inputCharacter == inputCharacters["^"] then scaleChordAction(6) end if inputCharacter == inputCharacters["&"] then scaleChordAction(7) end -- if inputCharacter == inputCharacters["q"] then previewHigherScaleNoteAction(1) end if inputCharacter == inputCharacters["w"] then previewHigherScaleNoteAction(2) end if inputCharacter == inputCharacters["e"] then previewHigherScaleNoteAction(3) end if inputCharacter == inputCharacters["r"] then previewHigherScaleNoteAction(4) end if inputCharacter == inputCharacters["t"] then previewHigherScaleNoteAction(5) end if inputCharacter == inputCharacters["y"] then previewHigherScaleNoteAction(6) end if inputCharacter == inputCharacters["u"] then previewHigherScaleNoteAction(7) end -- if inputCharacter == inputCharacters["a"] then previewScaleNoteAction(1) end if inputCharacter == inputCharacters["s"] then previewScaleNoteAction(2) end if inputCharacter == inputCharacters["d"] then previewScaleNoteAction(3) end if inputCharacter == inputCharacters["f"] then previewScaleNoteAction(4) end if inputCharacter == inputCharacters["g"] then previewScaleNoteAction(5) end if inputCharacter == inputCharacters["h"] then previewScaleNoteAction(6) end if inputCharacter == inputCharacters["j"] then previewScaleNoteAction(7) end -- if inputCharacter == inputCharacters["z"] then previewLowerScaleNoteAction(1) end if inputCharacter == inputCharacters["x"] then previewLowerScaleNoteAction(2) end if inputCharacter == inputCharacters["c"] then previewLowerScaleNoteAction(3) end if inputCharacter == inputCharacters["v"] then previewLowerScaleNoteAction(4) end if inputCharacter == inputCharacters["b"] then previewLowerScaleNoteAction(5) end if inputCharacter == inputCharacters["n"] then previewLowerScaleNoteAction(6) end if inputCharacter == inputCharacters["m"] then previewLowerScaleNoteAction(7) end -- if inputCharacter == inputCharacters["Q"] then higherScaleNoteAction(1) end if inputCharacter == inputCharacters["W"] then higherScaleNoteAction(2) end if inputCharacter == inputCharacters["E"] then higherScaleNoteAction(3) end if inputCharacter == inputCharacters["R"] then higherScaleNoteAction(4) end if inputCharacter == inputCharacters["T"] then higherScaleNoteAction(5) end if inputCharacter == inputCharacters["Y"] then higherScaleNoteAction(6) end if inputCharacter == inputCharacters["U"] then higherScaleNoteAction(7) end -- if inputCharacter == inputCharacters["A"] then scaleNoteAction(1) end if inputCharacter == inputCharacters["S"] then scaleNoteAction(2) end if inputCharacter == inputCharacters["D"] then scaleNoteAction(3) end if inputCharacter == inputCharacters["F"] then scaleNoteAction(4) end if inputCharacter == inputCharacters["G"] then scaleNoteAction(5) end if inputCharacter == inputCharacters["H"] then scaleNoteAction(6) end if inputCharacter == inputCharacters["J"] then scaleNoteAction(7) end -- if inputCharacter == inputCharacters["Z"] then lowerScaleNoteAction(1) end if inputCharacter == inputCharacters["X"] then lowerScaleNoteAction(2) end if inputCharacter == inputCharacters["C"] then lowerScaleNoteAction(3) end if inputCharacter == inputCharacters["V"] then lowerScaleNoteAction(4) end if inputCharacter == inputCharacters["B"] then lowerScaleNoteAction(5) end if inputCharacter == inputCharacters["N"] then lowerScaleNoteAction(6) end if inputCharacter == inputCharacters["M"] then lowerScaleNoteAction(7) end ----------------- --[[ local function shiftKeyIsHeldDown() return gfx.mouse_cap & 8 == 8 end ]]-- local function controlKeyIsHeldDown() return gfx.mouse_cap & 32 == 32 end local function optionKeyIsHeldDown() return gfx.mouse_cap & 16 == 16 end local function commandKeyIsHeldDown() return gfx.mouse_cap & 4 == 4 end -- --[[ local function shiftKeyIsNotHeldDown() return gfx.mouse_cap & 8 ~= 8 end ]]-- local function controlKeyIsNotHeldDown() return gfx.mouse_cap & 32 ~= 32 end local function optionKeyIsNotHeldDown() return gfx.mouse_cap & 16 ~= 16 end local function commandKeyIsNotHeldDown() return gfx.mouse_cap & 4 ~= 4 end -- --[[ local function shiftModifierIsActive() return shiftKeyIsHeldDown() and controlKeyIsNotHeldDown() and optionKeyIsNotHeldDown() and commandKeyIsNotHeldDown() end ]]-- local function controlModifierIsActive() return controlKeyIsHeldDown() and optionKeyIsNotHeldDown() and commandKeyIsNotHeldDown() end local function optionModifierIsActive() return optionKeyIsHeldDown() and controlKeyIsNotHeldDown() and commandKeyIsNotHeldDown() end local function commandModifierIsActive() return commandKeyIsHeldDown() and optionKeyIsNotHeldDown() and controlKeyIsNotHeldDown() end --- if inputCharacter == inputCharacters[","] and controlModifierIsActive() then decrementScaleTonicNoteAction() end if inputCharacter == inputCharacters["."] and controlModifierIsActive() then incrementScaleTonicNoteAction() end if inputCharacter == inputCharacters["<"] and controlModifierIsActive() then decrementScaleTypeAction() end if inputCharacter == inputCharacters[">"] and controlModifierIsActive() then incrementScaleTypeAction() end if inputCharacter == inputCharacters[","] and optionModifierIsActive() then halveGridSize() end if inputCharacter == inputCharacters["."] and optionModifierIsActive() then doubleGridSize() end if inputCharacter == inputCharacters["<"] and optionModifierIsActive() then decrementOctaveAction() end if inputCharacter == inputCharacters[">"] and optionModifierIsActive() then incrementOctaveAction() end if inputCharacter == inputCharacters[","] and commandModifierIsActive() then decrementChordTypeAction() end if inputCharacter == inputCharacters["."] and commandModifierIsActive() then incrementChordTypeAction() end if inputCharacter == inputCharacters["<"] and commandModifierIsActive() then decrementChordInversionAction() end if inputCharacter == inputCharacters[">"] and commandModifierIsActive() then incrementChordInversionAction() end if inputCharacter == inputCharacters["CMDQ"] then decrementScaleTonicNoteAction() end if inputCharacter == inputCharacters["CMDW"] then incrementScaleTonicNoteAction() end if inputCharacter == inputCharacters["CMDE"] then decrementScaleTypeAction() end if inputCharacter == inputCharacters["CMDR"] then incrementScaleTypeAction() end if inputCharacter == inputCharacters["CMDA"] then halveGridSize() end if inputCharacter == inputCharacters["CMDS"] then doubleGridSize() end if inputCharacter == inputCharacters["CMDD"] then decrementOctaveAction() end if inputCharacter == inputCharacters["CMDF"] then incrementOctaveAction() end --if inputCharacter == inputCharacters["CMDY"] then -- decrementChordTypeAction() --end if inputCharacter == inputCharacters["CMDZ"] then decrementChordTypeAction() end if inputCharacter == inputCharacters["CMDX"] then incrementChordTypeAction() end if inputCharacter == inputCharacters["CMDC"] then decrementChordInversionAction() end if inputCharacter == inputCharacters["CMDV"] then incrementChordInversionAction() end if inputCharacter == inputCharacters["CMDG"] then moveCursor() end if inputCharacter == inputCharacters["ARRIGHT"] then moveCursor() end if inputCharacter == inputCharacters["ARLEFT"] then moveEditCursorPosition(-noteLength()) end if inputCharacter == inputCharacters["HELP"] then reaper.ClearConsole() msg="1 - play scale chord 1\n" msg=msg.."2 - play scale chord 2\n" msg=msg.."3 - play scale chord 3\n" msg=msg.."4 - play scale chord 4\n" msg=msg.."5 - play scale chord 5\n" msg=msg.."6 - play scale chord 6\n" msg=msg.."7 - play scale chord 7\n" msg=msg.."q - higher scale note 1\n" msg=msg.."w - higher scale note 2\n" msg=msg.."e - higher scale note 3\n" msg=msg.."r - higher scale note 4\n" msg=msg.."t - higher scale note 5\n" msg=msg.."y - higher scale note 6\n" msg=msg.."u - higher scale note 7\n" msg=msg.."a - scale note 1\n" msg=msg.."s - scale note 2\n" msg=msg.."d - scale note 3\n" msg=msg.."f - scale note 4\n" msg=msg.."g - scale note 5\n" msg=msg.."h - scale note 6\n" msg=msg.."j - scale note 7\n" msg=msg.."z - lower scale note 1\n" msg=msg.."x - lower scale note 2\n" msg=msg.."c - lower scale note 3\n" msg=msg.."v - lower scale note 4\n" msg=msg.."b - lower scale note 5\n" msg=msg.."n - lower scale note 6\n" msg=msg.."m - lower scale note 7\n" msg=msg.."cmd q - decrement scale tonic note\n" msg=msg.."cmd w - increment scale tonic note\n" msg=msg.."cmd e - decrement scale type\n" msg=msg.."cmd r - increment scale type\n" msg=msg.."cmd a - halve grid size\n" msg=msg.."cmd s - double grid size\n" msg=msg.."cmd d - decrement octave\n" msg=msg.."cmd f - increment octave\n" msg=msg.."cmd y - decrement chord type\n" msg=msg.."cmd z - decrement chord type (in german version)\n" msg=msg.."cmd x - increment chord type\n" msg=msg.."cmd c - decrement chord inversion\n" msg=msg.."cmd v -increment chord inversion\n" msg=msg.."cmd g - cursor one step right in grid\n" msg=msg.."right arrow - cursour one step right (only with midi-item) \n" msg=msg.."left arrow - cursor one step left (only with midi-item)\nF1 - this help" reaper.ShowConsoleMsg(msg) end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" Interface = {} Interface.__index = Interface local interfaceWidth = 775 local interfaceHeight = 620 local function getInterfaceXPos() local screenWidth = getScreenWidth() return screenWidth/2 - interfaceWidth/2 end local function getInterfaceYPos() local screenHeight = getScreenHeight() return screenHeight/2 - interfaceHeight/2 end local dockerXPadding = 0 local dockerYPadding = 0 function Interface:init(name) local self = {} setmetatable(self, Interface) self.name = name self.x = getInterfaceXPos() self.y = getInterfaceYPos() self.width = interfaceWidth self.height = interfaceHeight self.elements = {} return self end function Interface:restartGui() self.elements = {} self:startGui() end local function getDockerXPadding() if gfx.w <= interfaceWidth then return 0 end return (gfx.w - interfaceWidth) / 2 end function Interface:startGui() currentWidth = gfx.w dockerXPadding = getDockerXPadding() self:addMainWindow() self:addDocker() self:addTopFrame() self:addBottomFrame() end function Interface:addMainWindow() gfx.clear = reaper.ColorToNative(36, 36, 36) local dockState = 0 if windowShouldBeDocked() then dockState = getDockState() end gfx.init(self.name, self.width, self.height, dockState, self.x, self.y) end function Interface:addDocker() local docker = Docker:new() table.insert(self.elements, docker) end function Interface:addChordButton(buttonText, x, y, width, height, scaleNoteIndex, chordTypeIndex, chordIsInScale) local chordButton = ChordButton:new(buttonText, x, y, width, height, scaleNoteIndex, chordTypeIndex, chordIsInScale) table.insert(self.elements, chordButton) end function Interface:addHeader(headerText, x, y, width, height, getTextCallback) local header = Header:new(headerText, x, y, width, height, getTextCallback) table.insert(self.elements, header) end function Interface:addFrame(x, y, width, height) local frame = Frame:new(x, y, width, height) table.insert(self.elements, frame) end function Interface:addLabel(x, y, width, height, getTextCallback) local label = Label:new(x, y, width, height, getTextCallback) table.insert(self.elements, label) end function Interface:addDropdown(x, y, width, height, options, defaultOptionIndex, onSelectionCallback) local dropdown = Dropdown:new(x, y, width, height, options, defaultOptionIndex, onSelectionCallback) table.insert(self.elements, dropdown) end function Interface:addChordInversionValueBox(x, y, width, height) local valueBox = ChordInversionValueBox:new(x, y, width, height) table.insert(self.elements, valueBox) end function Interface:addOctaveValueBox(x, y, width, height) local valueBox = OctaveValueBox:new(x, y, width, height) table.insert(self.elements, valueBox) end function Interface:updateElements() for _, element in pairs(self.elements) do element:update() end end function Interface:update() self:updateElements() gfx.update() if not mouseButtonIsNotPressedDown and leftMouseButtonIsNotHeldDown() then mouseButtonIsNotPressedDown = true end if scaleTonicNote ~= getScaleTonicNote() then scaleTonicNote = getScaleTonicNote() updateScaleData() self:restartGui() end if scaleType ~= getScaleType() then scaleType = getScaleType() updateScaleData() self:restartGui() end if currentWidth ~= gfx.w then self:restartGui() end if guiShouldBeUpdated then self:restartGui() guiShouldBeUpdated = false end if windowIsDocked() and (getDockState() ~= gfx.dock(-1)) then setDockState(gfx.dock(-1)) end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local windowWidth = 775 scaleNames = {} for key, scale in ipairs(scales) do table.insert(scaleNames, scale['name']) end local xMargin = 8 local yMargin = 8 local xPadding = 16 local yPadding = 5 local scaleLabelWidth = nil local horizontalMargin = 8 local scaleTonicNoteWidth = 50 local scaleTypeWidth = 150 local octaveLabelWidth = nil local octaveValueBoxWidth = 55 keySelectionFrameHeight = 25 function Interface:addTopFrame() self:addFrame(xMargin+dockerXPadding, yMargin, self.width - 2 * xMargin, keySelectionFrameHeight) self:addScaleLabel() self:addScaleTonicNoteDropdown() self:addScaleTypeDropdown() self:addScaleNotesTextLabel() self:addOctaveLabel() self:addOctaveSelectorValueBox() end function Interface:addScaleLabel() local labelText = "Scale:" scaleLabelWidth = gfx.measurestr(labelText) local labelXpos = xMargin+xPadding local labelYpos = yMargin+yPadding local labelHeight = 16 self:addLabel(labelXpos+dockerXPadding, labelYpos, scaleLabelWidth, labelHeight, function() return labelText end) end function Interface:addScaleTonicNoteDropdown() local scaleTonicNoteXpos = xMargin+xPadding+scaleLabelWidth+horizontalMargin local scaleTonicNoteYpos = yMargin+yPadding+1 local scaleTonicNoteHeight = 15 local onScaleTonicNoteSelection = function(i) setScaleTonicNote(i) setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() end local scaleTonicNote = getScaleTonicNote() self:addDropdown(scaleTonicNoteXpos+dockerXPadding, scaleTonicNoteYpos, scaleTonicNoteWidth, scaleTonicNoteHeight, notes, scaleTonicNote, onScaleTonicNoteSelection) end function Interface:addScaleTypeDropdown() local scaleTypeXpos = xMargin+xPadding+scaleLabelWidth+scaleTonicNoteWidth+horizontalMargin*1.5 local scaleTypeYpos = yMargin+yPadding+1 local scaleTypeHeight = 15 local onScaleTypeSelection = function(i) setScaleType(i) setSelectedScaleNote(1) setChordText("") resetSelectedChordTypes() resetChordInversionStates() updateScaleData() updateScaleDegreeHeaders() end local scaleName = getScaleType() self:addDropdown(scaleTypeXpos+dockerXPadding, scaleTypeYpos, scaleTypeWidth, scaleTypeHeight, scaleNames, scaleName, onScaleTypeSelection) end function Interface:addScaleNotesTextLabel() local getScaleNotesTextCallback = function() return getScaleNotesText() end local scaleNotesXpos = xMargin+xPadding+scaleLabelWidth+scaleTonicNoteWidth+scaleTypeWidth+horizontalMargin*2+4 local scaleNotesYpos = yMargin+yPadding+1 local scaleNotesWidth = 360 local scaleNotesHeight = 15 self:addLabel(scaleNotesXpos+dockerXPadding, scaleNotesYpos, scaleNotesWidth, scaleNotesHeight, getScaleNotesTextCallback) end function Interface:addOctaveLabel() local labelText = "Octave:" octaveLabelWidth = gfx.measurestr(labelText) local labelYpos = yMargin+yPadding+1 local labelHeight = 15 local labelXpos = windowWidth - 80 - octaveValueBoxWidth self:addLabel(labelXpos+dockerXPadding, labelYpos, octaveLabelWidth, labelHeight, function() return labelText end) end function Interface:addOctaveSelectorValueBox() local windowWidth = 775 local valueBoxXPos = windowWidth - octaveValueBoxWidth - xMargin - xPadding + 3 local valueBoxYPos = yMargin + 6 local valueBoxHeight = 15 self:addOctaveValueBox(valueBoxXPos+dockerXPadding, valueBoxYPos, octaveValueBoxWidth, valueBoxHeight) end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" local xMargin = 8 local yMargin = 8 + keySelectionFrameHeight + 6 local xPadding = 7 local yPadding = 30 local headerHeight = 25 local inversionLabelWidth = 80 local inversionValueBoxWidth = 55 local chordTextWidth = nil function Interface:addBottomFrame() local chordButtonsFrameHeight = self.height - yMargin - 6 self:addFrame(xMargin+dockerXPadding, yMargin, self.width - 2 * xMargin, chordButtonsFrameHeight) self:addChordTextLabel() self:addInversionLabel() self:addInversionValueBox() self:addHeaders() self:addChordButtons() end function Interface:addChordTextLabel() local getChordTextCallback = function() return getChordText() end local chordTextXpos = xMargin + xPadding local chordTextYpos = yMargin + 4 chordTextWidth = self.width - 4 * xMargin - inversionLabelWidth - inversionValueBoxWidth - 6 local chordTextHeight = 24 self:addLabel(chordTextXpos+dockerXPadding, chordTextYpos, chordTextWidth, chordTextHeight, getChordTextCallback) end function Interface:addInversionLabel() local inversionLabelText = "Inversion:" local inversionLabelXPos = xMargin + xPadding + chordTextWidth local inversionLabelYPos = yMargin + 4 local stringWidth, _ = gfx.measurestr(labelText) local inversionLabelTextHeight = 24 self:addLabel(inversionLabelXPos+dockerXPadding, inversionLabelYPos, inversionLabelWidth, inversionLabelTextHeight, function() return inversionLabelText end) end function Interface:addInversionValueBox() local inversionValueBoxXPos = xMargin + xPadding + chordTextWidth + inversionLabelWidth + 2 local inversionValueBoxYPos = yMargin + 9 local inversionValueBoxHeight = 15 self:addChordInversionValueBox(inversionValueBoxXPos+dockerXPadding, inversionValueBoxYPos, inversionValueBoxWidth, inversionValueBoxHeight) end function Interface:addHeaders() for i = 1, #scaleNotes do local headerWidth = 104 local innerSpacing = 2 local headerXpos = xMargin+xPadding-1 + headerWidth * (i-1) + innerSpacing * i local headerYpos = yMargin+yPadding self:addHeader(headerXpos+dockerXPadding, headerYpos, headerWidth, headerHeight, function() return getScaleDegreeHeader(i) end) end end function Interface:addChordButtons() local scaleNoteIndex = 1 for note = getScaleTonicNote(), getScaleTonicNote() + 11 do if noteIsInScale(note) then for chordTypeIndex, chord in ipairs(scaleChords[scaleNoteIndex]) do local text = getScaleNoteName(scaleNoteIndex) .. chord['display'] local buttonWidth = 104 local buttonHeight = 38 local innerSpacing = 2 local xPos = xMargin + xPadding + buttonWidth * (scaleNoteIndex-1) + innerSpacing * scaleNoteIndex + dockerXPadding local yPos = yMargin + yPadding + headerHeight + buttonHeight * (chordTypeIndex-1) + innerSpacing * (chordTypeIndex-1) - 3 local numberOfChordsInScale = getNumberOfScaleChordsForScaleNoteIndex(scaleNoteIndex) if chordTypeIndex > numberOfChordsInScale then local chordIsInScale = false self:addChordButton(text, xPos, yPos, buttonWidth, buttonHeight, scaleNoteIndex, chordTypeIndex, chordIsInScale) else local chordIsInScale = true self:addChordButton(text, xPos, yPos, buttonWidth, buttonHeight, scaleNoteIndex, chordTypeIndex, chordIsInScale) end end scaleNoteIndex = scaleNoteIndex + 1 end end end local workingDirectory = reaper.GetResourcePath() .. "/Scripts/ChordGun/src" --clearConsoleWindow() updateScaleData() local interface = Interface:init("ChordGun") interface:startGui() local function windowHasNotBeenClosed() return inputCharacter ~= -1 end local function main() handleInput() if windowHasNotBeenClosed() then reaper.runloop(main) end interface:update() end main()