asteroid/cl-streamer/protocol.lisp

172 lines
7.0 KiB
Common Lisp

;;;; protocol.lisp - Protocol definitions for cl-streamer
;;;; Defines the generic function protocol that decouples application code
;;;; from specific backend implementations (Harmony, encoders, server).
;;;;
;;;; Applications program against these generics; backends provide methods.
(in-package #:cl-streamer)
;;; ============================================================
;;; Stream Server Protocol
;;; ============================================================
;;; The stream server handles HTTP connections, mount points,
;;; ICY metadata injection, and client lifecycle.
(defgeneric server-start (server)
(:documentation "Start the stream server, begin accepting connections."))
(defgeneric server-stop (server)
(:documentation "Stop the stream server, disconnect all clients."))
(defgeneric server-running-p (server)
(:documentation "Return T if the server is currently running."))
(defgeneric server-add-mount (server path &key content-type bitrate name genre buffer-size)
(:documentation "Add a mount point to the server. Returns the mount-point object."))
(defgeneric server-remove-mount (server path)
(:documentation "Remove a mount point from the server."))
(defgeneric server-update-metadata (server path &key title url)
(:documentation "Update ICY metadata for a mount point."))
(defgeneric server-listener-count (server &optional path)
(:documentation "Return the number of connected listeners.
If PATH is given, count only listeners on that mount."))
(defgeneric server-write-audio (server mount-path data &key start end)
(:documentation "Write encoded audio data to a mount point's broadcast buffer."))
;;; ============================================================
;;; Audio Pipeline Protocol
;;; ============================================================
;;; The pipeline connects an audio source (e.g. Harmony) to
;;; encoders and the stream server. It manages playback,
;;; queueing, crossfading, and metadata propagation.
(defgeneric pipeline-start (pipeline)
(:documentation "Start the audio pipeline."))
(defgeneric pipeline-stop (pipeline)
(:documentation "Stop the audio pipeline."))
(defgeneric pipeline-running-p (pipeline)
(:documentation "Return T if the pipeline is currently running."))
(defgeneric pipeline-play-file (pipeline file-path &key title)
(:documentation "Play a single audio file through the pipeline.
Returns (values voice display-title track-info)."))
(defgeneric pipeline-play-list (pipeline file-list &key crossfade-duration
fade-in fade-out
loop-queue)
(:documentation "Play a list of files sequentially with crossfading.
Each entry can be a string (path) or plist (:file path :title title).
Runs in a background thread."))
(defgeneric pipeline-skip (pipeline)
(:documentation "Skip the currently playing track."))
(defgeneric pipeline-queue-files (pipeline file-entries &key position)
(:documentation "Add file entries to the playback queue.
POSITION is :end (append, default) or :next (prepend)."))
(defgeneric pipeline-get-queue (pipeline)
(:documentation "Return a copy of the current playback queue."))
(defgeneric pipeline-clear-queue (pipeline)
(:documentation "Clear the playback queue."))
(defgeneric pipeline-current-track (pipeline)
(:documentation "Return the current track info plist, or NIL.
Plist keys: :file :display-title :artist :title :album"))
(defgeneric pipeline-listener-count (pipeline &optional mount)
(:documentation "Return the listener count (delegates to the server)."))
(defgeneric pipeline-update-metadata (pipeline title)
(:documentation "Update ICY metadata on all mount points."))
;;; ============================================================
;;; Pipeline Hook Protocol
;;; ============================================================
;;; Hooks replace direct slot access for callbacks.
;;; Events: :track-change, :playlist-change
(defgeneric pipeline-add-hook (pipeline event function)
(:documentation "Register FUNCTION to be called when EVENT occurs.
Events:
:track-change — (lambda (pipeline track-info))
:playlist-change — (lambda (pipeline playlist-path))"))
(defgeneric pipeline-remove-hook (pipeline event function)
(:documentation "Remove FUNCTION from the hook list for EVENT."))
(defgeneric pipeline-fire-hook (pipeline event &rest args)
(:documentation "Fire all hooks registered for EVENT with ARGS.
Called internally by the pipeline implementation."))
;;; ============================================================
;;; Encoder Protocol
;;; ============================================================
;;; Encoders convert PCM audio data into a streaming format
;;; (MP3, AAC, Opus, etc).
(defgeneric encoder-encode (encoder pcm-buffer num-samples)
(:documentation "Encode PCM samples. Returns encoded byte vector.
PCM-BUFFER is a (signed-byte 16) array of interleaved stereo samples.
NUM-SAMPLES is the number of sample frames (not individual samples)."))
(defgeneric encoder-flush (encoder)
(:documentation "Flush any remaining data from the encoder. Returns byte vector."))
(defgeneric encoder-close (encoder)
(:documentation "Release encoder resources."))
;;; ============================================================
;;; Default method implementations
;;; ============================================================
;;; Server protocol — default methods on the existing stream-server class
;;; These delegate to the existing functions so nothing breaks.
(defmethod server-start ((server stream-server))
(start-server server))
(defmethod server-stop ((server stream-server))
(stop-server server))
(defmethod server-running-p ((server stream-server))
(slot-value server 'running))
(defmethod server-add-mount ((server stream-server) path
&key (content-type "audio/mpeg")
(bitrate 128)
(name "CL-Streamer")
(genre "Various")
(buffer-size (* 1024 1024)))
(add-mount server path
:content-type content-type
:bitrate bitrate
:name name
:genre genre
:buffer-size buffer-size))
(defmethod server-remove-mount ((server stream-server) path)
(remove-mount server path))
(defmethod server-update-metadata ((server stream-server) path &key title url)
(update-metadata server path :title title :url url))
(defmethod server-listener-count ((server stream-server) &optional path)
(listener-count server path))
(defmethod server-write-audio ((server stream-server) mount-path data
&key (start 0) (end (length data)))
(let ((mount (gethash mount-path (server-mounts server))))
(when mount
(buffer-write (mount-buffer mount) data :start start :end end))))
;;; Encoder protocol methods are defined in encoder.lisp and aac-encoder.lisp
;;; alongside their respective class definitions (separate ASDF subsystems).