forked from defaultxr/cl-patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
supercollider.lisp
125 lines (99 loc) · 5.26 KB
/
supercollider.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
;;; supercollider.lisp - the SuperCollider/cl-collider backend for cl-patterns.
;; FIX: changing :instrument in pmono causes the old one to stay playing.
;; FIX: multichannel expansion breaks :mono
(in-package #:cl-patterns)
;;; global settings
(defvar *cl-collider-buffer-preview-synth* :spt
"The name of the synth to use to `play' a buffer.")
;;; backend functions
(defmethod start-backend ((backend (eql :supercollider)) &rest rest &key (name "cl-collider") (host "localhost") (port 4444) just-connect-p &allow-other-keys)
(setf cl-collider:*s* (cl-collider:make-external-server
name
:server-options (apply #'cl-collider:make-server-options
(remove-from-plist rest :name :host :port :just-connect-p))
:host host
:port port
:just-connect-p just-connect-p))
(cl-collider:server-boot cl-collider:*s*))
(defmethod stop-backend ((backend (eql :supercollider)))
(cl-collider:server-quit cl-collider:*s*))
(defmethod backend-instrument-controls (instrument (backend (eql :supercollider)))
(mapcar #'car (cl-collider:synthdef-metadata instrument :controls)))
(defmethod backend-node-p (object (backend (eql :supercollider)))
(typep object 'cl-collider::node))
(defmethod backend-timestamps-for-event (event task (backend (eql :supercollider)))
(let ((time (if-let ((timestamp (raw-event-value event :timestamp-at-start)))
(+ (local-time:timestamp-to-unix timestamp) (* (local-time:nsec-of timestamp) 1.0d-9))
(cl-collider:now))))
(list time (+ time (dur-time (sustain event))))))
(defmethod backend-proxys-node (id (backend (eql :supercollider)))
(let ((proxy-table (cl-collider::node-proxy-table cl-collider::*s*)))
(etypecase id
(symbol
(gethash id proxy-table))
(string
(find id (hash-table-values proxy-table)
:key #'cl-collider::name
:test #'string-equal))
(integer
(find id (hash-table-values proxy-table)
:key #'cl-collider::id
:test #'=))
(cl-collider::node
;; even if we're provided a node object, we still look it up in cl-collider's node-proxy-table.
;; this is because if the node was a proxy, it may have been redefined, which would cause its ID to change
;; thus causing the ID stored in the node object to be invalid.
(backend-proxys-node (cl-collider::name id) :supercollider)))))
(defmethod backend-control-node-at (time (node symbol) params (backend (eql :supercollider)))
(cl-collider:at time
(apply #'cl-collider:synth node params)))
(defmethod backend-control-node-at (time (node cl-collider::node) params (backend (eql :supercollider)))
(cl-collider:at time
(apply #'cl-collider:ctrl node params)))
(defmethod backend-convert-object ((object cl-collider::buffer) key (backend (eql :supercollider)))
(declare (ignore key))
(cl-collider:bufnum object))
(defmethod backend-convert-object ((object cl-collider::bus) key (backend (eql :supercollider)))
(declare (ignore key))
(cl-collider:busnum object))
(defmethod backend-convert-object ((object cl-collider::node) key (backend (eql :supercollider)))
(let ((bus (if (eql key :out)
(cl-collider:synthdef-metadata object :input-bus)
(or (cl-collider:synthdef-metadata object :output-bus)
(cl-collider:synthdef-metadata object :input-bus)))))
(if bus
(cl-collider:busnum bus)
object)))
(defmethod backend-convert-object ((object cl-collider::group) key (backend (eql :supercollider)))
(declare (ignore key))
(cl-collider::id object))
;;; convenience methods
(defun cl-collider-proxy (id) ;; FIX: remove this and just make `lookup-object-for-symbol' call `backend-proxys-node' for each enabled/registered backend?
"Get the object representing the `cl-collider:proxy' with the given name."
(backend-proxys-node id :supercollider))
(unless (member 'cl-collider-proxy *dictionary-lookup-functions* :test #'eql)
(appendf *dictionary-lookup-functions* (list 'cl-collider-proxy)))
(defmethod play ((object cl-collider::node))
t)
(defmethod stop ((object cl-collider::node))
(cl-collider:free object))
(defmethod end ((object cl-collider::node))
(cl-collider:release object))
(defmethod playing-p ((node cl-collider::node) &optional (server cl-collider:*s*))
(when (position (cl-collider::id node) (cl-collider::node-watcher server))
t))
(defmethod play ((buffer cl-collider::buffer))
(let* ((synth *cl-collider-buffer-preview-synth*)
(synthdef-controls (mapcar #'car (cl-collider:synthdef-metadata synth :controls))))
(play (event :backend :supercollider
;; :type :note-on ;; to avoid automatically stopping it ;; FIX: implement this note type
:instrument synth
(find-if (lambda (x) ;; buffer or bufnum argument
(position x (list 'buffer 'bufnum) :test #'string-equal))
synthdef-controls)
(cl-collider:bufnum buffer) ;; get the actual buffer number
:dur 32
:quant 0
:latency 0))))
(register-backend :supercollider)
;; (enable-backend :supercollider)