Work-in-progress repo for ambisonics extensions for OM-SoX
Marlon Schumacher
24.02.25 197ce0f724aa567c7401f63dc889e7861ed8c0aa
fix: smaller fixes, comments and cleanups
4 files modified
65 ■■■■ changed files
.gitignore 3 ●●●● patch | view | raw | blame | history
sources/classes/sox-hoaencode.lisp 39 ●●●●● patch | view | raw | blame | history
sources/sox-process.lisp 21 ●●●● patch | view | raw | blame | history
sources/utilities.lisp 2 ●●● patch | view | raw | blame | history
.gitignore
@@ -6,4 +6,5 @@
*.*~
# OM Reference files
reference-pages/
reference-pages/
.vscode
sources/classes/sox-hoaencode.lisp
@@ -4,8 +4,8 @@
; 1) Sounds must have identical sample rate; otherwise, SoX fails silently, literally.
; Questions:
; 1) Is there a built-in (flatten lst)-function?
; 2) Implement up to which order? <=> CLI issues to be expected (e.g., maximum command character length)?
; 1) Is there a built-in (flatten lst)-function? Yes, om:flat
; 2) Implement up to which order? <=> CLI issues to be expected (e.g., maximum command character length)? max character length for CLI should be changeable
(in-package :om)
@@ -34,8 +34,9 @@
    )
)
; Ambisonics
(defun sox-hoaencode-sn3d-factor (order degree)
;  ### Ambisonics ###
(defun sox-hoaencode-sn3d-factor (order degree) ; andere Nomenklatur, "degree" wäre besser "order" um Verwechslung mit Winkeln zu vermeiden
    (ecase order
        (0 1)
        (1 1)
@@ -96,13 +97,14 @@
        )
    ))
; Convenience functions
; #### Utility functions ####
; Ensures that positions are given as '((azimuth elevation) ...) (in degrees), i.e.
; 1) if list of list of three values, input is assumed to be '(x y z) coordinates and will be transformed to '(azimuth elevation) coordinates (in the navigational spherical coordinate system).
; 2) if list of list of two values, input is assumed to be '(azimuth elevation) coordinates and won't be transformed any further.
; 3) if 3dc, input is transformed to '(azimuth elevation) coordinates.
(defun sox-hoaencode-auto-convert-positions (positions)
"Ensures that positions are given as '((azimuth elevation) ...) (in degrees), i.e.
1) if list of list of three values, input is assumed to be '(x y z) coordinates and will be transformed to '(azimuth elevation) coordinates (in the navigational spherical coordinate system).
2) if list of list of two values, input is assumed to be '(azimuth elevation) coordinates and won't be transformed any further.
3) if 3dc, input is transformed to '(azimuth elevation) coordinates.
"
    (cond 
        ((subtypep (type-of positions) '3dc)  
            (progn ; convert xyz->aed, keep azimuth and elevation only.
@@ -129,9 +131,13 @@
            ))
        (t (error "Positions must be of type 3dc or list."))))
; High-level API
; Returns the gain value (linear, -1 to 1) for a single ACN-channel
; ####### High-level API ##########
(defun sox-hoaencode-gain-single-component (order degree azimuth_deg elevation_deg)
  "Returns the gain value (linear, -1 to 1) for a single ACN-channel"
    (let
        (
            ; It is assumed that azimuth_deg follows the implementation details of SpatDIF, 
@@ -146,13 +152,18 @@
            (sox-hoaencode-elevation-factor order degree elevation_deg)))
    )
; Returns the gain values for all components at a specific order
; (sox-hoaencode-gain-single-component 1 1 45 0)
; what is the difference between "order" and "degree"?
(defun sox-hoaencode-gains-by-order (order azimuth_deg elevation_deg)
  "Returns the gain values for all components at a specific order"
    (loop for degree from (* -1 order) to order collect 
        (sox-hoaencode-gain-single-component order degree azimuth_deg elevation_deg)))
; Returns the gain values for all components up to a specific order
(defun sox-hoaencode-gains-up-to-order (order azimuth_deg elevation_deg)
  "Returns the gain values for all components up to a specific order"
    (sox-hoaencode-double-to-float 
        (flatten
            (loop for ord from 0 to order collect 
@@ -176,3 +187,5 @@
        (sox-init-sound self 'list)
    )
)
sources/sox-process.lisp
@@ -181,11 +181,16 @@
; === sox-hoaencode =============
(defmethod! sox-process ((sox-input sox-hoaencode) (sox-effect string) &key output filetype samplerate bitdepth recursive batch-mode)
; would be more consistent with OO programming to have a function sox-hoaencode->sox-merge
; (consider how to deal with multiple sounds to be hoaencoded and mixed later on... sox-mix might be better done in an explicit way rather than via the class directly.
; in that case a 3DC would have to be used by using points (point-pairs) in a loop
    (if (probe-file *sox-path*)
        (progn
            (when 
                (not (listp (sound sox-input)))
                (om-message-abort "sound must be a list.")
                (om-message-abort "sound must be a list.") ; use list! instead
            )
            (when 
                (not 
@@ -236,7 +241,7 @@
                        (if (= (order sox-input) 0)
                            ; case: order = 0
                            (progn 
                                (setf str (string+ str " -m"))
                                (setf str (string+ str " -m")) ; this is the mixer. e.g. sox-mix
                                (loop for filename in filenames do
                                    (setf str (string+ str (format nil " ~s" filename)))
                                )
@@ -250,6 +255,11 @@
                                (loop 
                                    for filename in filenames
                                    for position in positions-ae do
                                                                   ; in Object-Oriented Programming we would call-next-method i.e. set a sox-merge input class with the corresponding params.
                                                                   ; e.g. (make-instance 'sox-merge ... (more redundancy and compactness, less error-prone) ... possible?
                                                                   ; for multiple sound inputs if possible we would then call a sox-mix to mix the sox merge pipes. everything
                                    (progn  
                                        (setf str_component (format nil " ~s ~a -M" (namestring *sox-path*) *sox-options*))
                                        (loop 
@@ -282,6 +292,11 @@
                    (setf str (string+ str sox-effect))
                    (print str)
                    (sox-out str sox-input output outfile recursive)
                       ;optional removal of temp file
                  ;    (add-tmp-file outfile_tmp)
                  ;    (when *delete-inter-file* (clean-tmp-files))
                )
            )
        )
@@ -516,7 +531,7 @@
; === sox-mix-console =============
; Notes
; Notes on polymorphism
; I could make this sox-mix-console (non-consistent) a method where a list of effects will be applied to each sound in the mix-console
;   OR: keep this as an extra function for the player
; I can't use pipe-input to the mix-console as it makes sox-remix instances with pipe-outputs ->is a pipe in a pipe not possible?
sources/utilities.lisp
@@ -366,7 +366,7 @@
                                                for gainitem in (cdr subgainlist) do
                                                (unless (< gainitem -150)   ;it would be better to catch 'nil' instead of a very small value (depends on float precision)
                                                  (setf substr (string+ substr (format nil ",~dp~d" channelitem gainitem))))
                                                finally return substr)
                                                finally (return substr))
                                          )))))
    outstr))