I'd like to write a script that receives text input from the clipboard that will be from a song lyric sheet with chords. The goal is for the function to return the text to the clipboard after transposing the chord names up or down a half-step. If the solution is with shell commands, I might still wrap it in an AppleScript. I'm having a hard time conceptualizing a good, reliable approach to the transpose function itself.
Transposing down, any "C" chord would become a "B" chord. An "F#" would become and "F". An "Ab" would become a "G", etc. It really is fine if the function only transposes down or up, cause one can cycle through, but it'd be nice if it could go either way. Here's a sample of a text, with about as many variations I can think to include:
C F G
La la is a line of lyrics with simple chords
C B7 Ab F#
La la some chords have flats and sharps
C Abm Fm
La la other lines have minor chords
F#sus Fmj7 B5 Gsus2 Gm7
La la but chords can can't kinda funky
Cdim Daug F+ G2
Doo wop with many short suffixed annotations
C/F Am/G B7/G
and any can have a slash followed by a bass note.
Notes about a chord sheet formatting:
Chord names are given on lines above the line of lyric. * Verses etc. are separated by an extra line break.
All chord names are in capitals, and no other letters on a chord line are.
Only the chord letter and the flat b or # need to change when transposed. All other "sus", "m7", "+", "dim", etc. remain unchanged.
Chord half-step progression is: A A#/Bb B C C#/Db D D#/Eb E F F#/Gb G G#/Ab
Technically, a song should only use flats b or sharps uniformly.
Any bass notes after a slash / also need to be transposed.
One of the problems of course has to do with sequential changes and not changing a chord name that has already been changed.
One could isolate the lines with chords by looking only at lines with three spaces in a row. And only the capital letters (and b & #) need to be looked at on those rows.
I found this where someone was working on chord transposing, (PHP chord transposer), but it was using inline chord notation, and also I don't speak PHP.
Here is code that I now have working. It has a lot of repeats, so is not very efficient. There a better way than this?
property chordList : {"A#", "Bb", "C#", "Db", "D#", "Eb", "F#", "Gb", "G#", "Ab", "A", "B", "C", "D", "E", "F", "G"}
property codeList : {"©12", "©13", "©16", "©17", "©19", "©20", "©23", "©24", "©26", "©27", "©11", "©14", "©15", "©18", "©21", "©22", "©25"}
property loweredList : {"A", "A", "C", "C", "D", "D", "F", "F", "G", "G", "Ab", "Bb", "B", "Db", "Eb", "E", "Gb"}
property raisedList : {"B", "B", "D", "D", "E", "E", "G", "G", "A", "A", "A#", "C", "C#", "D#", "F", "F#", "G#"}
set theString to the clipboard
set transposeUp to false -- true transposes up, false shifts down
set newString to transposeChords(theString, transposeUp)
set the clipboard to newString
on transposeChords(musicString, shiftUp)
if shiftUp then
set changeList to raisedList
else
set changeList to loweredList
end if
set otid to AppleScript's text item delimiters
considering case
set transposedString to ""
repeat with p from 1 to (count paragraphs in musicString)
set thisLine to paragraph p of musicString
if (thisLine contains " ") and (thisLine does not contain "t") then
-- change all chord names to a ©11, ©12, etc. code
repeat with c from 1 to (count chordList)
set thisLine to replaceString((item c of chordList), (item c of codeList), thisLine)
end repeat
-- change all codes to the shifted counterpart
repeat with c from 1 to (count codeList)
set thisLine to replaceString((item c of codeList), (item c of changeList), thisLine)
end repeat
set newLine to thisLine
else
set newLine to thisLine
end if
set transposedString to transposedString & newLine & return
end repeat
return transposedString
end considering
set AppleScript's text item delimiters to otid
end transposeChords
on replaceString(toFind, replaceWith, aString)
set AppleScript's text item delimiters to toFind
set aString to text items of aString
set AppleScript's text item delimiters to replaceWith
set aString to aString as string
end replaceString
1 Answer 1
After working for a couple days, I came up with code that works. It loops through paragraphs that match three spaces, and then finds/replaces each chord name, one a time, first changing them to a code, and then to the shifted chord. It's not super robust, in that it uses arrays and repeat loops through them to alter the chords. It works fine thought since song sheets are not huge. Still, a regex change if possible would be more robust.
property chordList : {"A#", "Bb", "C#", "Db", "D#", "Eb", "F#", "Gb", "G#", "Ab", "A", "B", "C", "D", "E", "F", "G"}
property codeList : {"©12", "©13", "©16", "©17", "©19", "©20", "©23", "©24", "©26", "©27", "©11", "©14", "©15", "©18", "©21", "©22", "©25"}
property loweredList : {"A", "A", "C", "C", "D", "D", "F", "F", "G", "G", "Ab", "Bb", "B", "Db", "Eb", "E", "Gb"}
property raisedList : {"B", "B", "D", "D", "E", "E", "G", "G", "A", "A", "A#", "C", "C#", "D#", "F", "F#", "G#"}
set theString to the clipboard
set transposeUp to false -- true transposes up, false shifts down
set newString to transposeChords(theString, transposeUp)
set the clipboard to newString
on transposeChords(musicString, shiftUp)
if shiftUp then
set changeList to raisedList
else
set changeList to loweredList
end if
set otid to AppleScript's text item delimiters
considering case
set transposedString to ""
repeat with p from 1 to (count paragraphs in musicString)
set thisLine to paragraph p of musicString
if (thisLine contains " ") and (thisLine does not contain "t") then
-- change all chord names to a ©11, ©12, etc. code
repeat with c from 1 to (count chordList)
set thisLine to replaceString((item c of chordList), (item c of codeList), thisLine)
end repeat
-- change all codes to the shifted counterpart
repeat with c from 1 to (count codeList)
set thisLine to replaceString((item c of codeList), (item c of changeList), thisLine)
end repeat
set newLine to thisLine
else
set newLine to thisLine
end if
set transposedString to transposedString & newLine & return
end repeat
return transposedString
end considering
set AppleScript's text item delimiters to otid
end transposeChords
on replaceString(toFind, replaceWith, aString)
set AppleScript's text item delimiters to toFind
set aString to text items of aString
set AppleScript's text item delimiters to replaceWith
set aString to aString as string
end replaceString
b
and the sign#
in place of genuine flat/sharp symbols. Is that intentional, or something that some piece fo software limits you to, or a product of not previously knowing how to access those symbols for use in text:sharp: ♯
flat: ♭
There's a bunch of others too:♩ ♪ ♫ ♬ 𝄫 ♮ 𝄞 𝄢 〽︎ 𝄀 𝄁 𝄂 𝄃 𝄆 𝄇 𝄈 𝄐 𝄑 𝄡 ⏔ ⏕ ⏖ ℟ ℣
and a few more (they may not print well in SO's choice of font, but generally look good in most monospace situations. \$\endgroup\$