Skip to content

Instantly share code, notes, and snippets.

@whacked
Created January 25, 2015 16:54
Show Gist options
  • Save whacked/2c6a74e65505e7d2c80d to your computer and use it in GitHub Desktop.
Save whacked/2c6a74e65505e7d2c80d to your computer and use it in GitHub Desktop.
(defn collect-into-consecutive
"collect indexes into sets of N consecutive indexes,
discarding all other indexes.
N defaults to 6 (for guitar tabs)"
([index-list]
(collect-into-consecutive index-list 6))
([index-list required-length]
(loop [input (sort index-list)
buf []
rtn []]
(let [next-rtn (if (= required-length (count buf))
(conj rtn buf)
rtn)]
(if (empty? input)
next-rtn
(let [last-val (last buf)
cur-val (first input)]
(recur (rest input)
(if (or (nil? last-val)
(= required-length (count buf))
(not= 1 (- cur-val last-val)))
[cur-val]
(conj buf cur-val))
next-rtn)))))))
;; http://stackoverflow.com/questions/21191045/get-string-indices-from-the-result-of-re-seq
(defn re-seq-pos [pattern string]
(let [m (re-matcher pattern string)]
((fn step []
(when (. m find)
(cons {:start (. m start) :end (. m end) :group (. m group)}
(lazy-seq (step))))))))
(defn parse-guitar-tab-line
"reads a single well-formed line from the guitar tab
and returns a seq of seq, where each inner seq takes the form of
[index fret-value]
index = at what index in time the pluck should occur
fret-value = being which fret to pluck; nil if it should be rest
"
[line]
(sort
(seq
(let [ ;; note: drop-while predicate needs a char (\-), not a string ("-")!
tab-string (apply str (drop-while #(not= % \-) line))]
(into
(zipmap (range (count tab-string)) (repeatedly (constantly nil)))
(map
(fn [match-group]
[(:start match-group) (Integer/parseInt (:group match-group))])
(re-seq-pos #"\d+" tab-string)))))))
(def everybody-hurts
"
;; Guitar Tab for Everybody Hurts by REM
;;
;; D G
;; E:5]--------2-----------2------------3-----------3-----[
;; B:4]------3---2-------3---3--------0---0-------0---0---[
;; G:3]----2-------2---2-------2----0-------0---0-------0-[
;; D:2]--0-----------0------------------------------------[
;; A:1]---------------------------------------------------[
;; E:0]---------------------------3-----------3-----------[
")
(defn play-tab
[guitar-tab]
(let [pluck-map-list (let [index-mapped (filter
;; a tab line must contain "-"
(fn [[_ s]] (.contains s "-"))
(into {} (map-indexed vector (s/split-lines guitar-tab))))
index-group-list (collect-into-consecutive (keys index-mapped))
]
;; from the 6-line groups, get lines from the original index-line maps
(let [sorted-grouped-line-set-list (map #(vals (sort (select-keys (into {} index-mapped) %)))
;; these will be grouped into sets of 6 lines, with indexes
(sort index-group-list))]
;; map over each 6-line-set...
(->> sorted-grouped-line-set-list
;; map over each line within the set...
(map (fn [line-set]
(apply merge-with concat
(map-indexed
(fn [string-index tab-line]
(into {}
(remove nil?
(->> tab-line
(parse-guitar-tab-line)
(map (fn [[index fret-value]]
(if fret-value
[index [string-index fret-value]])))))))
(reverse line-set))))))))
play! (partial guitar-pick-note-sequence' 100)
]
(play!
(apply
concat
(->> pluck-map-list
(map
(fn [pluck-map]
(let [max-beat (apply max (keys pluck-map))]
(map seq (remove nil? (map pluck-map (range max-beat))))
))))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment