Skip to content

Instantly share code, notes, and snippets.

@Jach
Last active October 2, 2021 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jach/dc2ec7b9402d0ec5836a935384cacdcb to your computer and use it in GitHub Desktop.
Save Jach/dc2ec7b9402d0ec5836a935384cacdcb to your computer and use it in GitHub Desktop.
Simple hacky test of using cl-autowrap and cffi to load fmod with Lisp and get a song to play
;; Public domain example of using cl-autowrap and cffi to follow using the core api documented at
;; https://fmod.com/resources/documentation-api?version=2.0&page=white-papers-getting-started.html
;; to load and play a sound file with fmod.
;; This file assumes it's in the downloaded fmodstudioapi20009linux/ folder,
;; which contains the C includes and pre-built dynamic libraries.
;; Try it out, evaluating step by step!
(ql:quickload :cl-autowrap)
(defpackage :fmod-test
(:use :cl))
(in-package :fmod-test)
(defparameter *fmod.h* (merge-pathnames #p"api/core/inc/fmod.h"))
(ensure-directories-exist (merge-pathnames #p"spec/"))
(autowrap:c-include *fmod.h* :spec-path (merge-pathnames #p"spec/"))
(defparameter *fmod.so* (merge-pathnames #p"api/core/lib/x86_64/libfmod.so"))
(cffi:load-foreign-library *fmod.so*)
(defparameter *system-ptr* (cffi:foreign-alloc :pointer))
(fmod-system-create *system-ptr*)
; (= * +fmod-ok+) ; 0 -- see fmod_errors.h for explanations
(defparameter *system* (cffi:mem-ref *system-ptr* :pointer))
(fmod-system-init *system* 512 +fmod-init-normal+ nil)
; (= * +fmod-ok+) ; 0
(defparameter *test-sound-path*
(merge-pathnames (uiop:parse-unix-namestring "../Desktop/muZn_HnTnL.mp3")))
(defparameter *sound-ptr* (cffi:foreign-alloc :pointer))
(fmod-system-create-sound *system* (namestring *test-sound-path*) +fmod-default+ nil *sound-ptr*)
; (= * +fmod-ok+) ; 0
; play immediately:
;(fmod-system-play-sound *system* (cffi:mem-ref *sound-ptr* :pointer) nil 0 nil)
; (= * +fmod-ok+) ; 0 -- should return immediately, i.e. sound playing does not block
; if we want, we can monitor the sound with a channel object
(defparameter *channel-ptr* (cffi:foreign-alloc :pointer))
(fmod-system-play-sound *system* (cffi:mem-ref *sound-ptr* :pointer) nil 0 *channel-ptr*)
; e.g. check if it's playing:
(defun is-playing? ()
(cffi:with-foreign-object (is-playing? :pointer)
(fmod-channel-is-playing (cffi:mem-ref *channel-ptr* :pointer) is-playing?)
(cffi:mem-ref is-playing? :bool)))
(is-playing?)
; could also know this by seeing +fmod-err-invalid-handle+ (30)
; returned when calling any channel methods after it's done playing
; btw, you can know what enum key the value 30 corresponds to with:
; (autowrap:enum-key '(:enum (fmod-result)) 30)
(sleep 5)
(format t "Pausing 5 seconds...~%")
(fmod-channel-set-paused (cffi:mem-ref *channel-ptr* :pointer) 1)
(sleep 5)
(fmod-channel-set-paused (cffi:mem-ref *channel-ptr* :pointer) 0)
(loop while (is-playing?) do
(sleep 1)
(fmod-system-update *system*)) ; should be called once per 'game' tick, not needed here but see its doc for what it is needed for
(fmod-sound-release (cffi:mem-ref *sound-ptr* :pointer))
(fmod-system-release *system*)
(cffi:foreign-free *channel-ptr*)
(cffi:foreign-free *sound-ptr*)
(cffi:foreign-free *system-ptr*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment