Implement performance (broken)
This commit is contained in:
parent
1d12d95542
commit
489741d3ed
@ -1,16 +1,82 @@
|
||||
(ns performance
|
||||
(:require [clojure.core.match :refer [match]]))
|
||||
|
||||
;; A note attribute is a modifier or descriptor attached to a note
|
||||
;; It goes in the note map as a map in the :attrs key
|
||||
;; Here's an example of a note with attributes:
|
||||
(def note-with-attributes
|
||||
{:pitch [:E 4]
|
||||
:attrs {:volume 127 ;; 0 - 127
|
||||
:fingering 4 ;; ???
|
||||
:dynamics "pianissimo" ;; maybe, not actually sure what string should go here
|
||||
:params []}}) ;; an array of doubles ???
|
||||
|
||||
;; A phrase attribute is attached to the :phrase key in a :modify operation
|
||||
;; It is a map with any of keys :dynamic, :tempo, :articulation, :ornament.
|
||||
;; Each value is a vector where the first value is a type descriptor and the
|
||||
;; second is a numeric value, if applicable. See p. 144 for all possible values
|
||||
(def some-phrase-attributes
|
||||
{:dynamic [:crescendo 30]
|
||||
:tempo [:accelerando 3]
|
||||
:articulation [:staccato 10]
|
||||
:ornament [:trill]})
|
||||
|
||||
;; A player is an entity that knows how to interpret music in a certain ways
|
||||
;; I need to flesh most of this out -- see section 8.3 on page 141
|
||||
(def a-player
|
||||
{:name "player1" ;; player name
|
||||
(declare default-play-note)
|
||||
(declare default-interp-phrase)
|
||||
(declare perf)
|
||||
(def default-player
|
||||
{:name "Default" ;; player name
|
||||
:play-note
|
||||
(fn [context note] ()) ;; function to play a note with a context, returns Performance
|
||||
default-play-note ;; function to play a note with a context, returns Performance
|
||||
:interp-phrase
|
||||
(fn [p-map context attrs music]) ;; function to interpret a Music value, returns performance
|
||||
default-interp-phrase ;; function to interpret a phrase, returns performance
|
||||
:notate (fn [context note] ())}) ;; function to transcribe a note - not implemented in Euterpea
|
||||
|
||||
(def default-player-map {"Default" default-player})
|
||||
|
||||
(defn default-play-note
|
||||
"Turns a note into an event, interpreting the :volume attribute"
|
||||
[context note]
|
||||
(let [{:keys [time instrument duration-t pitch-offset volume]} context
|
||||
{:keys [duration pitch attrs]} note
|
||||
event {:time time
|
||||
:instrument instrument
|
||||
:duration (* duration duration-t)
|
||||
:volume volume
|
||||
:pitch (+ (music/abs-pitch pitch) pitch-offset)
|
||||
:params []}] ;; still don't know what params is for
|
||||
(reduce ;; this could be an elegant way to handle :modify too
|
||||
(fn [evt attr]
|
||||
(match attr
|
||||
[:volume v] (assoc evt :volume v)
|
||||
[:params pms] (assoc evt :params pms)
|
||||
_ evt))
|
||||
event (seq attrs))))
|
||||
|
||||
(defn default-interp-phrase
|
||||
"Interprets a phrase, accounting for accents, staccato, and legato"
|
||||
[player-map context attrs music]
|
||||
(let [[performance duration] (perf player-map context music)
|
||||
interpreted (reduce
|
||||
(fn [pf attr]
|
||||
(match attr
|
||||
[:dynamic [:accent n]] (map
|
||||
#(assoc % :volume (Math/round
|
||||
(* n (:volume %))))
|
||||
pf)
|
||||
[:articulation [:staccato n]] (map #(assoc % :duration
|
||||
(* n (:duration %)))
|
||||
pf)
|
||||
[:articulation [:legato n]] (map #(assoc % :duration
|
||||
(* n (:duration %)))
|
||||
pf)
|
||||
_ pf))
|
||||
performance (seq attrs))]
|
||||
[interpreted duration]))
|
||||
|
||||
|
||||
;; A performance is a data structure that describes the act
|
||||
;; of performing a piece of music. It is a vector of events,
|
||||
;; where each event describes the playing of a single note
|
||||
@ -28,14 +94,14 @@
|
||||
;; changing tempo, key signature, tempo, etc.
|
||||
(def my-context
|
||||
{:time 0 ;; time at which the performance starts in seconds
|
||||
:player a-player ;; player currently interpreting the music
|
||||
:player default-player ;; player currently interpreting the music
|
||||
:instrument "Cello" ;; instrument currently playing (?)
|
||||
:duration-t 2 ;; the duration in seconds of a whole note
|
||||
:duration-t 4 ;; the duration in seconds of a whole note -- 4 == 60 bpm
|
||||
:pitch-offset 0 ;; a pitch offset -- number of semitones to transpose notes up or down
|
||||
:volume 50 ;; volume of performance
|
||||
:key [:C :major]}) ;; current key signature
|
||||
|
||||
(defn merge-perfs
|
||||
(defn merge-perfs ;; TODO what if p1 and p2 are single notes?
|
||||
"Merges two performances"
|
||||
[p1 p2]
|
||||
(if (nil? (seq p1))
|
||||
@ -48,12 +114,6 @@
|
||||
(into [e1] (merge-perfs (rest p1) p2))
|
||||
(into [e2] (merge-perfs p1 (rest p2))))))))
|
||||
|
||||
(defn assoc-if-present
|
||||
[cmap context control key val-func]
|
||||
(if (control cmap)
|
||||
(assoc context key (val-func (control cmap)))
|
||||
context))
|
||||
|
||||
(declare modify)
|
||||
|
||||
(defn perf
|
||||
@ -62,11 +122,10 @@
|
||||
(let [{:keys [player duration-t]} context]
|
||||
(match music
|
||||
{:duration duration
|
||||
:pitch pitch} (apply (:play-note player)
|
||||
context
|
||||
duration
|
||||
pitch
|
||||
(* duration duration-t))
|
||||
:pitch pitch} [((:play-note player)
|
||||
context
|
||||
music)
|
||||
(* duration duration-t)]
|
||||
{:duration duration} [[] (* duration duration-t)]
|
||||
[:+ & ms] (reduce (fn [[events-acc dur-acc] m]
|
||||
(let [[events dur] (perf player-map
|
||||
@ -89,8 +148,6 @@
|
||||
context
|
||||
m))))
|
||||
|
||||
(def default-player-map {}) ;; TODO
|
||||
|
||||
(defn perform
|
||||
"Convert a music value to a performance value"
|
||||
([player-map context music]
|
||||
@ -99,21 +156,21 @@
|
||||
(perform default-player-map context music)))
|
||||
|
||||
(defn modify
|
||||
"Modifies the context of a performance, returns new performance"
|
||||
"Modifies the context of a performance, returns a new performance"
|
||||
[control player-map context music]
|
||||
(let [assoc-func (partial assoc-if-present control)
|
||||
(let [phrase (:phrase control)
|
||||
new-context
|
||||
(-> context
|
||||
(assoc-func :tempo :duration-t
|
||||
(fn [r] (/ (:duration-t context) r)))
|
||||
(assoc-func :transpose :pitch-offset
|
||||
(fn [p] (+ (:pitch-offset context) p)))
|
||||
(assoc-func :instrument :instrument identity)
|
||||
(assoc-func :key :key identity) ;; a key looks like [:C :major]
|
||||
(assoc-func :player :player
|
||||
(fn [name] (name player-map))))]
|
||||
(if (:phrase control)
|
||||
((:interp-phrase (:player new-context)) player-map new-context (:phrase control))
|
||||
(reduce (fn [cxt c]
|
||||
(match c
|
||||
[:tempo r] (assoc cxt :duration-t (/ (:duration-t cxt) r))
|
||||
[:transpose p] (assoc cxt :pitch-offset (+ (:pitch-offset cxt) p))
|
||||
[:instrument i] (assoc cxt :instrument i)
|
||||
[:key key] (assoc cxt :key key)
|
||||
[:player name] (assoc cxt :player (get player-map name))
|
||||
_ cxt))
|
||||
context (seq control))]
|
||||
(if phrase
|
||||
((:interp-phrase (:player new-context)) player-map new-context phrase music)
|
||||
(perf player-map new-context music))))
|
||||
|
||||
(defn metro
|
||||
|
Loading…
Reference in New Issue
Block a user