2005-11-29 11:11:26 +00:00
|
|
|
;;; -*- mode: lisp -*-
|
|
|
|
;;;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; An implementation of the OSC (Open Sound Control) protocol
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; Copyright (c) 2004 FoAM
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; cl-osc is free software: you can redistribute it and/or modify it
|
|
|
|
;;; under the terms of the GNU General Public License as published by
|
|
|
|
;;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
;;; (at your option) any later version.
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; Authors
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; nik gaffney <nik@fo.am> and the listed AUTHORS
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; Requirements
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; depends on ieee-floats for float encoding and 5am for testing
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; Commentary
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; This is an implementation of the OSC protocol which is used
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; for communication mostly amongst music programs and their attached
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; musicians (eg. supercollider, max/pd, ableton, etc).
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; The OSC V1.0 is supported, and there is partial support for V1.1
|
|
|
|
;;; More details of the protocol can be found at
|
|
|
|
;;; http://OpenSoundControl.org
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; see the README file for further details...
|
2006-02-10 23:38:40 +00:00
|
|
|
;;;
|
2023-12-31 16:33:12 +00:00
|
|
|
;;; Known BUGS/Issues
|
2023-12-29 11:12:38 +00:00
|
|
|
;;; - encoding a :symbol that is unbound or without symbol-value causes an error
|
|
|
|
;;; - unknown types are sent as 'blobs' which may or may not be an issue
|
|
|
|
;;; - malformed input -> exception
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defpackage :osc
|
|
|
|
(:use :cl)
|
2023-12-31 16:33:12 +00:00
|
|
|
(:shadow :ieee-floats)
|
|
|
|
(:documentation "OSC the 'Open Sound Control' protocol")
|
2023-12-29 11:12:38 +00:00
|
|
|
(:export
|
|
|
|
#:encode-message
|
|
|
|
#:encode-bundle
|
|
|
|
#:decode-message
|
|
|
|
#:decode-bundle))
|
2005-02-10 13:32:51 +00:00
|
|
|
|
|
|
|
(in-package :osc)
|
2023-12-29 11:12:38 +00:00
|
|
|
;; (declaim (optimize (speed 3) (safety 1) (debug 3)))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
|
|
|
;;;;;; ; ;; ; ; ; ; ; ; ;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;
|
2005-01-26 13:52:24 +00:00
|
|
|
;; eNcoding OSC messages
|
|
|
|
;;
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;; ;; ;; ; ; ;; ; ; ; ;
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2006-02-10 23:38:40 +00:00
|
|
|
(defun encode-bundle (data &optional timetag)
|
2005-03-14 14:05:05 +00:00
|
|
|
"will encode an osc message, or list of messages as a bundle
|
2006-02-10 23:38:40 +00:00
|
|
|
with an optional timetag (symbol or 64bit int).
|
|
|
|
doesnt handle nested bundles"
|
2005-03-14 14:05:05 +00:00
|
|
|
(cat '(35 98 117 110 100 108 101 0) ; #bundle
|
2006-02-10 23:38:40 +00:00
|
|
|
(if timetag
|
|
|
|
(encode-timetag timetag)
|
|
|
|
(encode-timetag :now))
|
2005-03-14 14:05:05 +00:00
|
|
|
(if (listp (car data))
|
2023-12-29 11:12:38 +00:00
|
|
|
(apply #'cat (mapcar #'encode-bundle-elt data))
|
|
|
|
(encode-bundle-elt data))))
|
2005-03-14 14:05:05 +00:00
|
|
|
|
|
|
|
(defun encode-bundle-elt (data)
|
|
|
|
(let ((message (apply #'encode-message data)))
|
2023-12-29 11:12:38 +00:00
|
|
|
(cat (encode-int32 (length message)) message)))
|
2005-03-14 14:05:05 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun encode-message (address &rest data)
|
2005-01-26 13:52:24 +00:00
|
|
|
"encodes an osc message with the given address and data."
|
2005-03-06 12:25:01 +00:00
|
|
|
(concatenate '(vector (unsigned-byte 8))
|
2023-12-29 11:12:38 +00:00
|
|
|
(encode-address address)
|
|
|
|
(encode-typetags data)
|
|
|
|
(encode-data data)))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun encode-address (address)
|
2023-12-29 11:12:38 +00:00
|
|
|
(cat (map 'vector #'char-code address)
|
2005-03-14 14:05:05 +00:00
|
|
|
(string-padding address)))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun encode-typetags (data)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Create a typetag string suitable for the given DATA.
|
2024-01-01 23:38:25 +00:00
|
|
|
valid typetags according to the OSC 1.0 spec are ,i ,f ,s and ,b
|
|
|
|
the OSC 1.1 spec includes ,h ,t ,d ,S ,T ,F ,N and ,I
|
|
|
|
|
|
|
|
The following tags are written based on type check
|
|
|
|
integer => i => #(105)
|
|
|
|
=> h => #(104)
|
|
|
|
single-float => f => #(102)
|
|
|
|
double-float => d => #(100)
|
|
|
|
simple-string => s => #(115)
|
|
|
|
* => 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)
|
|
|
|
"
|
2023-12-29 11:12:38 +00:00
|
|
|
(let ((lump (make-array 0 :adjustable t
|
|
|
|
:fill-pointer t)))
|
2005-03-02 17:23:56 +00:00
|
|
|
(macrolet ((write-to-vector (char)
|
|
|
|
`(vector-push-extend
|
|
|
|
(char-code ,char) lump)))
|
2024-01-01 23:38:25 +00:00
|
|
|
(write-to-vector #\,) ;; #(44)
|
2023-12-29 11:12:38 +00:00
|
|
|
(dolist (x data)
|
2005-03-02 17:23:56 +00:00
|
|
|
(typecase x
|
2015-08-25 19:54:38 +00:00
|
|
|
(integer (if (>= x 4294967296) (write-to-vector #\h) (write-to-vector #\i)))
|
2024-01-01 23:38:25 +00:00
|
|
|
(single-float (write-to-vector #\f))
|
|
|
|
(double-float (write-to-vector #\d))
|
2005-03-02 17:23:56 +00:00
|
|
|
(simple-string (write-to-vector #\s))
|
2024-01-01 23:38:25 +00:00
|
|
|
;; 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))))
|
2024-01-01 23:55:51 +00:00
|
|
|
(null (write-to-vector #\F))
|
2024-01-01 23:38:25 +00:00
|
|
|
;; anything else is treated as a blob
|
2023-12-29 11:12:38 +00:00
|
|
|
(t (write-to-vector #\b)))))
|
2005-11-29 11:11:26 +00:00
|
|
|
(cat lump
|
2023-12-29 11:12:38 +00:00
|
|
|
(pad (padding-length (length lump))))))
|
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun encode-data (data)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Encode DATA in a format suitable for an OSC message."
|
2005-01-26 13:52:24 +00:00
|
|
|
(let ((lump (make-array 0 :adjustable t :fill-pointer t)))
|
2005-03-02 17:23:56 +00:00
|
|
|
(macrolet ((enc (f)
|
|
|
|
`(setf lump (cat lump (,f x)))))
|
2023-12-29 11:12:38 +00:00
|
|
|
(dolist (x data)
|
2005-03-02 17:23:56 +00:00
|
|
|
(typecase x
|
2015-08-25 19:54:38 +00:00
|
|
|
(integer (if (>= x 4294967296) (enc encode-int64) (enc encode-int32)))
|
2024-01-01 23:38:25 +00:00
|
|
|
(single-float (enc encode-float32))
|
|
|
|
(double-float (enc encode-float64))
|
2005-03-14 14:05:05 +00:00
|
|
|
(simple-string (enc encode-string))
|
2024-01-01 23:38:25 +00:00
|
|
|
;; -> timetag
|
2023-12-29 11:12:38 +00:00
|
|
|
(t (enc encode-blob))))
|
2005-03-02 17:23:56 +00:00
|
|
|
lump)))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2023-12-29 11:12:38 +00:00
|
|
|
|
2005-01-26 13:52:24 +00:00
|
|
|
;;;;;; ; ;; ; ; ; ; ; ; ;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;
|
2005-01-26 13:52:24 +00:00
|
|
|
;; decoding OSC messages
|
|
|
|
;;
|
|
|
|
;;; ;; ;; ; ; ; ; ; ;
|
|
|
|
|
2005-03-14 14:05:05 +00:00
|
|
|
(defun decode-bundle (data)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Decode an OSC bundle into a list of decoded-messages.
|
|
|
|
The first element is an osc-timetag."
|
2005-03-14 14:05:05 +00:00
|
|
|
(let ((contents '()))
|
2023-12-29 11:12:38 +00:00
|
|
|
(if (equalp 35 (elt data 0)) ;; a bundle begins with '#'
|
|
|
|
(let ((timetag (subseq data 8 16))
|
|
|
|
(i 16)
|
|
|
|
(bundle-length (length data)))
|
|
|
|
(loop while (< i bundle-length)
|
|
|
|
do (let ((mark (+ i 4))
|
|
|
|
(size (decode-int32
|
|
|
|
(subseq data i (+ i 4)))))
|
|
|
|
(if (eq size 0)
|
|
|
|
(setf bundle-length 0)
|
|
|
|
(push (decode-bundle
|
|
|
|
(subseq data mark (+ mark size)))
|
|
|
|
contents))
|
|
|
|
(incf i (+ 4 size))))
|
|
|
|
(push timetag contents))
|
|
|
|
(decode-message data))))
|
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun decode-message (message)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Reduce an OSC MESSAGE to an (address . data) pair."
|
2005-03-06 12:25:01 +00:00
|
|
|
(declare (type (vector *) message))
|
2005-01-26 13:52:24 +00:00
|
|
|
(let ((x (position (char-code #\,) message)))
|
2005-03-02 17:23:56 +00:00
|
|
|
(if (eq x NIL)
|
2023-12-29 11:12:38 +00:00
|
|
|
(format t "Message contains no data.. ")
|
|
|
|
(cons (decode-address (subseq message 0 x))
|
|
|
|
(decode-taged-data (subseq message x))))))
|
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun decode-address (address)
|
2023-12-29 11:12:38 +00:00
|
|
|
(coerce (map 'vector #'code-char
|
|
|
|
(delete 0 address))
|
|
|
|
'string))
|
2005-03-14 14:05:05 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun decode-taged-data (data)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Decode DATA encoded with typetags.
|
|
|
|
NOTE: currently handles the following tags
|
2005-01-26 13:52:24 +00:00
|
|
|
i => #(105) => int32
|
2023-12-31 16:33:12 +00:00
|
|
|
f => #(102) => float32
|
2005-03-14 14:05:05 +00:00
|
|
|
s => #(115) => string
|
2015-08-25 19:54:38 +00:00
|
|
|
b => #(98) => blob
|
|
|
|
h => #(104) => int64"
|
2005-03-14 14:05:05 +00:00
|
|
|
|
|
|
|
(let ((div (position 0 data)))
|
2023-12-29 11:12:38 +00:00
|
|
|
(let ((tags (subseq data 1 div))
|
2015-08-25 19:59:32 +00:00
|
|
|
(acc (subseq data (padded-length div)))
|
|
|
|
(result '()))
|
2005-03-14 14:05:05 +00:00
|
|
|
(map 'vector
|
2015-08-25 19:59:32 +00:00
|
|
|
#'(lambda (x)
|
|
|
|
(cond
|
2023-12-29 11:12:38 +00:00
|
|
|
((eq x (char-code #\i))
|
|
|
|
(push (decode-int32 (subseq acc 0 4))
|
|
|
|
result)
|
|
|
|
(setf acc (subseq acc 4)))
|
|
|
|
((eq x (char-code #\h))
|
|
|
|
(push (decode-uint64 (subseq acc 0 8))
|
|
|
|
result)
|
|
|
|
(setf acc (subseq acc 8)))
|
|
|
|
((eq x (char-code #\f))
|
|
|
|
(push (decode-float32 (subseq acc 0 4))
|
|
|
|
result)
|
|
|
|
(setf acc (subseq acc 4)))
|
|
|
|
((eq x (char-code #\s))
|
|
|
|
(let ((pointer (padded-length (position 0 acc))))
|
|
|
|
(push (decode-string
|
|
|
|
(subseq acc 0 pointer))
|
|
|
|
result)
|
|
|
|
(setf acc (subseq acc pointer))))
|
|
|
|
((eq x (char-code #\b))
|
|
|
|
(let* ((size (decode-int32 (subseq acc 0 4)))
|
|
|
|
(bl (+ 4 size))
|
|
|
|
(end (+ bl (mod (- 4 bl) 4))))
|
|
|
|
;; NOTE: cannot use (padded-length bl), as it is not the same algorithm. Blobs of 4, 8, 12 etc bytes should not be padded!
|
|
|
|
(push (decode-blob (subseq acc 0 end))
|
|
|
|
result)
|
|
|
|
(setf acc (subseq acc end))))
|
|
|
|
(t (error "unrecognised typetag ~a" x))))
|
2015-08-25 19:59:32 +00:00
|
|
|
tags)
|
2005-03-14 14:05:05 +00:00
|
|
|
(nreverse result))))
|
|
|
|
|
2005-11-29 11:11:26 +00:00
|
|
|
;;;;;; ;; ;; ; ; ; ; ; ;; ;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;
|
|
|
|
;; Timetags
|
2005-03-14 14:05:05 +00:00
|
|
|
;;
|
2006-02-10 23:38:40 +00:00
|
|
|
;; - timetags can be encoded using a value, or the :now and :time keywords. the
|
|
|
|
;; keywords enable either a tag indicating 'immediate' execution, or
|
|
|
|
;; a tag containing the current time (which will most likely be in the past
|
2023-12-29 11:12:38 +00:00
|
|
|
;; of any receiver) to be created.
|
2005-03-14 14:05:05 +00:00
|
|
|
;;
|
2006-02-10 23:38:40 +00:00
|
|
|
;; - note: not well tested, and probably not accurate enough for syncronisation.
|
2023-12-29 11:12:38 +00:00
|
|
|
;; see also: CLHS 25.1.4 Time, and the NTP timestamp format. also needs to
|
2006-02-10 23:38:40 +00:00
|
|
|
;; convert from 2 32bit ints to 64bit fixed point value.
|
2005-03-14 14:05:05 +00:00
|
|
|
;;
|
2007-02-26 15:20:53 +00:00
|
|
|
;; - see this c.l.l thread to sync universal-time and internal-time
|
|
|
|
;; http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/c207fef63a78d720/adc7442d2e4de5a0?lnk=gst&q=internal-real-time-sync&rnum=1#adc7442d2e4de5a0
|
|
|
|
;;
|
2023-12-29 11:12:38 +00:00
|
|
|
;;;; ;; ; ;
|
2005-03-14 14:05:05 +00:00
|
|
|
|
|
|
|
(defconstant +unix-epoch+ (encode-universal-time 0 0 0 1 1 1970 0))
|
|
|
|
|
2006-02-10 23:38:40 +00:00
|
|
|
(defun encode-timetag (utime &optional subseconds)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Encode an OSC timetag from a universal-time and 32bit 'sub-second' part.
|
|
|
|
for an 'instantaneous' timetag use (encode-timetag :now)
|
2006-02-10 23:38:40 +00:00
|
|
|
for a timetag with the current time use (encode-timetag :time)"
|
|
|
|
(cond
|
2023-12-29 11:12:38 +00:00
|
|
|
;; a timetag of 1 will be interpreted as 'immediately'
|
2006-02-10 23:38:40 +00:00
|
|
|
((equalp utime :now)
|
2023-12-29 11:12:38 +00:00
|
|
|
#(0 0 0 0 0 0 0 1))
|
2006-02-10 23:38:40 +00:00
|
|
|
;; converts seconds since 19000101 to seconds since 19700101
|
2023-12-29 11:12:38 +00:00
|
|
|
;; note: fractions of seconds are accurate, but not synchronised.
|
2006-02-10 23:38:40 +00:00
|
|
|
((equalp utime :time)
|
|
|
|
(cat (encode-int32 (- (get-universal-time) +unix-epoch+))
|
2023-12-29 11:12:38 +00:00
|
|
|
(encode-int32
|
2006-02-10 23:44:10 +00:00
|
|
|
(round (* internal-time-units-per-second
|
2023-12-29 11:12:38 +00:00
|
|
|
(second (multiple-value-list
|
|
|
|
(floor (/ (get-internal-real-time)
|
2006-02-10 23:44:10 +00:00
|
|
|
internal-time-units-per-second)))))))))
|
2006-02-10 23:38:40 +00:00
|
|
|
((integerp utime)
|
|
|
|
(cat (encode-int32 (+ utime +unix-epoch+))
|
|
|
|
(encode-int32 subseconds)))
|
2023-12-29 11:12:38 +00:00
|
|
|
(t (error "The time or subsecond given is not an integer."))))
|
2005-03-14 14:05:05 +00:00
|
|
|
|
|
|
|
(defun decode-timetag (timetag)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Decompose a TIMETAG into unix-time and subsecond."
|
2005-03-14 14:05:05 +00:00
|
|
|
(list
|
|
|
|
(decode-int32 (subseq timetag 0 4))
|
|
|
|
(decode-int32 (subseq timetag 4 8))))
|
|
|
|
|
2007-02-26 15:20:53 +00:00
|
|
|
|
2005-03-02 17:23:56 +00:00
|
|
|
;;;;; ; ; ;; ;; ; ;
|
|
|
|
;;
|
2005-01-26 13:52:24 +00:00
|
|
|
;; dataformat en- de- cetera.
|
2005-03-02 17:23:56 +00:00
|
|
|
;;
|
|
|
|
;;; ;; ; ; ;
|
2005-11-29 11:11:26 +00:00
|
|
|
|
2023-12-31 16:33:12 +00:00
|
|
|
;; integers. 32 and 64 bit. signed and unsigned.
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2019-03-24 21:43:20 +00:00
|
|
|
(defmacro defint-decoder (num-of-octets &optional docstring)
|
|
|
|
(let ((decoder-name (intern (format nil "~:@(decode-uint~)~D" (* 8 num-of-octets))))
|
|
|
|
(seq (gensym))
|
|
|
|
(int (gensym)))
|
|
|
|
`(defun ,decoder-name (,seq)
|
|
|
|
,@(when docstring
|
|
|
|
(list docstring))
|
|
|
|
(let* ((,int 0)
|
|
|
|
,@(loop
|
|
|
|
for n below num-of-octets
|
2023-12-31 16:33:12 +00:00
|
|
|
collect `(,int
|
|
|
|
(dpb (aref ,seq ,n)
|
|
|
|
(byte 8 (* 8 (- (1- ,num-of-octets) ,n)))
|
|
|
|
,int))))
|
2019-05-02 04:34:42 +00:00
|
|
|
,int))))
|
2005-12-05 19:07:01 +00:00
|
|
|
|
2019-03-24 21:00:00 +00:00
|
|
|
(defmacro defint-encoder (num-of-octets &optional docstring)
|
|
|
|
(let ((enc-name (intern (format nil "~:@(encode-int~)~D" (* 8 num-of-octets))))
|
|
|
|
(buf (gensym))
|
|
|
|
(int (gensym)))
|
|
|
|
`(defun ,enc-name (,int)
|
|
|
|
,@(when docstring
|
|
|
|
(list docstring))
|
|
|
|
(let ((,buf (make-array ,num-of-octets :element-type '(unsigned-byte 8))))
|
|
|
|
,@(loop
|
|
|
|
for n below num-of-octets
|
|
|
|
collect `(setf (aref ,buf ,n)
|
|
|
|
(ldb (byte 8 (* 8 (- (1- ,num-of-octets) ,n)))
|
|
|
|
,int)))
|
|
|
|
,buf))))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2023-12-31 16:33:12 +00:00
|
|
|
;; generate functions decode-uint32 and decode-uint64
|
|
|
|
(defint-decoder 4 "4 byte -> 32 bit unsigned int")
|
|
|
|
(defint-decoder 8 "8 byte -> 64 bit unsigned int")
|
|
|
|
|
|
|
|
;; generate functions encode-int32 and encode-int64
|
2019-03-24 21:00:00 +00:00
|
|
|
(defint-encoder 4 "Convert an integer into a sequence of 4 bytes in network byte order.")
|
|
|
|
(defint-encoder 8 "Convert an integer into a sequence of 8 bytes in network byte order.")
|
2015-08-25 19:54:38 +00:00
|
|
|
|
2019-03-24 21:14:43 +00:00
|
|
|
(defun decode-int32 (s)
|
|
|
|
"4 byte -> 32 bit int -> two's complement (in network byte order)"
|
|
|
|
(let ((i (decode-uint32 s)))
|
2019-03-24 21:57:45 +00:00
|
|
|
(if (>= i #.(1- (expt 2 31)))
|
|
|
|
(- (- #.(expt 2 32) i))
|
2023-12-29 11:12:38 +00:00
|
|
|
i)))
|
2015-08-25 19:54:38 +00:00
|
|
|
|
|
|
|
(defun decode-int64 (s)
|
2019-03-24 21:14:43 +00:00
|
|
|
"8 byte -> 64 bit int -> two's complement (in network byte order)"
|
|
|
|
(let ((i (decode-uint64 s)))
|
2019-03-24 21:57:45 +00:00
|
|
|
(if (>= i #.(1- (expt 2 63)))
|
|
|
|
(- (- #.(expt 2 64) i))
|
2023-12-29 11:12:38 +00:00
|
|
|
i)))
|
2015-08-25 19:54:38 +00:00
|
|
|
|
2023-12-31 16:33:12 +00:00
|
|
|
;; floats are encoded using ieee-floats library for brevity and compatibility
|
|
|
|
;; - https://ieee-floats.common-lisp.dev/
|
|
|
|
;;
|
2024-01-01 23:38:25 +00:00
|
|
|
;; It should be possible to use 32 and 64 bit floats in most common lisp environments.
|
2024-01-02 12:14:13 +00:00
|
|
|
;; An implementation specific encoder/decoder can be used where available.
|
|
|
|
|
|
|
|
(declaim (inline ieee-floats:encode-float32
|
|
|
|
ieee-floats:decode-float32
|
|
|
|
ieee-floats:encode-float64
|
|
|
|
ieee-floats:decode-float64))
|
|
|
|
|
|
|
|
(ieee-floats:make-float-converters ieee-floats:encode-float32
|
|
|
|
ieee-floats:decode-float32 8 23 t)
|
|
|
|
|
|
|
|
(ieee-floats:make-float-converters ieee-floats:encode-float64
|
|
|
|
ieee-floats:decode-float64 11 52 t)
|
2023-12-31 16:33:12 +00:00
|
|
|
|
|
|
|
(defun encode-float32 (f)
|
2024-01-02 12:14:13 +00:00
|
|
|
"Encode an ieee754 float as a 4 byte vector."
|
2024-01-01 23:38:25 +00:00
|
|
|
#+sbcl (encode-int32 (sb-kernel:single-float-bits f))
|
2024-01-02 12:14:13 +00:00
|
|
|
(encode-int32 (ieee-floats:encode-float32 f)))
|
2023-12-31 16:33:12 +00:00
|
|
|
|
|
|
|
(defun decode-float32 (v)
|
|
|
|
"Convert a vector of 4 bytes in network byte order into an ieee754 float."
|
2024-01-02 12:14:13 +00:00
|
|
|
(ieee-floats:decode-float32 (decode-uint32 v)))
|
2023-12-31 16:33:12 +00:00
|
|
|
|
|
|
|
(defun encode-float64 (d)
|
|
|
|
"Encode an ieee754 float as a 8 byte vector."
|
2024-01-02 12:14:13 +00:00
|
|
|
(encode-int64 (ieee-floats:encode-float64 d)))
|
2023-12-31 16:33:12 +00:00
|
|
|
|
|
|
|
(defun decode-float64 (v)
|
|
|
|
"Convert a vector of 8 bytes in network byte order into an ieee754 float."
|
2024-01-02 12:14:13 +00:00
|
|
|
(ieee-floats:decode-float64 (decode-uint64 v)))
|
2023-12-31 16:33:12 +00:00
|
|
|
|
2023-12-29 11:12:38 +00:00
|
|
|
;; osc-strings are unsigned bytes, padded to a 4 byte boundary
|
2005-03-06 12:25:01 +00:00
|
|
|
|
2005-01-26 13:52:24 +00:00
|
|
|
(defun decode-string (data)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Convert a binary vector to a string and remove any trailing #\nul characters."
|
2005-01-26 13:52:24 +00:00
|
|
|
(string-trim '(#\nul) (coerce (map 'vector #'code-char data) 'string)))
|
|
|
|
|
|
|
|
(defun encode-string (string)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Encode a STRING as a vector of character-codes padded to 4 byte boundary."
|
|
|
|
(cat (map 'vector #'char-code string)
|
2005-03-14 14:05:05 +00:00
|
|
|
(string-padding string)))
|
|
|
|
|
|
|
|
;; blobs are binary data, consisting of a length (int32) and bytes which are
|
2024-01-01 23:38:25 +00:00
|
|
|
;; padded to a 4 byte boundary.
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-03-14 14:05:05 +00:00
|
|
|
(defun decode-blob (blob)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Decode a BLOB as a vector of unsigned bytes."
|
2005-03-14 14:05:05 +00:00
|
|
|
(let ((size (decode-int32
|
2023-12-29 11:12:38 +00:00
|
|
|
(subseq blob 0 4))))
|
|
|
|
(subseq blob 4 (+ 4 size))))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-03-14 14:05:05 +00:00
|
|
|
(defun encode-blob (blob)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Encode BLOB as a vector."
|
2005-03-14 14:05:05 +00:00
|
|
|
(let ((bl (length blob)))
|
|
|
|
(cat (encode-int32 bl) blob
|
2023-12-29 11:12:38 +00:00
|
|
|
(pad (mod (- 4 bl) 4)))))
|
|
|
|
;; NOTE: cannot use (padding-length bl), as it is not the same algorithm. Blobs of 4, 8, 12 etc bytes should not be padded!
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2023-12-29 11:12:38 +00:00
|
|
|
;; utility functions for osc-string/padding/slonking
|
2024-01-01 23:38:25 +00:00
|
|
|
;; NOTE: string padding is treated differently between v1.0 and v1.1
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2024-01-02 12:14:13 +00:00
|
|
|
(defun write-data-as-hex (data)
|
|
|
|
"Write OSC data (represented as vector) as string in base 16."
|
|
|
|
(write-to-string data :base 16))
|
|
|
|
|
2005-01-26 14:36:20 +00:00
|
|
|
(defun cat (&rest catatac)
|
2023-12-31 16:33:12 +00:00
|
|
|
"Concatenate items into a byte vector."
|
|
|
|
(apply #'concatenate '(vector (unsigned-byte 8)) catatac))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun padding-length (s)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Return the length of padding required for a given length of string."
|
2005-03-06 12:25:01 +00:00
|
|
|
(declare (type fixnum s))
|
2005-01-26 13:52:24 +00:00
|
|
|
(- 4 (mod s 4)))
|
|
|
|
|
2005-03-14 14:05:05 +00:00
|
|
|
(defun padded-length (s)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Return the length of an osc-string made from a given length of string."
|
2005-03-14 14:05:05 +00:00
|
|
|
(declare (type fixnum s))
|
|
|
|
(+ s (- 4 (mod s 4))))
|
|
|
|
|
|
|
|
(defun string-padding (string)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Return the padding required for a given osc string."
|
|
|
|
(declare (type simple-string string))
|
2005-11-29 11:11:26 +00:00
|
|
|
(pad (padding-length (length string))))
|
2005-01-26 13:52:24 +00:00
|
|
|
|
2005-02-10 13:32:51 +00:00
|
|
|
(defun pad (n)
|
2023-12-29 11:12:38 +00:00
|
|
|
"Make a sequence of the required number of #\Nul characters."
|
2005-03-06 12:25:01 +00:00
|
|
|
(declare (type fixnum n))
|
2005-01-26 13:52:24 +00:00
|
|
|
(make-array n :initial-element 0 :fill-pointer n))
|
|
|
|
|
2006-04-05 10:33:33 +00:00
|
|
|
(provide :osc)
|
2023-12-29 11:12:38 +00:00
|
|
|
|
2005-03-02 17:23:56 +00:00
|
|
|
;; end
|