File: draw.scm

package info (click to toggle)
snd 25.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 44,016 kB
  • sloc: ansic: 291,818; lisp: 260,387; ruby: 71,134; sh: 3,293; fortran: 2,342; csh: 1,062; cpp: 294; makefile: 294; python: 87; xml: 27; javascript: 1
file content (253 lines) | stat: -rw-r--r-- 10,454 bytes parent folder | download | duplicates (3)
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
;;; examples of extensions to Snd's graphics

(provide 'snd-draw.scm)

(define overlay-rms-env
  
  ;; these functions are an optimization to speed up calculating the rms env graph.
  ;; ideally we'd use something like:
  ;;
  ;;   (let* ((x1 (x->position (/ i (srate)) snd chn))
  ;;          (y1 (y->position (moving-rms rms (reader)) snd chn)))
  ;;     (draw-line x0 y0 x1 y1)
  ;;
  ;; in the do-loop below that runs through the samples, but I haven't added x|y->position or draw-line
  ;; to the optimizer ("run"), and each would be looking up the graph axis info on each call even if
  ;; available to the optimizer -- this seems wasteful.  So, the grf-it function below is using the
  ;; axis info in axinf to get the pixel location for the envelope line segment break point.
  ;; Also, draw-lines takes a vector for some reason, so we need to tell "run" that it is an
  ;; integer vector (and preload it with 0).  We save the vector in the channel property 'rms-lines,
  ;; and the associated axis info in 'rms-axis-info.  Since redisplay is common in Snd, it reduces
  ;; flicker a lot to have this data instantly available.
  
  (let ((pack-x-info (lambda (axinf)
		       (float-vector (axinf 2) ;  x0
				     (axinf 4) ;  x1
				     (axinf 10) ; x_axis_x0
				     (axinf 12) ; x_axis_x1
				     (axinf 15) ; scale
				     (- (axinf 10) (* (axinf 2) (axinf 15)))))) ; base
	(pack-y-info (lambda (axinf)
			      (float-vector (axinf 3) ;  y0
					    (axinf 5) ;  y1
					    (axinf 11) ; y_axis_y0
					    (axinf 13) ; y_axis_y1
					    (axinf 16) ; scale
					    (- (axinf 11) (* (axinf 3) (axinf 16)))))) ; base
	(grf-it (lambda (val v)
		  (round
		   (if (>= val (v 1))
		       (v 3)
		       (if (<= val (v 0))
			   (v 2)
			   (+ (v 5) (* val (v 4))))))))
	
	(make-moving-rms (lambda* ((size 128))
			   (make-moving-average size)))
	
	(moving-rms (lambda (gen y)
		      (sqrt (moving-average gen (* y y))))))
    
    (lambda (snd chn)
      (let ((red (make-color 1 0 0))            ; rms env displayed in red
	    (left (left-sample snd chn))
	    (right (right-sample snd chn))
	    (rms-size 128)                      ; this could be a parameter -- not sure what the "right" size is
	    (sr (/ 1.0 (srate snd)))
	    (old-color (foreground-color snd chn))
	    (axinf (axis-info snd chn))
	    (old-axinf (channel-property 'rms-axis-info snd chn)))

	(if (equal? axinf old-axinf)                    ; the previously calculated lines can be re-used
	    (begin
	      (set! (foreground-color snd chn) red)
	      (draw-lines (channel-property 'rms-lines snd chn) snd chn time-graph #f)
	      (set! (foreground-color snd chn) old-color))
	    (let ((start (max 0 (- left rms-size))))
	      (let ((xdata (pack-x-info axinf))
		    (ydata (pack-y-info axinf))
		    (reader (make-sampler start snd chn))
		    (rms (make-moving-rms rms-size))
		    (x0 0)
		    (y0 0)
		    (line-ctr 2)
		    (lines (make-vector (* 2 (- (+ (axinf 12) 1) (axinf 10))) 0)))
		(dynamic-wind
		    (lambda ()
		      (set! (foreground-color snd chn) red))
		    (lambda ()
		      (if (< start left)                 ; check previous samples to get first rms value
			  (do ((i start (+ 1 i))) 
			      ((= i left))
			    (moving-rms rms (reader))))
		      (let ((first-sample (next-sample reader)))
			(set! x0 (grf-it (* left sr) xdata))
			(set! y0 (grf-it first-sample ydata)))
		      (set! (lines 0) x0)                ; first graph point
		      (set! (lines 1) y0)
		      (do ((i (+ left 1) (+ 1 i)))       ; loop through all samples calling moving-rms
			  ((= i right))
			(let ((x1 (grf-it (* i sr) xdata))
			      (y (moving-rms rms (next-sample reader))))
			  (if (> x1 x0)                 ; very often many samples are represented by one pixel
			      (let ((y1 (grf-it y ydata)))
				(set! (lines line-ctr) x1)
				(set! (lines (+ 1 line-ctr)) y1)
				(set! line-ctr (+ line-ctr 2))
				(set! x0 x1)
				(set! y0 y1)))))      ; else should we do "max" here? or draw a vertical line from min to max?
		      (if (< line-ctr (length lines))
			  (do ((j line-ctr (+ j 2)))       ; off-by-one in vector size calc -- need to pad so we don't get a bogus line to (0, 0)
			      ((>= j (length lines)))
			    (set! (lines j) x0)
			    (set! (lines (+ j 1)) y0)))
		      (draw-lines lines snd chn time-graph #f)
		      (set! (channel-property 'rms-lines snd chn) lines)  ; save current data for possible redisplay
		      (set! (channel-property 'rms-axis-info snd chn) axinf))
		    (lambda ()
		      (set! (foreground-color snd chn) old-color))))))))))
    
;; (hook-push after-graph-hook (lambda (hook) (overlay-rms-env (hook 'snd) (hook 'chn))))


(define display-colored-samples 
  (let ((+documentation+ "(display-colored-samples color beg dur snd chn) displays samples from beg for dur in color 
whenever they're in the current view."))
    (lambda* (color beg dur snd chn)
      (let ((left (left-sample snd chn))
	    (right (right-sample snd chn))
	    (end (+ beg dur))
	    (old-color (foreground-color snd chn)))
	(when (and (< left end)
		   (> right beg))
	  (let ((data (make-graph-data snd chn)))
	    (if (float-vector? data)
		(let ((new-data (let ((samps (- (min right end) (max left beg)))
				      (offset (max 0 (- beg left))))
				  (float-vector-subseq data offset (+ offset samps)))))
		  (set! (foreground-color snd chn) color)
		  (graph-data new-data snd chn copy-context (max beg left) (min end right) (time-graph-style snd chn) #f)
		  (set! (foreground-color snd chn) old-color))
		(let* ((size (length (car data)))
		       (samps (- right left))
		       (left-bin (floor (/ (* size (max 0 (- beg left))) samps)))
		       (right-bin (floor (/ (* size (- (min end right) left)) samps)))
		       (new-low-data (float-vector-subseq (car data) left-bin right-bin))
		       (new-high-data (float-vector-subseq (cadr data) left-bin right-bin)))
		  (set! (foreground-color snd chn) color)
		  (graph-data (list new-low-data new-high-data) snd chn copy-context left-bin right-bin (time-graph-style snd chn) #f)
		  (set! (foreground-color snd chn) old-color)))))))))


(define (display-samples-in-color hook)
  (let ((snd (hook 'snd))
	(chn (hook 'chn)))
    ;; intended as after-graph-hook member 
    ;; run through 'colored-samples lists passing each to display-colored-samples
    (let ((colors (channel-property 'colored-samples snd chn)))
      (if (pair? colors)
	  (for-each
	   (lambda (vals)
	     (apply display-colored-samples (append vals (list snd chn))))
	   colors)))))


(define color-samples 
  (let ((+documentation+ "(color-samples color beg dur snd chn) causes samples from beg to beg+dur to be displayed in color"))
    (lambda* (color ubeg udur usnd uchn)
      (if (not (member display-samples-in-color (hook-functions after-graph-hook)))
	  (hook-push after-graph-hook display-samples-in-color))
      (let ((snd (or usnd (selected-sound) (car (sounds)))))
	(let ((chn (or uchn (selected-channel snd) 0))
	      (beg (or ubeg 0)))
	  (let ((dur (or udur (- (framples snd chn) beg)))
		(old-colors (or (channel-property 'colored-samples snd chn) ())))
	    (set! (channel-property 'colored-samples snd chn) (cons (list color beg dur) old-colors))
	    (update-time-graph snd chn)))))))


(define uncolor-samples 
  (let ((+documentation+ "(uncolor-samples snd chn) cancels sample coloring in the given channel"))
    (lambda* (usnd uchn)
      (let* ((snd (or usnd (selected-sound) (car (sounds))))
	     (chn (or uchn (selected-channel snd) 0)))
	(set! (channel-property 'colored-samples snd chn) ())
	(update-time-graph snd chn)))))


(define display-previous-edits 
  (let ((+documentation+ "(display-previous-edits snd chn) displays all edits of the current sound, with older versions gradually fading away"))
    (lambda (snd chn)
      (let ((eds (edit-position snd chn)))
	(when (> eds 0)
	  (let* ((old-color (foreground-color snd chn))
		 (clist (color->list old-color)))
	    (let ((rinc (/ (- 1.0 (car clist)) (+ eds 1)))
		  (ginc (/ (- 1.0 (cadr clist)) (+ eds 1)))
		  (binc (/ (- 1.0 (caddr clist)) (+ eds 1))))
	      (do ((pos 0 (+ 1 pos))
		   (re (- 1.0 rinc) (- re rinc))
		   (ge (- 1.0 ginc) (- ge ginc))
		   (be (- 1.0 binc) (- be binc)))
		  ((> pos eds))
		(let ((data (make-graph-data snd chn pos)))
		  (set! (foreground-color snd chn) (make-color re ge be))
		  (graph-data data snd chn copy-context #f #f (time-graph-style snd chn) #f)))
	      (set! (foreground-color snd chn) old-color))))))))

(define overlay-sounds
  (let ((+documentation+ "(overlay-sounds . args) overlays onto its first argument all subsequent arguments: (overlay-sounds 1 0 3)"))
    (lambda args
      (let ((base (if (integer? (car args)) 
		      (integer->sound (car args)) 
		      (car args))))
	(hook-push after-graph-hook
		   (lambda (hook)
		     (let ((snd (hook 'snd))
			   (chn (hook 'chn)))
		       (if (equal? snd base)
			   (for-each 
			    (lambda (nsnd)
			      (if (and (sound? nsnd)
				       (> (chans nsnd) chn))
				  (graph-data (make-graph-data nsnd chn) base chn copy-context #f #f graph-dots #f)))
			    (cdr args))))))))))


(define samples-via-colormap 
  (let ((+documentation+ "(samples-via-colormap snd chn) displays time domain graph using current colormap (just an example of colormap-ref)"))
    (lambda (snd chn)
      (let ((data (make-graph-data snd chn)))
	(define (samples-1 cur-data)
	  (let ((left (left-sample snd chn))
		(right (right-sample snd chn))
		(old-color (foreground-color snd chn))
		(y0 (y->position (cur-data 0)))
		(colors (make-vector *colormap-size* #f))
		(len (length cur-data)))
	    (let ((x0 (x->position (/ left (srate snd))))
		  (incr (/ (- (+ right 1) left) len)))
	      (do ((i (+ left incr) (+ i incr))
		   (j 1 (+ 1 j)))
		  ((or (>= i right)
		       (>= j len)))
		(let ((x1 (x->position (/ i (srate snd))))
		      (y1 (y->position (cur-data j))))
		  (let* ((x (abs (cur-data j)))
			 (ref (floor (* *colormap-size* x))))
		    (set! (foreground-color snd chn) 
			  (or (colors ref)
			      (let ((new-color (apply make-color (colormap-ref (colormap) x))))
				(set! (colors ref) new-color)))))
		  (draw-line x0 y0 x1 y1 snd chn time-graph #f)
		  (set! x0 x1)
		  (set! y0 y1)))
	      (set! (foreground-color snd chn) old-color))))

	(when data
	  (if (float-vector? data)
	      (samples-1 data)
	      (begin
		(samples-1 (car data))
		(samples-1 (cadr data)))))))))