;;-*-Lisp-*-
(in-package goal)

;; name: gsound.gc
;; name in dgo: gsound
;; dgos: ENGINE, GAME

;; DECOMP BEGINS

(deftype engine-sound-pers (engine-pers)
  ()
  )


(defmethod kill-callback ((this engine-sound-pers) (arg0 connection-pers))
  "Called when a connection is removed."
  (let ((v1-0 (the-as sound-rpc-set-param (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-param))
    (set! (-> v1-0 id) (the-as sound-id (-> arg0 param-int64 0)))
    (set! (-> v1-0 params volume) -4)
    (set! (-> v1-0 auto-time) 120)
    (set! (-> v1-0 auto-from) 2)
    (set! (-> v1-0 params mask) (the-as uint 17))
    (the-as sound-rpc-set-param (-> v1-0 id))
    )
  0
  (none)
  )

(kmemopen global "sound-loop-engine")

(define *sound-loop-engine* (new 'global 'engine-sound-pers '*sound-loop-engine* 32 connection-pers))

(kmemclose)

(kmemopen global "sound-rpc")

(define *sound-player-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 128) 0))

(define *sound-loader-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 1) 1))

(kmemclose)

(defun sound-name= ((a sound-name) (b sound-name))
  "Are two sound names the same?"
  (and (= (the-as uint a) (the-as uint b)) (= (-> a hi) (-> b hi)))
  )

(deftype sound-iop-info (structure)
  ((frame            uint32)
   (strpos           int32)
   (str-id           uint32)
   (str-id-sign      int32                :overlay-at str-id)
   (freemem          uint32)
   (chinfo           uint8              48)
   (freemem2         uint32)
   (nocd             uint32)
   (dirtycd          uint32)
   (diskspeed        uint32             2)
   (lastspeed        uint32)
   (dupseg           int32)
   (times            int32              41)
   (times-seq        uint32)
   (iop-ticks        uint32)
   (stream-position  uint32             4 :offset 272)
   (stream-status    stream-status      4)
   (stream-name      sound-stream-name  4 :inline)
   (stream-id        sound-id           4)
   (stream-id-int32  int32              4 :overlay-at (-> stream-id 0))
   (music-register   uint8              17)
   (music-excite     int8                 :overlay-at (-> music-register 16))
   (ramdisk-name     uint8              48)
   )
  )


(define *sound-iop-info* (new 'global 'sound-iop-info))

(defun str-is-playing? ()
  "Is any streaming audio playing?"
  (countdown (v1-0 4)
    (if (and (>= (the-as uint (-> *sound-iop-info* stream-id v1-0)) 0)
             (logtest? (-> *sound-iop-info* stream-status v1-0) (stream-status ststatus-four))
             )
        (return #t)
        )
    )
  #f
  )

(defun str-id-is-playing? ((arg0 int))
  "Is any streaming audio with the given ID playing?"
  (countdown (v1-0 4)
    (if (and (= arg0 (-> *sound-iop-info* stream-id v1-0))
             (logtest? (-> *sound-iop-info* stream-status v1-0) (stream-status ststatus-one ststatus-six))
             )
        (return #t)
        )
    )
  #f
  )

(defun current-str-pos ((id sound-id))
  "Get the current stream position of the given sound ID, or -1 if it is not playing."
  (if (>= (-> *setting-control* user-current movie-skip-frame) 0.0)
      (return (the int (* 34.133335 (-> *setting-control* user-current movie-skip-frame))))
      )
  (dotimes (v1-5 4)
    (if (= id (-> *sound-iop-info* stream-id v1-5))
        (return (the-as int (-> *sound-iop-info* stream-position v1-5)))
        )
    )
  -1
  )

(defun is-ramdisk-loaded? ((name symbol))
  "Check if the ramdisk-name is set to this (unused)."
  (let ((gp-0 (-> *sound-iop-info* ramdisk-name))
        (s5-0 (symbol->string name))
        )
    (and (charp-prefix= (-> s5-0 data) gp-0) (= (-> gp-0 (length s5-0)) 46))
    )
  )

(defun is-cd-in? ()
  "Is the CD/DVD in the ps2?"
  (zero? (-> *sound-iop-info* nocd))
  )

(defun new-sound-id ()
  "Allocate a new sound ID, used to identify a unique playback of a sound."
  (set! *current-sound-id* (+ *current-sound-id* 1))
  (if (< (the-as int *current-sound-id*) #x10000)
      (set! *current-sound-id* (the-as sound-id #x10000))
      )
  *current-sound-id*
  )

(defun check-irx-version ()
  "Confirm OVERLORD version is correct and set up the sound-iop-info address so the OVERLORD can report back to the EE."
  (let ((gp-0 (the-as sound-rpc-get-irx-version (add-element *sound-loader-rpc*))))
    (set! (-> gp-0 command) (sound-command get-irx-version))
    (set! (-> gp-0 ee-addr) (&-> *sound-iop-info* frame))
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer gp-0) (the-as uint 80))
    (sync *sound-loader-rpc* #f)
    (format 0 "IRX version ~D.~D~%" (-> gp-0 major) (-> gp-0 minor))
    (when (or (!= (-> gp-0 major) 4) (nonzero? (-> gp-0 minor)))
      (format 0 "ERROR: IRX is the wrong version - need ~D.~D~%" 4 0)
      (format 0 "~%~%Please do (:mch) then mkee on linux-dog~%~%~%")
      (crash!)
      0
      )
    )
  0
  )

(defun sound-bank-iop-store ((name sound-name))
  "Send a iop-store RPC to loader with the given name"
  (let ((gp-0 (new-sound-id)))
    (let ((v1-1 (the-as sound-rpc-bank-cmd (add-element *sound-loader-rpc*))))
      (set! (-> v1-1 command) (sound-command iop-store))
      (set! (-> v1-1 bank-name) name)
      )
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    gp-0
    )
  )

(defun sound-bank-iop-free ((name sound-name))
  "Send a iop-free RPC to loader with the given name"
  (let ((gp-0 (new-sound-id)))
    (let ((v1-1 (the-as sound-rpc-bank-cmd (add-element *sound-loader-rpc*))))
      (set! (-> v1-1 command) (sound-command iop-free))
      (set! (-> v1-1 bank-name) name)
      )
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    gp-0
    )
  )

(defun sound-bank-load ((arg0 sound-name))
  "Send RPC to load a sound bank."
  (let ((gp-0 (new-sound-id)))
    (let ((v1-1 (the-as sound-rpc-load-bank (add-element *sound-loader-rpc*))))
      (set! (-> v1-1 command) (sound-command load-bank))
      (set! (-> v1-1 bank-name) arg0)
      )
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    gp-0
    )
  )

(defun sound-bank-load-from-iop ((name sound-name))
  "Send load-bank-from-iop rpc to loader"
  (let ((gp-0 (new-sound-id)))
    (let ((v1-1 (the-as sound-rpc-load-bank (add-element *sound-loader-rpc*))))
      (set! (-> v1-1 command) (sound-command load-bank-from-iop))
      (set! (-> v1-1 bank-name) name)
      )
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    gp-0
    )
  )

(defun sound-bank-load-from-ee ((name sound-name) (addr pointer))
  "Send load-bank-from-ee rpc to loader"
  (let ((gp-0 (new-sound-id)))
    (let ((v1-1 (the-as sound-rpc-load-bank (add-element *sound-loader-rpc*))))
      (set! (-> v1-1 command) (sound-command load-bank-from-ee))
      (set! (-> v1-1 bank-name) name)
      (set! (-> v1-1 ee-addr) addr)
      )
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    gp-0
    )
  )

(defun sound-bank-unload ((name sound-name))
  "Send unload-bank rpc to loader"
  (let ((v1-1 (the-as sound-rpc-unload-bank (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command unload-bank))
    (set! (-> v1-1 bank-name) name)
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  0
  )

(defun sound-music-load ((name sound-name))
  "Send load-music rpc to loader"
  (let ((v1-1 (the-as sound-rpc-load-music (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command load-music))
    (set! (-> v1-1 bank-name) name)
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  0
  )

(defun sound-music-unload ()
  "Send unload-music rpc to loader."
  (let ((v1-1 (the-as sound-rpc-unload-music (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command unload-music))
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  0
  )

(defun set-language ((arg0 language-enum))
  "Send set-language rpc to loader. Note this is only for sound, no game text is changed."
  (kset-language arg0)
  (let ((v1-1 (the-as sound-rpc-set-language (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command set-language))
    (set! (-> v1-1 lang) (the-as uint arg0))
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  0
  )

(defun sound-set-stereo-mode ((arg0 stereo-mode))
  "Send set-stereo-mode rpc to loader."
  (let ((v1-1 (the-as sound-rpc-set-stereo-mode (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command set-stereo-mode))
    (set! (-> v1-1 mode) (the-as int arg0))
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  0
  (none)
  )

(defun list-sounds ()
  "Send list-sounds rpc to loader."
  (let ((v1-1 (the-as sound-rpc-list-sounds (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) (sound-command list-sounds))
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  (syncv 0)
  0
  (none)
  )

(defun sound-command->string ((cmd sound-command))
  (enum->string sound-command cmd)
  )

(defun sound-buffer-dump ()
  "Print out all sound-commands in the buffer."
  (let ((gp-0 (-> *sound-player-rpc* current elt-used))
        (s5-0 (-> *sound-player-rpc* current elt-size))
        )
    (dotimes (s4-0 (the-as int gp-0))
      (let* ((s3-0 (the-as sound-rpc-play (&+ (-> *sound-player-rpc* current base) (* s5-0 (the-as uint s4-0)))))
             (a3-0 (sound-command->string (-> s3-0 command)))
             )
        (case (-> s3-0 command)
          (((sound-command play))
           (format #t "~D ~A ~G~%" s4-0 a3-0 (&-> s3-0 name))
           )
          (else
            (format #t "~D ~A~%" s4-0 a3-0)
            )
          )
        )
      )
    )
  0
  )

;; og:preserve-this mirror mode
(define *sound-current-mirror* (sound-mirror-mode normal))

;; og:preserve-this mirror mode
(defun sound-set-mirror-mode ((mode sound-mirror-mode))
  (when (!= mode *sound-current-mirror*)
    (let ((cmd (the sound-rpc-set-mirror-mode (add-element *sound-loader-rpc*))))
      (set! (-> cmd command) (sound-command set-mirror))
      (set! (-> cmd mirror) mode))
    (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
    (set! *sound-current-mirror* mode))
  (none))

(define *sound-player-enable* #t)

(defun swap-sound-buffers ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float))
  "Per-frame update of sound system - update loop-engine, update ear trans, send player RPC, check for missing/dirty CD."
  (run-pending-updates! *sound-loop-engine* (-> *display* base-clock frame-counter))
  (cond
    ((check-busy *sound-player-rpc*)
     (set! *sound-player-enable* #f)
     )
    (else
      (let ((a0-3 (-> *sound-player-rpc* current)))
        (if (< (-> a0-3 elt-used) (-> a0-3 elt-count))
            (sound-set-ear-trans arg0 arg1 arg2 arg3)
            )
        )
      (call *sound-player-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
      (set! *sound-player-enable* #t)
      )
    )
  (cond
    ((not (-> *setting-control* user-current allow-error))
     )
    ((nonzero? (-> *sound-iop-info* nocd))
     (if (or (not *progress-process*) (!= (-> *progress-process* 0 current) 'error-disc-removed))
         (activate-progress *dproc* 'error-disc-removed)
         )
     )
    ((nonzero? (-> *sound-iop-info* dirtycd))
     (if (or (not *progress-process*) (!= (-> *progress-process* 0 current) 'error-reading))
         (activate-progress *dproc* 'error-reading)
         )
     )
    )
  0
  )

(defun get-sound-buffer-entry ()
  "Allocate a new entry in the player RPC queue, to be flushed on the next frame."
  (add-element *sound-player-rpc*)
  )

(defun free-last-sound-buffer-entry ()
  "Remove the last thing added with get-sound-buffer-entry."
  (decrement-elt-used *sound-player-rpc*)
  0
  (none)
  )

(defun sound-basic-cb ((arg0 int) (arg1 (pointer int32)))
  "Unknown and unused callback."
  (set! (-> arg1 0) arg0)
  0
  (none)
  )

(defun sound-trans-convert ((arg0 vector3w) (arg1 vector))
  "Convert a GOAL translation (meters) to integer format for the IOP"
  (let ((v1-0 (if arg1
                  arg1
                  (ear-trans 0)
                  )
              )
        )
    (set! (-> arg0 x) (the int (/ (-> v1-0 x) 16)))
    (set! (-> arg0 y) (the int (/ (-> v1-0 y) 16)))
    (set! (-> arg0 z) (the int (/ (-> v1-0 z) 16)))
    )
  0
  )

(defun sound-angle-convert ((float-angle float))
  "Convert a GOAL angle to integer format for the iop"
  (let* ((f0-3 (the float (sar (shl (the int float-angle) 48) 48)))
         (v0-0 (the int (* 0.005493164 f0-3)))
         )
    (if (< v0-0 0)
        (+! v0-0 360)
        )
    (if (< 359 v0-0)
        (+! v0-0 -360)
        )
    v0-0
    )
  )

;; WARN: Return type mismatch uint128 vs sound-name.
(defun string->sound-name ((arg0 string))
  "Create a sound-name from a string"
  (let ((v1-0 (new 'stack-no-clear 'qword)))
    (set! (-> v1-0 quad) (the-as uint128 0))
    (let ((a1-0 (the-as (pointer uint8) v1-0))
          (a2-0 (-> arg0 data))
          )
      (while (and (nonzero? (-> a2-0 0)) (< (&- a2-0 (the-as uint (-> arg0 data))) 15))
        (set! (-> a1-0 0) (-> a2-0 0))
        (set! a1-0 (&-> a1-0 1))
        (set! a2-0 (&-> a2-0 1))
        )
      )
    (the-as sound-name (-> v1-0 quad))
    )
  )

(defun sound-set-volume ((arg0 sound-group) (arg1 float))
  "Send set-master-volume rpc."
  (let ((v1-0 (the-as sound-rpc-set-master-volume (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-master-volume))
    (set! (-> v1-0 group) arg0)
    (set! (-> v1-0 volume) (the int (* 1024.0 arg1)))
    )
  0
  )

(defun sound-set-reverb ((reverb int) (left float) (right float) (core uint))
  "Send set-reverb rpc"
  (let ((v1-0 (the-as sound-rpc-set-reverb (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-reverb))
    (set! (-> v1-0 core) core)
    (set! (-> v1-0 reverb) reverb)
    (set! (-> v1-0 left) (the-as uint (the int (* 32767.0 left))))
    (set! (-> v1-0 right) (the-as uint (the int (* 32767.0 right))))
    )
  0
  )

(defun sound-set-ear-trans ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 float))
  "Send set-ear-trans rpc"
  (let ((gp-0 (the-as sound-rpc-set-ear-trans (get-sound-buffer-entry))))
    (set! (-> gp-0 command) (sound-command set-ear-trans))
    (sound-trans-convert (the-as vector3w (-> gp-0 ear-trans0)) arg0)
    (sound-trans-convert (the-as vector3w (-> gp-0 ear-trans1)) arg1)
    (sound-trans-convert (the-as vector3w (-> gp-0 cam-trans)) arg2)
    (set! (-> gp-0 cam-angle) (sound-angle-convert arg3))
    )
  0
  )

(defbehavior sound-play-by-name process-drawable ((arg0 sound-name) (arg1 sound-id) (arg2 int) (arg3 int) (arg4 int) (arg5 sound-group) (arg6 object))
  "Send play rpc to play a sound!
   Last arg can by a symbol with value [[#t]], in which case it will pull `trans` [[vector]] off the current [[process-drawable]]
   otherwise, an explicit [[vector]] can be provided"
  (local-vars (sv-16 sound-group))
  (set! sv-16 arg5)
  (let ((s4-0 arg6))
    (when *sound-player-enable*
      (let ((s5-0 (the-as sound-rpc-play (get-sound-buffer-entry))))
        (set! (-> s5-0 command) (sound-command play))
        (set! (-> s5-0 id) arg1)
        (set! (-> s5-0 name) arg0)
        (set! (-> s5-0 params mask) (the-as uint 0))
        (set! (-> s5-0 params group) (the-as uint sv-16))
        (set! (-> s5-0 params volume) arg2)
        (set! (-> s5-0 params pitch-mod) arg3)
        (set! (-> s5-0 params bend) arg4)
        (let ((s3-1 self))
          (when (= s4-0 #t)
            (if (and s3-1 (type? s3-1 process-drawable) (nonzero? (-> s3-1 root)))
                (set! s4-0 (-> s3-1 root trans))
                (set! s4-0 #f)
                )
            )
          )
        (sound-trans-convert (the-as vector3w (-> s5-0 params trans)) (the-as vector s4-0))
        )
      )
    )
  arg1
  )

(defbehavior sound-play-by-spec process-drawable ((arg0 sound-spec) (arg1 sound-id) (arg2 vector))
  "Send play rpc to play a sound!"
  (when *sound-player-enable*
    (let ((s5-0 (the-as sound-rpc-play (get-sound-buffer-entry))))
      (set! (-> s5-0 command) (sound-command play))
      (set! (-> s5-0 id) arg1)
      (set! (-> s5-0 name) (-> arg0 sound-name))
      (set! (-> s5-0 params mask) (the-as uint (-> arg0 mask)))
      (set! (-> s5-0 params group-and-regs) (-> arg0 group-and-regs))
      (set! (-> s5-0 params volume) (-> arg0 volume))
      (set! (-> s5-0 params pitch-mod) (-> arg0 pitch-mod))
      (set! (-> s5-0 params bend) (-> arg0 bend))
      (set! (-> s5-0 params fo-min) (-> arg0 fo-min))
      (set! (-> s5-0 params fo-max) (-> arg0 fo-max))
      (set! (-> s5-0 params fo-curve) (-> arg0 fo-curve))
      (set! (-> s5-0 params priority) (-> arg0 priority))
      (let ((s3-1 self))
        (when (= arg2 #t)
          (if (and s3-1 (type? s3-1 process-drawable) (nonzero? (-> s3-1 root)))
              (set! arg2 (-> s3-1 root trans))
              (set! arg2 (the-as vector #f))
              )
          )
        )
      (sound-trans-convert (the-as vector3w (-> s5-0 params trans)) arg2)
      )
    )
  arg1
  )

(defun sound-pause ((id sound-id))
  "Send pause-sound rpc to pause by id"
  (when (nonzero? id)
    (let ((v1-1 (the-as sound-rpc-pause-sound (get-sound-buffer-entry))))
      (set! (-> v1-1 command) (sound-command pause-sound))
      (set! (-> v1-1 id) id)
      )
    )
  0
  )

(defun sound-stop ((id sound-id))
  "Send stop-sound rpc to stop by id"
  (when (nonzero? id)
    (let ((v1-1 (the-as sound-rpc-stop-sound (get-sound-buffer-entry))))
      (set! (-> v1-1 command) (sound-command stop-sound))
      (set! (-> v1-1 id) id)
      )
    )
  0
  )

(defun sound-continue ((id sound-id))
  "Send continue-sound rpc to continue by id"
  (when (nonzero? id)
    (let ((v1-1 (the-as sound-rpc-continue-sound (get-sound-buffer-entry))))
      (set! (-> v1-1 command) (sound-command continue-sound))
      (set! (-> v1-1 id) id)
      )
    )
  0
  )

(defun sound-group-pause ((arg0 sound-group))
  "Send pause-group rpc"
  (let ((v1-0 (the-as sound-rpc-pause-group (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command pause-group))
    (set! (-> v1-0 group) arg0)
    )
  0
  )

(defun sound-group-stop ((arg0 sound-group))
  "Send stop-group rpc"
  (let ((v1-0 (the-as sound-rpc-stop-group (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command stop-group))
    (set! (-> v1-0 group) arg0)
    )
  0
  )

(defun sound-group-continue ((arg0 sound-group))
  "Send continue-group rpc"
  (let ((v1-0 (the-as sound-rpc-continue-group (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command continue-group))
    (set! (-> v1-0 group) arg0)
    )
  0
  )

(defun sound-set-flava ((flava uint) (excitement uint))
  "Send set-flava rpc"
  (let ((v1-0 (the-as sound-rpc-set-flava (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-flava))
    (set! (-> v1-0 flava) flava)
    (set! (-> v1-0 excitement) excitement)
    )
  0
  )

(defun sound-set-midi-reg ((reg int) (val int))
  "Send set-midi-reg rpc"
  (let ((v1-0 (the-as sound-rpc-set-midi-reg (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-midi-reg))
    (set! (-> v1-0 reg) reg)
    (set! (-> v1-0 value) val)
    )
  0
  )

(defun sound-set-fps ((fps int))
  "Send set-fps rpc"
  (let ((v1-0 (the-as sound-rpc-set-fps (get-sound-buffer-entry))))
    (set! (-> v1-0 command) (sound-command set-fps))
    (set! (-> v1-0 fps) (the-as uint fps))
    )
  0
  )

(defun sound-volume-off ()
  "Adjust settings to turn all sound volume off"
  (set-setting! 'music-volume 'abs 0.0 0)
  (set-setting! 'sfx-volume 'abs 0.0 0)
  (set-setting! 'ambient-volume 'abs 0.0 0)
  0
  )

(define *ambient-spec* (new 'static 'sound-spec))

;; WARN: Return type mismatch object vs ambient-sound.
(defmethod new ambient-sound ((allocation symbol) (type-to-make type) (arg0 basic) (arg1 vector))
  "Set up ambient-sound. Can use an entity-actor (grabs from lump), sound-spec, or name as a string."
  (local-vars
    (sv-16 sound-spec)
    (sv-32 sound-name)
    (sv-48 (pointer float))
    (sv-52 pointer)
    (sv-56 int)
    (sv-64 res-tag)
    )
  (set! sv-16 (the-as sound-spec #f))
  (set! sv-32 (the-as sound-name 0))
  (set! sv-48 (the-as (pointer float) #f))
  (set! sv-52 (the-as pointer #f))
  (set! sv-56 0)
  (case (-> arg0 type)
    ((entity-actor)
     (let ((v1-2
             ((method-of-type res-lump get-property-struct)
              (the-as res-lump arg0)
              'effect-name
              'exact
              0.0
              (the-as structure #f)
              (the-as (pointer res-tag) #f)
              *res-static-buf*
              )
             )
           )
       (when v1-2
         (set! sv-32 (string->sound-name (symbol->string v1-2)))
         (set! sv-48 (res-lump-data (the-as res-lump arg0) 'cycle-speed (pointer float)))
         (set! sv-16 *ambient-spec*)
         (set! sv-64 (new 'static 'res-tag))
         (let ((v1-7
                 ((method-of-type res-lump get-property-data)
                  (the-as res-lump arg0)
                  'effect-param
                  'exact
                  0.0
                  (the-as pointer #f)
                  (& sv-64)
                  *res-static-buf*
                  )
                 )
               )
           (when v1-7
             (set! sv-52 v1-7)
             (set! sv-56 (the-as int (-> sv-64 elt-count)))
             )
           )
         )
       )
     )
    ((sound-spec)
     (set! sv-16 (the-as sound-spec arg0))
     )
    ((string)
     (set! sv-32 (string->sound-name (the-as string arg0)))
     )
    (else
      (format 0 "ERROR: ambient sound was told to play an unknown ~A.~%" arg0)
      )
    )
  (the-as
    ambient-sound
    (cond
      ((or sv-16 (nonzero? sv-32))
       (let ((s5-1 (the-as object (object-new allocation type-to-make (the-as int (-> type-to-make size))))))
         (set! (-> (the-as ambient-sound s5-1) spec) sv-16)
         (set! (-> (the-as ambient-sound s5-1) name) sv-32)
         (set! (-> (the-as ambient-sound s5-1) playing-id) (new-sound-id))
         (set! (-> (the-as ambient-sound s5-1) params) (the-as (pointer float) sv-52))
         (set! (-> (the-as ambient-sound s5-1) param-count) sv-56)
         (set! (-> (the-as ambient-sound s5-1) entity) #f)
         (set! (-> (the-as ambient-sound s5-1) sound-count) 1)
         (set! (-> (the-as ambient-sound s5-1) volume) 1024)
         (set! (-> (the-as ambient-sound s5-1) pitch) 0)
         (when (and sv-16 (!= sv-16 *ambient-spec*))
           (if (logtest? (-> (the-as sound-spec sv-16) mask) (sound-mask volume))
               (set! (-> (the-as ambient-sound s5-1) volume) (-> (the-as sound-spec sv-16) volume))
               )
           (if (logtest? (-> (the-as sound-spec sv-16) mask) (sound-mask pitch))
               (set! (-> (the-as ambient-sound s5-1) pitch) (-> (the-as sound-spec sv-16) pitch-mod))
               )
           )
         (cond
           (sv-48
             (set! (-> (the-as ambient-sound s5-1) time-base) (the-as time-frame (the int (* 300.0 (-> sv-48 0)))))
             (set! (-> (the-as ambient-sound s5-1) time-random) (the-as time-frame (the int (* 300.0 (-> sv-48 1)))))
             )
           (else
             (set! (-> (the-as ambient-sound s5-1) time-base) -1)
             )
           )
         (set! (-> (the-as ambient-sound s5-1) trans quad) (-> arg1 quad))
         s5-1
         )
       )
      (else
        0
        )
      )
    )
  )

(defmethod update! ((this ambient-sound))
  "Per-frame update of ambient sound."
  (with-pp
    (if (not *ambient-sound-class*)
        (return (the-as int #f))
        )
    (cond
      ((-> this spec)
       (when (or (< (-> this time-base) 0) (>= (current-time) (-> this play-time)))
         (when (>= (-> this time-base) 0)
           (set! (-> this play-time)
                 (+ (current-time) (-> this time-base) (rand-vu-int-count (the-as int (-> this time-random))))
                 )
           (set! (-> this playing-id) (new-sound-id))
           )
         (let ((s5-1 (-> this spec)))
           (when (= s5-1 *ambient-spec*)
             (set! (-> s5-1 volume) (-> this volume))
             (set! (-> s5-1 pitch-mod) (-> this pitch))
             (set! (-> s5-1 bend) 0)
             (set! (-> s5-1 sound-name) (-> this name))
             (set! (-> s5-1 fo-max) (-> this falloff-far))
             (set! (-> s5-1 mask) (sound-mask))
             (if (-> this params)
                 (effect-param->sound-spec s5-1 (-> this params) (-> this param-count) (the-as process-focusable pp))
                 )
             )
           (let ((v1-23 (-> s5-1 fo-max)))
             (if (and (nonzero? v1-23) (< (* 4096.0 (the float v1-23)) (vector-vector-distance (ear-trans 0) (-> this trans))))
                 (return 0)
                 )
             )
           (when (and *debug-effect-control* (>= (-> this time-base) 0))
             (format #t "(~5D) effect sound ~A ~G " (current-time) (-> pp name) (&-> s5-1 sound-name))
             (format
               #t
               "volume: ~f  pitch-mod: ~f~%"
               (* 0.09765625 (the float (-> s5-1 volume)))
               (* 0.000656168 (the float (-> s5-1 pitch-mod)))
               )
             )
           (let ((s4-2 (-> s5-1 volume)))
             (set! (-> s5-1 volume) (-> this volume))
             (set! (-> this playing-id) (sound-play-by-spec s5-1 (-> this playing-id) (-> this trans)))
             (set! (-> s5-1 volume) s4-2)
             )
           )
         )
       )
      ((< (-> this time-base) 0)
       (let ((v1-39 (-> this falloff-far)))
         (if (and (nonzero? v1-39) (< (* 4096.0 (the float v1-39)) (vector-vector-distance (ear-trans 0) (-> this trans))))
             (return 0)
             )
         )
       (set! (-> this playing-id) (sound-play-by-name
                                    (-> this name)
                                    (-> this playing-id)
                                    (-> this volume)
                                    (-> this pitch)
                                    0
                                    (sound-group sfx)
                                    (-> this trans)
                                    )
             )
       )
      (else
        (when (>= (current-time) (-> this play-time))
          (set! (-> this playing-id)
                (sound-play-by-name
                  (-> this name)
                  (new-sound-id)
                  (-> this volume)
                  (-> this pitch)
                  0
                  (sound-group sfx)
                  (-> this trans)
                  )
                )
          (set! (-> this play-time)
                (+ (current-time) (-> this time-base) (rand-vu-int-count (the-as int (-> this time-random))))
                )
          )
        )
      )
    0
    )
  )

(defmethod stop! ((this ambient-sound))
  (sound-stop (-> this playing-id))
  0
  )

(defmethod update-trans! ((this ambient-sound) (arg0 vector))
  "Change the trans of the sound."
  (with-pp
    (set! (-> this trans quad) (-> arg0 quad))
    (when (nonzero? (-> this playing-id))
      (when *sound-player-enable*
        (let ((s5-0 (the-as sound-rpc-set-param (get-sound-buffer-entry))))
          (set! (-> s5-0 command) (sound-command set-param))
          (set! (-> s5-0 id) (-> this playing-id))
          (let ((s4-1 (the-as process-drawable pp)))
            (when (= arg0 #t)
              (if (and s4-1 (type? s4-1 process-drawable) (nonzero? (-> s4-1 root)))
                  (set! arg0 (-> s4-1 root trans))
                  (set! arg0 (the-as vector #f))
                  )
              )
            )
          (sound-trans-convert (the-as vector3w (-> s5-0 params trans)) arg0)
          (set! (-> s5-0 params mask) (the-as uint 32))
          (-> s5-0 id)
          )
        )
      )
    0
    )
  )

(defmethod update-vol! ((this ambient-sound) (arg0 float))
  "Change the volume of the sound."
  (when (nonzero? (-> this playing-id))
    (when *sound-player-enable*
      (let ((v1-4 (the-as sound-rpc-set-param (get-sound-buffer-entry))))
        (set! (-> v1-4 command) (sound-command set-param))
        (set! (-> v1-4 id) (-> this playing-id))
        (set! (-> v1-4 params volume) (the int (* 1024.0 arg0)))
        (set! (-> v1-4 params mask) (the-as uint 1))
        (-> v1-4 id)
        )
      )
    )
  (set! (-> this volume) (the int (* 1024.0 arg0)))
  0
  )

(defmethod update-pitch-mod! ((this ambient-sound) (arg0 float))
  (when (nonzero? (-> this playing-id))
    (when *sound-player-enable*
      (let ((v1-4 (the-as sound-rpc-set-param (get-sound-buffer-entry))))
        (set! (-> v1-4 command) (sound-command set-param))
        (set! (-> v1-4 id) (-> this playing-id))
        (set! (-> v1-4 params pitch-mod) (the int (* 1524.0 arg0)))
        (set! (-> v1-4 params mask) (the-as uint 2))
        (-> v1-4 id)
        )
      )
    )
  (set! (-> this pitch) (the int (* 1524.0 arg0)))
  0
  (none)
  )

(defmethod set-falloff-far! ((this ambient-sound) (arg0 float))
  (set! (-> this falloff-far) (the int (/ arg0 METER_LENGTH)))
  0
  (none)
  )

(defmethod change-sound! ((this ambient-sound) (new-sound sound-name))
  "Change the sound being played."
  (when (not (and (= (the-as uint (-> this name)) (the-as uint new-sound)) (= (-> new-sound hi) (-> this name hi))))
    (stop! this)
    (set! (-> this playing-id) (new-sound-id))
    (set! (-> this name) new-sound)
    )
  0
  )

(defun show-iop-info ((arg0 dma-buffer))
  "Display iop info on screen."
  (dotimes (s5-0 24)
    (draw-string-xy
      (if (zero? (-> *sound-iop-info* chinfo s5-0))
          "."
          "X"
          )
      arg0
      (+ (* s5-0 16) 16)
      48
      (font-color default)
      (font-flags shadow)
      )
    )
  (dotimes (s5-1 24)
    (draw-string-xy
      (if (zero? (-> *sound-iop-info* chinfo (+ s5-1 24)))
          "."
          "X"
          )
      arg0
      (+ (* s5-1 16) 16)
      64
      (font-color default)
      (font-flags shadow)
      )
    )
  (let ((s5-2 draw-string-xy))
    (format
      (clear *temp-string*)
      "banks: ~15S ~15S ~15S"
      (-> *level* sound-bank 0)
      (-> *level* sound-bank 1)
      (-> *level* sound-bank 2)
      )
    (s5-2 *temp-string* arg0 16 88 (font-color default) (font-flags shadow))
    )
  (let ((s5-3 draw-string-xy))
    (format
      (clear *temp-string*)
      "music: ~15S fl:~1D/~2D ex:~4,,2f/~4,,2f ~10S"
      (-> *setting-control* user-current music)
      (-> *setting-control* user-current sound-flava)
      (-> *setting-control* user-current sound-stinger)
      (-> *setting-control* user-target sound-excitement)
      (-> *setting-control* user-current sound-excitement)
      (-> *setting-control* user-current mode-sound-bank)
      )
    (s5-3 *temp-string* arg0 16 104 (font-color default) (font-flags shadow))
    )
  0
  )

(defun show-iop-memory ((dma dma-buffer))
  "Display iop memory stats on screen"
  (let ((s5-0 draw-string-xy))
    (format
      (clear *temp-string*)
      "~8D [~4D]"
      (-> *sound-iop-info* freemem)
      (shr (-> *sound-iop-info* freemem) 10)
      )
    (s5-0 *temp-string* dma 32 96 (font-color default) (font-flags shadow))
    )
  (let ((s5-1 draw-string-xy))
    (format
      (clear *temp-string*)
      "~8D [~4D]"
      (-> *sound-iop-info* freemem2)
      (shr (-> *sound-iop-info* freemem2) 10)
      )
    (s5-1 *temp-string* dma 32 64 (font-color default) (font-flags shadow))
    )
  0
  )

(defun ear-trans ((arg0 int))
  "Get the current location of the ear. Use 1 for the settings ear-handle, or 0 for the camera."
  (cond
    ((or (movie?) *external-cam-mode*)
     (math-camera-pos)
     )
    ((and *target* (= arg0 1))
     (target-pos 0)
     )
    (else
      (camera-pos)
      )
    )
  )

(defun-debug make-sqrt-table ()
  "Generate integer square-root table used in the IOP."
  (format #t "static int sqrt_table[256] =~%{~%")
  (dotimes (gp-0 256)
    (let* ((f0-2 (sqrtf (* 16777216.0 (the float gp-0))))
           (a2-0 (the int (+ 0.5 f0-2)))
           )
      (format #t "~D,~%" a2-0)
      )
    )
  (format #t "};~%")
  0
  )

(check-irx-version)

(case (scf-get-territory)
  ((GAME_TERRITORY_SCEI)
   (sound-bank-load (static-sound-name "commonj"))
   )
  (else
    (sound-bank-load (static-sound-name "common"))
    )
  )

(dotimes (gp-0 3)
  (let ((s5-0 string->symbol))
    (format (clear *temp-string*) "empty~D" gp-0)
    (let ((s5-1 (s5-0 *temp-string*)))
      (sound-bank-load (string->sound-name (symbol->string s5-1)))
      (set! (-> *level* sound-bank gp-0) (the-as basic s5-1))
      )
    )
  )

(defun loader-test-command ((cmd sound-command) (param uint))
  "Send a command to loader by ID."
  (let ((v1-1 (the-as sound-rpc-test-cmd (add-element *sound-loader-rpc*))))
    (set! (-> v1-1 command) cmd)
    (set! (-> v1-1 ee-addr) (the-as pointer 0))
    (set! (-> v1-1 param0) param)
    )
  (call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
  (sync *sound-loader-rpc* #f)
  0
  (none)
  )

(defun doppler-pitch-shift ((sound-pos vector) (sound-vel vector))
  "Compute pitch shift for the doppler effect. This is done assuming the listener is *target* and has *target*'s velocity."
  (let ((gp-0 (new 'stack-no-clear 'inline-array 'vector 2)))
    (vector-! (-> gp-0 0) (target-pos 0) sound-pos)
    (if *target*
        (vector-! (-> gp-0 1) sound-vel (-> *target* control transv))
        (set! (-> gp-0 1 quad) (-> sound-vel quad))
        )
    (vector-normalize! (-> gp-0 0) 1.0)
    (let ((f0-1 (/ 1228800.0 (fmax 0.1 (- 1228800.0 (vector-dot (-> gp-0 0) (-> gp-0 1)))))))
      (log2f f0-1)
      )
    )
  )

(defun sound-bank-reload ()
  "Start a background process to unload all sound banks, then load them again."
  (process-spawn-function
    process
    (lambda ()
      (let ((gp-0 (new 'static 'boxed-array :type symbol :length 0 :allocated-length 3)))
        (dotimes (v1-0 3)
          (set! (-> gp-0 v1-0) (the-as symbol (-> *level* sound-bank v1-0)))
          )
        (let ((a1-3 (new 'stack-no-clear 'array 'symbol 4)))
          (set! (-> a1-3 2) 'empty2)
          (set! (-> a1-3 1) 'empty1)
          (set! (-> a1-3 0) 'empty0)
          (want-sound-banks *load-state* a1-3)
          )
        (suspend-for (seconds 1)
          )
        (let ((a1-4 (new 'stack-no-clear 'array 'symbol 4)))
          (set! (-> a1-4 2) (-> gp-0 2))
          (set! (-> a1-4 1) (-> gp-0 1))
          (set! (-> a1-4 0) (-> gp-0 0))
          (want-sound-banks *load-state* a1-4)
          )
        )
      (none)
      )
    :to *display-pool*
    )
  0
  (none)
  )
