synchroscope (part 3)
This commit is contained in:
parent
7d4ba661e6
commit
2bad195b15
3 changed files with 83 additions and 68 deletions
52
README.org
52
README.org
|
@ -22,9 +22,9 @@ This implementation supports the [[https://opensoundcontrol.stanford.edu/spec-1_
|
||||||
| *Type tag* | *type* | *description* | *v1.0* | *v1.1* | *cl-osc* |
|
| *Type tag* | *type* | *description* | *v1.0* | *v1.1* | *cl-osc* |
|
||||||
| i | int32 | 32-bit big-endian two’s complement integer | *R* | *R* | YES |
|
| i | int32 | 32-bit big-endian two’s complement integer | *R* | *R* | YES |
|
||||||
| f | float32 | 32-bit big-endian IEEE 754 floating point number | *R* | *R* | YES |
|
| f | float32 | 32-bit big-endian IEEE 754 floating point number | *R* | *R* | YES |
|
||||||
| s | OSC-string | A sequence of non-null ASCII characters followed by a null, | | *R* | |
|
| s | OSC-string | A sequence of non-null ASCII characters followed by a null… | | *R* | |
|
||||||
| | | followed by 0-3 additional null characters. Total bits is a multiple of 32. | *R* | N | YES |
|
| | | followed by 0-3 additional null characters. Total bits is a multiple of 32. | *R* | N | YES |
|
||||||
| b | OSC-blob | An int32 size count, followed by that many 8-bit bytes of arbitrary binary data, | | *R* | |
|
| b | OSC-blob | An int32 size count, followed by that many 8-bit bytes of arbitrary binary data… | | *R* | |
|
||||||
| | | followed by 0-3 additional zero bytes. Total bits is a multiple of 32. | *R* | N | YES |
|
| | | followed by 0-3 additional zero bytes. Total bits is a multiple of 32. | *R* | N | YES |
|
||||||
| T | True | No bytes are allocated in the argument data. | O | *R* | |
|
| T | True | No bytes are allocated in the argument data. | O | *R* | |
|
||||||
| F | False | No bytes are allocated in the argument data. | O | *R* | |
|
| F | False | No bytes are allocated in the argument data. | O | *R* | |
|
||||||
|
@ -32,7 +32,7 @@ This implementation supports the [[https://opensoundcontrol.stanford.edu/spec-1_
|
||||||
| I | Impulse | (aka “bang”), used for event triggers. No bytes are allocated in the argument data. | O | *R* | |
|
| I | Impulse | (aka “bang”), used for event triggers. No bytes are allocated in the argument data. | O | *R* | |
|
||||||
| t | OSC-timetag | an OSC timetag in NTP format, encoded in the data section | O | *R* | |
|
| t | OSC-timetag | an OSC timetag in NTP format, encoded in the data section | O | *R* | |
|
||||||
| h | int64 | 64 bit big-endian two’s complement integer | O | O | YES |
|
| h | int64 | 64 bit big-endian two’s complement integer | O | O | YES |
|
||||||
| d | float64 | 64 bit (“double”) IEEE 754 floating point number | O | O | |
|
| d | float64 | 64 bit (“double”) IEEE 754 floating point number | O | O | YES |
|
||||||
| S | OSC-string | Alternate type represented as an OSC-string (e.g to differentiate “symbols” from “strings”) | O | O | YES |
|
| S | OSC-string | Alternate type represented as an OSC-string (e.g to differentiate “symbols” from “strings”) | O | O | YES |
|
||||||
| c | | an ascii character, sent as 32 bits | O | O | |
|
| c | | an ascii character, sent as 32 bits | O | O | |
|
||||||
| r | | 32 bit RGBA color | O | O | |
|
| r | | 32 bit RGBA color | O | O | |
|
||||||
|
@ -41,49 +41,5 @@ This implementation supports the [[https://opensoundcontrol.stanford.edu/spec-1_
|
||||||
| ] | | Indicates the end of an array. | O | O | YES? |
|
| ] | | Indicates the end of an array. | O | O | YES? |
|
||||||
|
|
||||||
|
|
||||||
- *R*equired, *O*ptional and *N*ot supported (or *N*ot required).
|
- Required, Optional and Not supported (or Not required).
|
||||||
- data is encoded as =(vector (unsigned 8))= by =cl-osc=
|
- data is encoded as =(vector (unsigned 8))= by =cl-osc=
|
||||||
|
|
||||||
* Float encoding & decoding
|
|
||||||
|
|
||||||
#+BEGIN_SRC lisp
|
|
||||||
(defun encode-float32 (f)
|
|
||||||
"Encode an ieee754 float as a 4 byte vector. currently sbcl/cmucl specific."
|
|
||||||
(encode-int32 (ieee-floats:encode-float32 f)))
|
|
||||||
;; #+sbcl (encode-int32 (sb-kernel:single-float-bits f))
|
|
||||||
;; #+cmucl (encode-int32 (kernel:single-float-bits f))
|
|
||||||
;; #+openmcl (encode-int32 (CCL::SINGLE-FLOAT-BITS f))
|
|
||||||
;; #+allegro (encode-int32 (multiple-value-bind (x y)
|
|
||||||
;; (excl:single-float-to-shorts f)
|
|
||||||
;; (+ (ash x 16) y)))
|
|
||||||
;; #-(or sbcl cmucl openmcl allegro ieee-floats) (error "Can't encode single-floats using this implementation."))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+BEGIN_SRC lisp
|
|
||||||
(defun decode-float32 (v)
|
|
||||||
"Convert a vector of 4 bytes in network byte order into an ieee754 float."
|
|
||||||
(ieee-floats:decode-float32 (decode-int32 v)))
|
|
||||||
;; #+sbcl (sb-kernel:make-single-float (decode-int32 v))
|
|
||||||
;; #+cmucl (kernel:make-single-float (decode-int32 v))
|
|
||||||
;; #+openmcl (CCL::HOST-SINGLE-FLOAT-FROM-UNSIGNED-BYTE-32 (decode-uint32 v))
|
|
||||||
;; #+allegro (excl:shorts-to-single-float (ldb (byte 16 16) (decode-int32 v))
|
|
||||||
;; (ldb (byte 16 0) (decode-int32 v)))
|
|
||||||
;; #-(or sbcl cmucl openmcl allegro) (error "Can't decode single-floats using this implementation."))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+BEGIN_SRC lisp
|
|
||||||
(defun encode-float64 (d)
|
|
||||||
"Encode an ieee754 float as a 8 byte vector. currently sbcl/cmucl specific."
|
|
||||||
#+sbcl (cat (encode-int32 (sb-kernel:double-float-high-bits d))
|
|
||||||
(encode-int32 (sb-kernel:double-float-low-bits d)))
|
|
||||||
#-(or sbcl ieee-floats) (error "Can't encode double-floats using this implementation."))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+BEGIN_SRC lisp
|
|
||||||
(defun decode-float64 (v)
|
|
||||||
"Convert a vector of 8 bytes in network byte order into an ieee754 float."
|
|
||||||
#+sbcl (sb-kernel:make-double-float
|
|
||||||
(decode-uint32 (subseq v 0 4))
|
|
||||||
(decode-uint32 (subseq v 4 8)))
|
|
||||||
#-(or sbcl ieee-floats) (error "Can't decode single-floats using this implementation."))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
|
@ -66,6 +66,9 @@
|
||||||
(is (equalp
|
(is (equalp
|
||||||
(osc::decode-float32 #(254 254 254 254)) -1.6947395e38)))
|
(osc::decode-float32 #(254 254 254 254)) -1.6947395e38)))
|
||||||
|
|
||||||
|
;; (osc::decode-float32 #(127 255 255 255))
|
||||||
|
;; #<SINGLE-FLOAT quiet NaN>
|
||||||
|
|
||||||
(test osc-string
|
(test osc-string
|
||||||
"OSC string encoding tests."
|
"OSC string encoding tests."
|
||||||
(is (equalp
|
(is (equalp
|
||||||
|
@ -290,6 +293,28 @@
|
||||||
;; v1.1. tests
|
;; v1.1. tests
|
||||||
(in-suite protocol-v1.1)
|
(in-suite protocol-v1.1)
|
||||||
|
|
||||||
|
(test v1.1-required-data-types
|
||||||
|
"OSC data encoding test. All required types for v1.1"
|
||||||
|
(is (equalp
|
||||||
|
#(44 105 104 115 102 100 98 0)
|
||||||
|
(osc::encode-typetags '(3
|
||||||
|
4294967297
|
||||||
|
"test"
|
||||||
|
2.1e2
|
||||||
|
2.1d23
|
||||||
|
#(1 2 3 4)
|
||||||
|
;; (osc::encode-timetag :now)
|
||||||
|
)))))
|
||||||
|
|
||||||
|
(test v1.1-keyword-typetags
|
||||||
|
"OSC typetag encoding test."
|
||||||
|
(is (equalp
|
||||||
|
(osc::encode-typetags '(:true :false :null :impulse))
|
||||||
|
#(44 84 70 78 73 0 0 0))))
|
||||||
|
|
||||||
|
;; (osc::encode-typetags '("s" 1))
|
||||||
|
|
||||||
|
|
||||||
;; play nicely with others
|
;; play nicely with others
|
||||||
(in-suite interoperability)
|
(in-suite interoperability)
|
||||||
|
|
||||||
|
|
74
osc.lisp
74
osc.lisp
|
@ -82,29 +82,43 @@
|
||||||
|
|
||||||
(defun encode-typetags (data)
|
(defun encode-typetags (data)
|
||||||
"Create a typetag string suitable for the given DATA.
|
"Create a typetag string suitable for the given DATA.
|
||||||
valid typetags according to the OSC spec are ,i ,f ,s and ,b
|
valid typetags according to the OSC 1.0 spec are ,i ,f ,s and ,b
|
||||||
non-std extensions include ,{h|t|d|S|c|r|m|T|F|N|I|[|]}
|
the OSC 1.1 spec includes ,h ,t ,d ,S ,T ,F ,N and ,I
|
||||||
see the spec for more details. ..
|
|
||||||
|
|
||||||
NOTE: currently handles the following tags
|
The following tags are written based on type check
|
||||||
i => #(105) => int32
|
integer => i => #(105)
|
||||||
f => #(102) => float32
|
=> h => #(104)
|
||||||
s => #(115) => string
|
single-float => f => #(102)
|
||||||
b => #(98) => blob
|
double-float => d => #(100)
|
||||||
h => #(104) => int64
|
simple-string => s => #(115)
|
||||||
and considers non int/float/string data to be a blob."
|
* => b => #(98)
|
||||||
|
|
||||||
|
The following tags are written based on :keywords in the data
|
||||||
|
:true (or t) => T => #(84)
|
||||||
|
:false => F => #(70)
|
||||||
|
:null => N => #(78)
|
||||||
|
:impulse => I => #(73)
|
||||||
|
"
|
||||||
(let ((lump (make-array 0 :adjustable t
|
(let ((lump (make-array 0 :adjustable t
|
||||||
:fill-pointer t)))
|
:fill-pointer t)))
|
||||||
(macrolet ((write-to-vector (char)
|
(macrolet ((write-to-vector (char)
|
||||||
`(vector-push-extend
|
`(vector-push-extend
|
||||||
(char-code ,char) lump)))
|
(char-code ,char) lump)))
|
||||||
(write-to-vector #\,)
|
(write-to-vector #\,) ;; #(44)
|
||||||
(dolist (x data)
|
(dolist (x data)
|
||||||
(typecase x
|
(typecase x
|
||||||
(integer (if (>= x 4294967296) (write-to-vector #\h) (write-to-vector #\i)))
|
(integer (if (>= x 4294967296) (write-to-vector #\h) (write-to-vector #\i)))
|
||||||
(float (write-to-vector #\f))
|
(single-float (write-to-vector #\f))
|
||||||
|
(double-float (write-to-vector #\d))
|
||||||
(simple-string (write-to-vector #\s))
|
(simple-string (write-to-vector #\s))
|
||||||
|
;; lisp semantics vs. OSC semantics
|
||||||
|
(keyword (case x
|
||||||
|
(:true (write-to-vector #\T))
|
||||||
|
(:false (write-to-vector #\F))
|
||||||
|
(:null (write-to-vector #\N))
|
||||||
|
(:impulse (write-to-vector #\I))))
|
||||||
|
(null ('false (write-to-vector #\F)))
|
||||||
|
;; anything else is treated as a blob
|
||||||
(t (write-to-vector #\b)))))
|
(t (write-to-vector #\b)))))
|
||||||
(cat lump
|
(cat lump
|
||||||
(pad (padding-length (length lump))))))
|
(pad (padding-length (length lump))))))
|
||||||
|
@ -117,8 +131,10 @@
|
||||||
(dolist (x data)
|
(dolist (x data)
|
||||||
(typecase x
|
(typecase x
|
||||||
(integer (if (>= x 4294967296) (enc encode-int64) (enc encode-int32)))
|
(integer (if (>= x 4294967296) (enc encode-int64) (enc encode-int32)))
|
||||||
(float (enc encode-float32))
|
(single-float (enc encode-float32))
|
||||||
|
(double-float (enc encode-float64))
|
||||||
(simple-string (enc encode-string))
|
(simple-string (enc encode-string))
|
||||||
|
;; -> timetag
|
||||||
(t (enc encode-blob))))
|
(t (enc encode-blob))))
|
||||||
lump)))
|
lump)))
|
||||||
|
|
||||||
|
@ -324,24 +340,41 @@
|
||||||
;; floats are encoded using ieee-floats library for brevity and compatibility
|
;; floats are encoded using ieee-floats library for brevity and compatibility
|
||||||
;; - https://ieee-floats.common-lisp.dev/
|
;; - https://ieee-floats.common-lisp.dev/
|
||||||
;;
|
;;
|
||||||
;; implementation specific encoding can be used for sbc, cmucl,
|
;; It should be possible to use 32 and 64 bit floats in most common lisp environments.
|
||||||
;; allegro or ccl if required (see README)
|
;; An implementation specific encoder/decoder is used where available.
|
||||||
|
|
||||||
(defun encode-float32 (f)
|
(defun encode-float32 (f)
|
||||||
"Encode an ieee754 float as a 4 byte vector. currently sbcl/cmucl specific."
|
"Encode an ieee754 float as a 4 byte vector. currently sbcl/cmucl specific."
|
||||||
(encode-int32 (ieee-floats:encode-float32 f)))
|
#+sbcl (encode-int32 (sb-kernel:single-float-bits f))
|
||||||
|
#+cmucl (encode-int32 (kernel:single-float-bits f))
|
||||||
|
#+openmcl (encode-int32 (CCL::SINGLE-FLOAT-BITS f))
|
||||||
|
#+allegro (encode-int32 (multiple-value-bind (x y)
|
||||||
|
(excl:single-float-to-shorts f)
|
||||||
|
(+ (ash x 16) y)))
|
||||||
|
#-(or sbcl cmucl openmcl allegro) (encode-int32 (ieee-floats:encode-float32 f)))
|
||||||
|
|
||||||
(defun decode-float32 (v)
|
(defun decode-float32 (v)
|
||||||
"Convert a vector of 4 bytes in network byte order into an ieee754 float."
|
"Convert a vector of 4 bytes in network byte order into an ieee754 float."
|
||||||
(ieee-floats:decode-float32 (decode-uint32 v)))
|
#+sbcl (sb-kernel:make-single-float (decode-uint32 v))
|
||||||
|
#+cmucl (kernel:make-single-float (decode-int32 v))
|
||||||
|
#+openmcl (CCL::HOST-SINGLE-FLOAT-FROM-UNSIGNED-BYTE-32 (decode-uint32 v))
|
||||||
|
#+allegro (excl:shorts-to-single-float (ldb (byte 16 16) (decode-uint32 v))
|
||||||
|
(ldb (byte 16 0) (decode-uint32 v)))
|
||||||
|
#-(or sbcl cmucl openmcl allegro) (ieee-floats:decode-float32 (decode-uint32 v)))
|
||||||
|
|
||||||
|
|
||||||
(defun encode-float64 (d)
|
(defun encode-float64 (d)
|
||||||
"Encode an ieee754 float as a 8 byte vector."
|
"Encode an ieee754 float as a 8 byte vector."
|
||||||
(encode-int64 (ieee-floats:encode-float64 d)))
|
#+sbcl (cat (encode-int32 (sb-kernel:double-float-high-bits d))
|
||||||
|
(encode-int32 (sb-kernel:double-float-low-bits d)))
|
||||||
|
#-sbcl (encode-int64 (ieee-floats:encode-float64 d)))
|
||||||
|
|
||||||
(defun decode-float64 (v)
|
(defun decode-float64 (v)
|
||||||
"Convert a vector of 8 bytes in network byte order into an ieee754 float."
|
"Convert a vector of 8 bytes in network byte order into an ieee754 float."
|
||||||
(ieee-floats:decode-float64 (decode-int64 v)))
|
#+sbcl (sb-kernel:make-double-float
|
||||||
|
(decode-uint32 (subseq v 0 4))
|
||||||
|
(decode-uint32 (subseq v 4 8)))
|
||||||
|
#-sbcl (ieee-floats:decode-float64 (decode-uint64 v)))
|
||||||
|
|
||||||
;; osc-strings are unsigned bytes, padded to a 4 byte boundary
|
;; osc-strings are unsigned bytes, padded to a 4 byte boundary
|
||||||
|
|
||||||
|
@ -355,7 +388,7 @@
|
||||||
(string-padding string)))
|
(string-padding string)))
|
||||||
|
|
||||||
;; blobs are binary data, consisting of a length (int32) and bytes which are
|
;; blobs are binary data, consisting of a length (int32) and bytes which are
|
||||||
;; osc-padded to a 4 byte boundary.
|
;; padded to a 4 byte boundary.
|
||||||
|
|
||||||
(defun decode-blob (blob)
|
(defun decode-blob (blob)
|
||||||
"Decode a BLOB as a vector of unsigned bytes."
|
"Decode a BLOB as a vector of unsigned bytes."
|
||||||
|
@ -371,6 +404,7 @@
|
||||||
;; NOTE: cannot use (padding-length bl), as it is not the same algorithm. Blobs of 4, 8, 12 etc bytes should not be padded!
|
;; NOTE: cannot use (padding-length bl), as it is not the same algorithm. Blobs of 4, 8, 12 etc bytes should not be padded!
|
||||||
|
|
||||||
;; utility functions for osc-string/padding/slonking
|
;; utility functions for osc-string/padding/slonking
|
||||||
|
;; NOTE: string padding is treated differently between v1.0 and v1.1
|
||||||
|
|
||||||
(defun cat (&rest catatac)
|
(defun cat (&rest catatac)
|
||||||
"Concatenate items into a byte vector."
|
"Concatenate items into a byte vector."
|
||||||
|
|
Loading…
Reference in a new issue