;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Some examples using the new vdsp: and veclib: functions
;;
;; C arrays are very useful because (a) they're fast a (b) many libraries use them.
;; The easiest way to interact with C arrays in Impromptu is to use NSData/NSMutableData objects.
;; Basically NSData/NSMutableData objects are wrappers for malloc'd memory.
;;
;; Calling (objc:data:make 512) allocates 512 bytes of memory
;; and returns this "memory" as an objc object. When the objc object goes out of scope
;; the memory is automatically released (i.e. you don't need to do anything special).
;;
;; Many impromptu functions expect nsdata/nsmutabledata objects as arguments. in particular
;; the new vector libraries vdsp: and veclib: expect nsdata/nsmutabledata objects.
;; In this example file any reference to a 'vector' actually means NSData or NSMutableData
;; NOT a scheme vector.
;;
;; For a full list of functions tab complete on vDSP: or veclib:
;;
;; Note that when creating nsmutabledata objects all lengths are in bytes (think malloc)
;; but the vdsp and veclib functions take length and count arguments in number of
;; elements (think array access). The examples below should give you a reasonable idea about
;; how all this fits together
;;
;; WARNING: working with these functions (indeed any impromptu functions using nsdata) means
;; directly manipulating memory. For efficiency reasons I'm not checking everything you do
;; so be careful with your lengths counts strides etc..
;; In other words - don't access elements in NSData objects beyond your allocated memory!!
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; a couple of utility functions that we'll use later
(define print-vectorsd
(lambda args
(print '------------------)
(let ((size (/ (objc:call (car args) "length") 8)))
(dotimes (i size)
(map (lambda (v)
(print (objc:data:get-double v i)))
args)))))
(define print-vectorsf
(lambda args
(print '------------------)
(let ((size (/ (objc:call (car args) "length") 4)))
(dotimes (i size)
(map (lambda (v)
(print (objc:data:get-float v i)))
args)))))
;; create a vector (i.e. NSMutableData object) of double precision floats (i.e. Float64) zeroed out
(define v1 (objc:data:make (* 10 8)))
;; create a vector (i.e. NSMutableData object) of single precision floats (i.e. Float32) zeroed out
(define v2 (objc:data:make (* 10 4)))
;; create a vector of single precision floats (i.e. Float32) zeroed out
(define v3 (objc:data:make (* 10 4)))
;; create a vector of single precision floats (i.e. Float32) zeroed out
(define v4 (objc:data:make (* 10 4)))
;; assign random values (0.0-1.0) to every 2nd element in v1
(vdsp:vrandd v1 2 10)
(print-vectorsd v1)
;; reverse v1
(vdsp:vrvrsd v1 1 10)
(print-vectorsd v1)
;; assign the value 5.125 to each element of v2
(vdsp:vfill 5.125 v2 1 10)
(print-vectorsf v2)
;; assign a series starting at 0 incrementing by pi to v3
(vdsp:vramp 0.0 3.141592 v3 1 10)
(print-vectorsf v3)
;; assign random numbers (0.0-1.0) to v4
(vdsp:vrand v4 1 10)
(print-vectorsf v4)
;; scale v4 by 256.0 and put result in v2
(vdsp:vsmul v4 1 256.0 v2 1 10)
(print-vectorsf v2)
;; sum v2
(print 'sum-of-v2 (vdsp:sve v2 1 10))
;; let's see how fast this stuff is. one million float32 elements
(define v5 (objc:data:make (* 1000000 4)))
;; lets ramp from 0.0 to 999999.0
(let ((t (now)))
(vdsp:vramp 0.0 1.0 v5 1 1000000)
(print 'time-taken: (- (now) t)))
;; the answer is VERY FAST
;; don't print the whole vector because that will take FOREVER!
;; instead we'll print the first, last and a random internal element
(print (objc:data:get-float v5 0)
(objc:data:get-float v5 409193)
(objc:data:get-float v5 999999))
;; now let's do something cool - like convolution!
;; first create a 4 tap filter [4 3 2 1]
(define v-filter (objc:data:make (* 4 4)))
(vdsp:vramp 4.0 -1.0 v-filter 1 4)
;; then create a constant signal
(define v-signal (objc:data:make (* 4 16)))
(vdsp:vfill 1.0 v-signal 1 16)
(vdsp:vfill 0.0 v-signal 1 4)
;; now convolve the signal with the filter dumping the result into output
(define v-output (objc:data:make (* 4 16)))
(vdsp:conv v-signal 1 v-filter -1 v-output 1 16 4)
;; and print
(print-vectorsf v-output)
;; You can also create a reference into a data object.
;; This can be useful for working on subsections of a vector
;; note that this is a ref not a copy so you MUST NOT let the original fall out of scope
;; you can however let the refence fall out of scope without danger.
(define original-v (objc:data:make (* 9 4)))
;; subrange ref takes an offset in bytes and a length in bytes
;; and returns an NSData reference to the original
(define reference-v (objc:data:subref original-v (* 3 4) (* 3 4)))
(print-vectorsf original-v)
(print-vectorsf reference-v)
(vdsp:vfill 1.0 original-v 1 9)
(vdsp:vfill 2.0 reference-v 1 3)
(print-vectorsf original-v)
(print-vectorsf reference-v)
;; dont' forget the veclib: functions
(define new-sqrt-data (objc:data:make (* 9 4)))
(veclib:vvsqrtf new-sqrt-data original-v 9)
(print-vectorsf new-sqrt-data)
;; Note that we can get and set individual elements of vectors (i.e. NSMutableData objects)
;; but we must use the correct type (i.e. float uint8 uint32 double etc..)
;; however, bare in mind that this is slow - try to use the vdsp and veclib functions
(print (objc:data:get-float original-v 0))
(objc:data:set-float original-v 0 22.125)
(objc:data:set-float original-v 8 22.125)
(print (objc:data:get-float original-v 8))
(print-vectorsf original-v)