Translate number pairs to guitar notes

18

1

A guitar fretboard diagram looks like this:

  0  1  2  3  4  5  6  7  8  9 10 11 12   <- Fret number (0 means it's open)
|-E--F--F#-G--G#-A--A#-B--C--C#-D--D#-E
|-B--C--C#-D--D#-E--F--F#-G--G#-A--A#-B 
|-G--G#-A--A#-B--C--C#-D--D#-E--F--F#-G
|-D--D#-E--F--F#-G--G#-A--A#-B--C--C#-D
|-A--A#-B--C--C#-D--D#-E--F--F#-G--G#-A
|-E--F--F#-G--G#-A--A#-B--C--C#-D--D#-E

As you can see, the first string (from the top) open is an E. The first fret on the first string is an F. The fourth fret on the third string is a B. Note that the first note is the zeroth fret, not the first.

This can be written with numbers on the format string, fret. The strings are numbered from 1 to 6 from top to bottom. The frets are numbered from 0 to 12 from left to right. The first E is therefore 1, 0. Some other examples:

1, 0 --> E
1, 1 --> F
3, 5 --> C
5, 1 --> A# 
6, 6 --> A#

Challenge:

Take N pairs of numbers (s and f), and output a delimited note succession.

  • The input may be on any suitable format. tuples, 2D-matrix, two separate lists, an interweaved list (string,fret,string,fret...) etc.
  • The output tone should be separated, but the delimiter is optional (comma, space, dash...). The output can be in either upper or lower case.
  • s (for string) will be in the range [1, 6] (you may choose to have i 0-indexed)
  • f (for fret) will be in the range [0, 12]

Test cases and examples:

1 4 5 2 1 3   <- String
4 2 6 3 5 1   <- Fret
G# E D# D A G#

6 2 3 1 4 2 3 2 2 2 6 5 2
0 1 2 3 4 5 6 7 8 9 10 11 12
E C A G F# E C# F# G G# D G# B  

3 3 3 3 3 3 3 3 3 3 3 3 3   <- String
0 3 5 0 3 6 5 0 3 5 3 0 0   <- Fret
G A# C G A# C# C G A# C A# G G     

// The same test case, but different input and output format:
(3,0)(3,3)(3,5)(3,3)(3,6)(3,5)(3,0)(3,3)(3,5)(3,3)(3,0)(3,0)    
G,A#,C,G,A#,C#,C,G,A#,C,A#,G,G     

Good luck, and happy golfing!

Stewie Griffin

Posted 2016-09-26T16:59:10.403

Reputation: 43 471

Not a guitarist (nor even a decent musician, really), but isn't there a significant omission here, if you're expecting output as recognizable tunes? That is, the note duration - whole, half, quarter notes, &c. – jamesqf – 2016-09-27T05:10:30.367

1

@jamesqf Nope, it's perfectly fine as long as you know the song. This is currently the most popular song on ultimate-guitar.com. Have a look at the intro.

– Stewie Griffin – 2016-09-27T06:12:59.297

Answers

4

05AB1E, 48 47 43 40 bytes

Uses CP-1252 encoding.

Both strings and frets are 0-based.

v7YT5¾7)y`Šè+•™ÎÚ,Ülu•žh'#A«‡•7V3•3BS£è,

Explanation

v                                # for each pair in input
 7YT5¾7)                         # the list [7,2,10,5,0,7]
 y`                              # flatten the pair [string, fret] and places on stack
 Šè                              # index into the list above using the string
 +                               # add the fret
 •™ÎÚ,Ülu•žh'#A«‡•7V3•3BS£       # list of accords
 è                               # index into the string using the number calculated above
 ,                               # print

Try it online!

Saved 7 bytes thanks to Adnan

Emigna

Posted 2016-09-26T16:59:10.403

Reputation: 50 798

1Exploiting bugs is very much golfy! .-) – Luis Mendo – 2016-09-26T20:31:17.083

"AA#BCC#DD#EFF#GG#"•7V3•3BS£ instead of "A A# B C C# D D# E F F# G G#"# is a few bytes shorter :). – Adnan – 2016-09-26T20:56:26.910

@Adnan: Ooh, nice base-change :) – Emigna – 2016-09-26T21:07:25.303

Also a compressed version of the "AA#BCC#DD#EFF#GG#" string: •™ÎÚ,Ülu•žh'#A«‡ (since lowercase is allowed :p). – Adnan – 2016-09-26T21:32:15.157

9

JavaScript (ES6), 79 70 bytes

a=>a.map(([s,f])=>"AA#BCC#DD#EFF#GG#".match(/.#?/g)[(s*7+(s>2)+f)%12])

Requires 1-based strings. Edit: Saved 9 bytes by directly calculating the string to fret conversion, based on @nimi's old answer.

Neil

Posted 2016-09-26T16:59:10.403

Reputation: 95 035

@Arnauld Thanks but I ended up appropriating @ nimi's answer instead. – Neil – 2016-09-26T18:54:07.687

Much more efficient indeed ;) – Arnauld – 2016-09-26T19:21:41.150

Clever. very sneaky answer – Rohan Jhunjhunwala – 2016-09-26T21:00:30.767

7

Mathematica, 62 bytes (non-competing)

<<Music`;MusicScale[100(#2+{24,19,15,10,5,0}[[#]])&@@@#,E2,9]&

The {24,19,15,10,5,0} and the E2 represent the open-string tones of the six guitar strings (for example, the top string is 24 semitones above the note E2). Non-competing because it doesn't print the names of the notes—it plays the sequence of notes! (only if you have Mathematica, unfortunately) For example,

<<Music`;MusicScale[100(#2+{24,19,15,10,5,0}[[#]])&@@@#,E2,9]&@
 {{4,0},{3,2},{2,3},{1,2},{5,0},{4,2},{3,2},{2,2},
  {5,2},{4,4},{2,0},{2,3},{6,2},{4,4},{3,2},{2,2},
  {6,3},{4,0},{3,0},{2,0},{4,0},{4,4},{3,2},{2,3},
  {6,3},{3,0},{2,0},{2,3},{5,0},{4,2},{3,2},{2,2},{4,0}}

plays the opening 4 bars or so from Pachelbel's Canon. (which is about as much of Pachelbel's Canon as I can stand)

Greg Martin

Posted 2016-09-26T16:59:10.403

Reputation: 13 940

7

MATL, 48 47 45 bytes

Thanks to @Emigna for a correction regarding input format.

Guitar and code golf... I had to answer this one!

'$)-27<'i)-'F F# G G# A A# B C C# D D#

Input format is: an array of (1-based) string, then an array of (0-based) frets.

Try it online!

Explanation

Some language features used in this answer:

  • A string is automatically converted to a numerical array of ASCII code points when some arithmetic operation is applied to it.
  • Arithmetical operations work element-wise, i.e. vectorized. So the subtraction of a string and a numerical array of the same size gives an array with the subtraction of corresponding entries.
  • Indexing is 1-based and modular.
  • A cell array is like a list in other languages. It can contain arbitrary elements, possibly arrays of different types or sizes. Here a cell array will be used to store strings of different lengths (the notes' names).

Commented code:

'$)-27<'                       % Push this string
i                              % Take first input (array of guitar strings)
)                              % Index into the string. For example, input [1 3] gives
                               % the string '$-' (indexing is 1-based)
-                              % Implicitly take second input (array of guitar frets).
                               % Subtract element-wise. This automatically converts the
                               % previous string into an array of ASCII codes. For
                               % example, second input [1 5] gives a result [-35 -40],
                               % which is [1 5] minus [36 45], where 36 and 45 are the
                               % ASCII codes of '$-' 
'F F# G G# A A# B C C# D D# E' % Push this string
Yb                             % Split at spaces. Gives a cell array of 12 (sub)strings:
                               % {'F', 'F#', 'G', ..., 'E'}
w)                             % Swap and index into the cell array of strings.
                               % Indexing is 1-based and modular. In the example, since
                               % the cell array has 12 elements, the indexing array
                               % [-35 -40] is the same [1 8], and thus it gives a 
                               % (sub-)array formed by the first and eighth cells: 
                               % {'F', 'C'}. This is displayed as the cells' contents,
                               % one per line

Luis Mendo

Posted 2016-09-26T16:59:10.403

Reputation: 87 464

1I knew I was going to find an answer from you as soon as I saw the word "Guitar" – Suever – 2016-09-26T19:42:55.630

1@LuisMendo Very nice! I like the ascii-char index trick :) – Emigna – 2016-09-26T20:07:32.380

4

Java, 174

String f(int[]s,int[]f){String o="";for(int i=0;i<s.length;++i){int n =(7*s[i]-7+f[i]+(s[i]>2?1:0))%12*2;o+="E F F#G G#A A#B C C#D D#".substring(n,n+2).trim()+" ";}return o;}

Ungolfed:

  String f(int[] s, int[] f) {
    String o = "";
    for (int i = 0; i < s.length; ++i) {
      int n = (7 * s[i] - 7 + f[i] + (s[i] > 2 ? 1 : 0)) % 12 * 2;
      o += "E F F#G G#A A#B C C#D D#".substring(n, n + 2).trim() + " ";
    }
    return o;
  }

user18932

Posted 2016-09-26T16:59:10.403

Reputation:

3

C, 104 103 bytes

main(s,f){for(;~scanf("%d%d",&s,&f);printf("%.2s\n",
"E F F#G G#A A#B C C#D D#"+(f+7*~-s+(s>2))%12*2));}

Takes numbers as string fret pairs on stdin, and outputs the note after every pair. E.g.:

1 4
G#
4 2
E 
5 6
D#
2 3
D 

orlp

Posted 2016-09-26T16:59:10.403

Reputation: 37 067

3

Ruby, 63 bytes

takes an array of 2-element arrays, in the order [string,fret].

->x{x.map{|i|"BEADGCF"[6-n=((i[0]-3)%5+2+i[1]*7)%12]+?#*(n/7)}}

Explanation

In standard tuning the guitar is one of the few stringed instruments (bowed or fretted) that has inconsistent intervals between its strings. Most have either a consistent 5-semitone interval between all pairs of adjacent strings (a "fourth") or a consistent 7-semitone interval between all pairs of adjacent strings (a "fifth.") These correspond to frequency ratios of 3:4 and 2:3 respectively, and are second in importance only to the "octave" with frequency ratio 1:2.

The guitar has mostly 5-semitone intervals. If it had 5 of these it would have a difference of 25 semitones between the 1st and 6th string. Instead, the interval between the 2nd and 3rd string is reduced to 4 semitones, giving a difference of 24 semitones (2 octaves) which is better for playing chords.

This is inconvenient for the program, so we start by changing the 1-indexed guitar intonation into a 0 indexed 5-string-bass intonation, which has all intervals of 5 semitones:

formula (i[0]-3)%5
Before                            After
String      6 5 4 3 2 1           String 4 3 2 1 0
Note        E A D G B E           Note   B E A D G

Next we add 2, and give the tuning of a fictitious 12 string bass, with intonation of the open strings as follows, and all intervals being 5 semitones (12 string "basses" do exist but I am not sure there are many with precisely this tuning.)

String       11 10 9  8  7  6  5  4  3  2  1  0 
Note         A# D# G# C# F# B  E  A  D  G  C  F

As can be seen, all the sharps are grouped together. This pattern can be repeated ad infinitum. It is known as the "circle of fifths" and is fundamental to the Western musical scale (with a bit of tuning adjustment the circle can be closed due to the fact that (3/2)**12 and 2**7 are very similar numbers.

Now we deal with the fret parameter. Unlike many other answers here, which translate the string parameter into a number of frets, I translate the fret parameter into a number of strings. In the table above it can be seen that adding 7 to the string number puts us on a string whose note name is one semitone higher. (It's in a completely different octave but that does not matter.) So we add i[1]*7 to the string number, and take it modulo 12:

n=(i[0]-3)%5+2+i[1]*7)%12

We subtract this from 6 to get a number in the range 6 to -5 and look up the letter in BEADGCF (Ruby allows negative indices to wrap around back to the end of the array.) If n>=7 we need to add a # symbol to complete the output.

Test program

f=->x{x.map{|i|"BEADGCF"[6-n=((i[0]-3)%5+2+i[1]*7)%12]+?#*(n/7)}}

z=[[6, 2, 3, 1, 4, 2, 3, 2, 2, 2, 6,5,2],[0, 1, 2, 3, 4 ,5 ,6 ,7, 8, 9, 10, 11, 12]].transpose

puts f[z]

Output

E
C
A
G
F#
E
C#
F#
G
G#
D
G#
B

Level River St

Posted 2016-09-26T16:59:10.403

Reputation: 22 049

3

C#, 131 bytes

string n(int[]s,int[]f){return string.Join(" ",s.Zip(f,(x,y)=>"E,F,F#,G,G#,A,A#,B,C,C#,D,D#".Split(',')[(7*x-7+y+(x<3?0:1))%12]));}

Input two separate lists, strings are 1 based.

Taco

Posted 2016-09-26T16:59:10.403

Reputation: 141

1Welcome to the site! Nice first answer. – James – 2016-09-30T18:54:24.547

@DJMcMayhem: Thank you :-) – Taco – 2016-10-02T18:34:07.617

1

Java 7 197,163 bytes

void f(int[]s,int[]f){String[]l={"A","A#","B","C","C#","D","D#","E","F","F#","G","G#"};int[]d={0,7,2,10,5,0,7};int j=0;for(int i:s)out.print(l[(d[i]+f[j++])%12]);}

Ungolfed

  void f(int[]s,int[]f){
 String[]l={"A","A#","B","C","C#","D","D#","E","F","F#","G","G#"};
int[]d={0,7,2,10,5,0,7};
    int j=0;
    for(int i:s)
        out.print(l[(d[i]+f[j++])%12]);



}

Numberknot

Posted 2016-09-26T16:59:10.403

Reputation: 885

1

Clora, 55 bytes

@T[0,7,2,10,5,0,7]+N%12@T[,A,A#,B,C#,D,D#,E,F,F#,G,G#]!

Explanation

@ numeric mode (read input as numbers)

T[0,7,2,10,5,0,7] Transform input using the array, ex array[Input]

+N Add N (Next input value) to current Input

%12 Modulo 12 the current input value

@ Flag numeric mode off

T[,A,A#,B,C#,D,D#,E,F,F#,G,G#] Translate the input into an array

! Use input as output value

OPSXCQ

Posted 2016-09-26T16:59:10.403

Reputation: 151

0

Haskell, 83 82 bytes

zipWith$(!!).(`drop`cycle(words"A# B C C# D D# E F F# G G# A")).([6,1,9,4,11,6]!!)

Takes a list of strings and a list of frets, both 0-indexed. Usage example:

Prelude >  ( zipWith$(!!).(`drop`cycle$words"A# B C C# D D# E F F# G G# A").([6,1,9,4,11,6]!!) ) [0,1,2,3,4,5] [0,0,0,0,0,0]
["E","B","G","D","A","E"]

From the infinite list of notes starting with A#, drop the number of notes given by the list [6,1,9,4,11,6] at index of the string and pick the note at index of the fret from the remaining list.

nimi

Posted 2016-09-26T16:59:10.403

Reputation: 34 639

Unfortunately the intervals between strings are not all equal. – Neil – 2016-09-26T18:20:25.123

@Neil: ... fixed. – nimi – 2016-09-26T18:45:40.127

It turned out to be a simple fix in JavaScript - (s*7)+(s>2) - so I'm now using that in my answer. – Neil – 2016-09-26T18:55:07.187

@Neil: ... working on that, too. – nimi – 2016-09-26T18:55:58.240

0

JavaScript (ES6), 82 81 bytes

a=>a.map(b=>(q=(b[0]+.3+b[1]*7.3|0)%12/1.7+10.3).toString(17)[0]+(q%1>.5?"#":""))

I wanted to try an all-math answer, but it turned out a little long. Maybe there's a way to golf it...

Test snippet

f=a=>a.map(b=>(q=(b[0]+.3+b[1]*7.3|0)%12/1.7+10.3).toString(17)[0]+(q%1>.5?"#":""))
console.log(f([[4,1],[2,4],[6,5],[3,2],[5,1],[1,3]]))

ETHproductions

Posted 2016-09-26T16:59:10.403

Reputation: 47 880

I wanted to use toString(17) but struggled to get it in a reasonable byte count. – Neil – 2016-09-26T18:58:40.170

0

Python 2, 94, 91, 88 bytes

for s,f in input():print"A A# B C C# D D# E F F# G G#".split()[([7,2,10,5,0,7][s]+f)%12]

There are probably some obvious improvements to be made. Input is a list of pairs, and the strings are 0-indexed, e.g:

[0, 4], [3, 2], [4, 6]...

James

Posted 2016-09-26T16:59:10.403

Reputation: 54 537

0

PHP, 102 Bytes

<?foreach($_GET[i]as$t)echo[E,F,"F#",G,"G#",A,"A#",B,C,"C#",D,"D#"][[0,7,3,10,5][$t[0]%5]+$t[1]%12]._;

Input as multiple array both 0 based for example '[[2,0],[5,3],[2,12],[3,8],[0,3]]'

Nice alternative 106 Bytes to set the # based on mod 7 congruent

<?foreach($_GET[i]as$t)echo EFFGGAABCCDD[$d=[0,7,3,10,5][$t[0]%5]+$t[1]%12].["","#"][$d%7?$d%7%2?0:1:0]._;

Jörg Hülsermann

Posted 2016-09-26T16:59:10.403

Reputation: 13 026