Skip to content

Instantly share code, notes, and snippets.

Created October 5, 2020 15:20
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
398 - Newsletter

The well-tempered ergodox

This week, we're going to learn some guitar. Write a function that takes the string number and fret number and returns the note you get when plucking it. You can use the chart linked to at the bottom of this page as a guide.

;; third string, second fret
(note 3 2) => :A
;; first string, sixth fret
(note 1 6) => :A#

Open strings (no fret) are represented by 0.

You can represent sharps with a # and flats with a lowercase b.

There are obviously different ways to represent it. Try to find one that captures the underlying model of frets and intervals.

Thanks to this site for the challenge idea where it is considered Very Hard level in JavaScript.

Copy link

sztamas commented Oct 5, 2020

(def notes [:C :C# :D :D# :E :F :F# :G :G# :A :A# :B])

(def tuning [:E :B :G :D :A :E])

(defn string-notes [start]
  (->> notes
       (drop-while #(not= start %))
       (take 25)))

(defn note [string fret]
  (if (and (<= 1 string 6) (<= 0 fret 24))
    (let [open-note (tuning (dec string))]
      (nth (string-notes open-note) fret))
    "Invalid input"))

Copy link

steffan-westcott commented Oct 5, 2020

(def scale [:C :C#-Db :D :D#-Eb :E :F :F#-Gb :G :G#-Ab :A :A#-Bb :B])
(def open-notes [:E :B :G :D :A :E])

(defn note [string fret]
  (if-let [open-note (nth open-notes (dec string) nil)]
    (let [string-notes (->> scale cycle (drop-while #(not= open-note %)) (take 25))]
      (nth string-notes fret :invalid-fret))

Copy link

steffan-westcott commented Oct 5, 2020

Unicode includes musical symbols ♯ and ♭ so it's possible to write this:

(def scale [:C :C♯-D♭ :D :D♯-E♭ :E :F :F♯-G♭ :G :G♯-A♭ :A :A♯-B♭ :B])

Copy link

(defn note [s f]
  (nth (cycle [:E :F :F# :G :G# :A :A# :B :C :C# :D :D#])
       (+ (get [24 19 15 10 5 0] (dec s)) f)))

Note: the "magic values", 24, 19 etc are semitone offsets from low E.

Copy link

KingCode commented Oct 6, 2020

(def scale (cycle [:A :A#|Bb :B :C :C#|Db :D :D#|Eb :E :F :F#|Gb :G :G#|Ab]))
(def scale-offsets [7 2 10 5 0 7]) ;; E B G D A E
(def string-idx->offset (zipmap (range) scale-offsets))

(defn note [string fret]
   (let [ idx (-> string dec string-idx->offset (+ fret))]
      (nth scale idx)))

Copy link

zelark commented Oct 6, 2020

(def tuning {1 :E 2 :B 3 :G 4 :D 5 :A 6 :E})
(def scale  (cycle [:C :C#/Db :D :D#/Eb :E :F :F#/Gb :G :G# :A :A#/Bb :B]))

(defn note [string fret]
  (if-let [open-note (tuning string)]
    (->> scale
         (drop-while (complement #{open-note}))
         (drop fret)

(= (note 3 2) :A)
(= (note 1 6) :A#/Bb)
(= (note 7 0) :invalid-input)

Copy link

michelemendel commented Oct 7, 2020

(def notes [:C :C# :D :D# :E :F :F# :G :G# :A :A# :B])
(def tuning [:E :B :G :D :A :E])

(defn string-fret
  [guitar-string fret]
  (if (and
        (<= 1 guitar-string 6)
        (<= 0 fret 24))
    (let [open-note (tuning (dec guitar-string))
          pos-in-notes (mod (+ (.indexOf notes open-note) fret) 12)]
      (notes pos-in-notes))

Copy link

Realized I forgot to post my submission for last weeks. Posting incase people are still doing feedback. I realize it's not quite to spec, but couldn't decide out how to pick if something is a sharp or a flat.

(def NOTES [:A :A#-Bb :B :C :C#-Db :D :D#-Eb :E :F :F#-Gb :G :G#-Ab])
(def STANDARD-TUNING [:E :B :G :D :A :E])

(defn string-notes [open-note]
  {:pre [((set NOTES) open-note)] ;; Prevent runaway on non-existent notes
   :docstring "Get infinite sequence of notes starting from provided open-note."}
  (->> (cycle NOTES)
       (drop-while #(not= % open-note))))

(defn note
  "Returns note produced by guitar played on provided string and fret.
   Optionally takes string tuning (defaults to standard [:E :B :G :D :A :E])."
  ([string fret]
   {:pre [(<= 1 string)]}
   (note string fret STANDARD-TUNING))
  ([string fret tuning]
   {:pre [(<= 1 string (count tuning))]}
   (let [guitar (map string-notes tuning)]
     (->> guitar
          ;; Focus on string
          (drop (dec string))
          ;; Fret the string & get the note
          (drop fret)

(deftest note-test
  (testing "Edabit examples"
    (is (= (note 2 10) :A))
    (is (thrown? java.lang.AssertionError (note 0 16)))
    (is (= (note 2 19) :F#-Gb))
    (is (= (note 3  0) :G))))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment