Convert between Music clefs
Befunge, 70 64 bytes
~0~:70p##~+2%00p+"A"-~7%2++7%:3%2%00g*:10p+"A"+,00g!10g+#@_"#",@
Try it online!
The input should be in the form C# Treble
or F Bass
, although the clef can simply be the first letter (i.e. T
or B
), since the rest of the input is ignored anyway.
Explanation
~0 Read the note and push a zero (the purpose of this will become apparent later).
~:70p Read the following sharp or space and write that out as the next instruction.
As a result of this code modification, the next sequence of instructions will take one of two forms:
##~ The first # jumps over the second, and thus we perform the read instruction.
#~ But if there's only one #, we'll ending up skipping the read instruction.
At this point the stack either contains note,0,sharp,space
or note,0,space
.
+2% Add the top two stack items mod 2, returning 1 if we read a sharp, else 0 if not.
00p Save this 'sharp' boolean for later use.
At this point the stack either contains note,0
or just note
(with an implicit zero below).
+ By adding the top two items, we combine the 0 (if present) onto the note below.
"A"- We can then subtract 'A' to convert the note into a number in the range 0 to 6.
~7%2+ Read the T/B clef, then mod 7 and add 2, returning 2 or 5 (the conversion offset).
+7% Add that offset to our note number, then mod 7, to get the converted note number.
:3%2% Make a dup, and calculate mod 3 mod 2 to determine the special cases (B# or E#).
00g* Multiply that by the 'sharp' boolean, since we only care if the input was sharp.
:10p Duplicate and save this special case boolean for later.
+ Now add it to the note number, since the special cases need to be offset by 1.
"A"+, Then we can finally convert the number back into a character and output it.
00g!10g+ Now we check if the original note was not sharp, or if this was a special case.
#@_ If so, we exit immediately.
"#",@ Otherwise, we output a '#'.
Jelly, 35 34 bytes
I have a feeling some arithmetic may win over this method.
ØAḣ7µW€ż;€”#$Ẏ
Ç”C4¦”F⁵¦
Ñi+_⁸?4ị¢
Try it online!
A full program taking 1) the clef indicator 0
or 1
for Bass or Treble respectively and 2) the note; and printing the resulting note.
Would be 31 bytes if -4
and 4
were acceptable as the clef indicator input values (then Ñi+_⁸?4ị¢
can become Ñi+⁸ị¢
) but this has been clarified as not allowed unless -4 is falsey and 4 is truthy, which is not the case for Jelly.
How?
Builds a keyboard with phantom B#
and E#
keys, finds the index of the input, offsets that by 4
in the required direction, indexes back into a keyboard with those phantom keys replaced by their required results (the key above them):
ØAḣ7µW€ż;€”#$Ẏ - Link 1, keyboard with phantoms: no inputs
ØA - alphabet yield -> ['A', 'B', ..., 'Z']
7 - literal seven
ḣ - head -> ['A','B','C','D','E','F','G']
µ - new monadic chain, call that K
W€ - wrap €ach -> ["A","B","C","D","E","F","G"] ("" being lists of characters)
$ - last two links as a monad:
”# - character '#'
;€ - concatenate to €ach -> ["A#","B#","C#","D#","E#","F#","G#"]
ż - zip together -> [["A","A#"],["B","B#"],["C","C#"],["D","D#"],["E","E#"],["F","F#"],["G","G#"]]
Ẏ - tighten -> ["A","A#","B","B#","C","C#","D","D#","E","E#","F","F#","G","G#"]
Ç”C4¦”F⁵¦ - Link 2, keyboard with phantoms replaced: no inputs
Ç - call the last link (1) as a monad ["A","A#","B","B#","C","C#","D","D#","E","E#","F","F#","G","G#"]
¦ - sparse application:
4 - ...to index: literal four
”C - ...action: character 'C' -> ["A","A#","B","C","C","C#","D","D#","E","E#","F","F#","G","G#"]
¦ - sparse application:
⁵ - ...to index: literal ten
”F - ...action: character 'F' -> ["A","A#","B","C","C","C#","D","D#","E","F","F","F#","G","G#"]
Ñi+_⁸?4ị¢ - Main link: integer, clef (1 Treble / 0 Bass); list of characters, key
e.g. 0; "D#"
Ñ - next link (1) as a monad (no atom for next link as a nilad, but this works here anyway)
- ["A","A#","B","B#","C","C#","D","D#","E","E#","F","F#","G","G#"]
i - first index of key in that 8
4 - literal four
? - if:
⁸ - ...condition: chain's left argument, clef
+ - ...then: addition
_ - ...else: subtraction 4
¢ - next link as a nilad ["A","A#","B","C","C","C#","D","D#","E","F","F","F#","G","G#"]
ị - index into "C"
Python 2, 77 bytes
Function which prints to STDOUT
. True
converts bass to treble, and False
converts treble to bass.
def f(n,c):N=ord(n[0])-63-4*c;M=-~N%3<1<len(n);print chr((N+M)%7+65)+n[1:2-M]
Try it online!
Explanation:
- The first statement,
N=ord(n[0])-63-4*c;
, calculates the index (0 to 7) of the new note's letter, disregarding sharps.ord(N[0])-63-4*c
gets the current letter's index, and adds or subtracts 2 depending on the value ofc
(variable to toggle conversion direction)
- The next statement,
M=-~N%3<1<len(n);
calculates whether or not this variable will need to be adjusted. For example, if the new note isE
, and the original note had a sharp, this will need to be adjusted to anF
. The chained inequality works as follows:-~N%3<1
checks whether the new note's index is in the sequence3n-1
. This will only yield true forE
andB
, the two notes which do not have a sharp.1<len(n)
checks if the original note had a sharp (this would make the string's length larger than 1). This is necessary as, if there was no sharp, there is no need to adjust the new note letters.- This sets the value of
M
to eitherTrue
orFalse
, which can be used in calculation as1
and0
respectively, so to perform the adjustment we need only add M to N and modulo by 7.
- The final statement creates and outputs the final result.
chr((N+M)%7+65)
adds the adjustment if necessary, then converts the value from an index back to a character.+n[1:2-M]
will append a sharp symbol if bothM=0
(no adjustment was made) and the original value also had a sharp.