(in-package #:cl-streamer) (defclass aac-encoder () ((handle :initform nil :accessor encoder-handle) (sample-rate :initarg :sample-rate :accessor aac-encoder-sample-rate :initform 44100) (channels :initarg :channels :accessor aac-encoder-channels :initform 2) (bitrate :initarg :bitrate :accessor aac-encoder-bitrate :initform 128000) (aot :initarg :aot :accessor aac-encoder-aot :initform :aot-aac-lc) (out-buffer :initform nil :accessor aac-encoder-out-buffer) (out-buffer-size :initform (* 1024 8) :accessor aac-encoder-out-buffer-size) (frame-length :initform 1024 :accessor aac-encoder-frame-length))) (defun make-aac-encoder (&key (sample-rate 44100) (channels 2) (bitrate 128000)) "Create an AAC encoder with the specified parameters. BITRATE is in bits per second (e.g., 128000 for 128kbps)." (let ((encoder (make-instance 'aac-encoder :sample-rate sample-rate :channels channels :bitrate bitrate))) (initialize-aac-encoder encoder) encoder)) (defun initialize-aac-encoder (encoder) "Initialize the FDK-AAC encoder with current settings." (cffi:with-foreign-object (handle-ptr :pointer) (let ((result (aac-enc-open handle-ptr 0 (aac-encoder-channels encoder)))) (unless (zerop result) (error 'encoding-error :format :aac :message (format nil "aacEncOpen failed: ~A" result))) (setf (encoder-handle encoder) (cffi:mem-ref handle-ptr :pointer)))) (let ((handle (encoder-handle encoder))) (aac-encoder-set-param handle :aacenc-aot 2) (aac-encoder-set-param handle :aacenc-samplerate (aac-encoder-sample-rate encoder)) (aac-encoder-set-param handle :aacenc-channelmode (if (= (aac-encoder-channels encoder) 1) 1 2)) (aac-encoder-set-param handle :aacenc-channelorder 1) (aac-encoder-set-param handle :aacenc-bitrate (aac-encoder-bitrate encoder)) (aac-encoder-set-param handle :aacenc-transmux 2) (aac-encoder-set-param handle :aacenc-afterburner 1) (cffi:with-foreign-object (in-args '(:struct aacenc-in-args)) (cffi:with-foreign-object (out-args '(:struct aacenc-out-args)) (setf (cffi:foreign-slot-value in-args '(:struct aacenc-in-args) 'num-in-samples) -1) (setf (cffi:foreign-slot-value in-args '(:struct aacenc-in-args) 'num-ancillary-bytes) 0) (let ((result (aac-enc-encode handle (cffi:null-pointer) (cffi:null-pointer) in-args out-args))) (unless (zerop result) (aac-enc-close (cffi:foreign-alloc :pointer :initial-element handle)) (error 'encoding-error :format :aac :message (format nil "aacEncEncode init failed: ~A" result)))))) (cffi:with-foreign-object (info '(:struct aacenc-info-struct)) (aac-enc-info handle info) (setf (aac-encoder-frame-length encoder) (cffi:foreign-slot-value info '(:struct aacenc-info-struct) 'frame-length)) (setf (aac-encoder-out-buffer-size encoder) (cffi:foreign-slot-value info '(:struct aacenc-info-struct) 'max-out-buf-bytes))) (setf (aac-encoder-out-buffer encoder) (cffi:foreign-alloc :unsigned-char :count (aac-encoder-out-buffer-size encoder))) (log:info "AAC encoder initialized: ~Akbps, ~AHz, ~A channels, frame-length=~A" (floor (aac-encoder-bitrate encoder) 1000) (aac-encoder-sample-rate encoder) (aac-encoder-channels encoder) (aac-encoder-frame-length encoder)) encoder)) (defun close-aac-encoder (encoder) "Close the AAC encoder and free resources." (when (encoder-handle encoder) (cffi:with-foreign-object (handle-ptr :pointer) (setf (cffi:mem-ref handle-ptr :pointer) (encoder-handle encoder)) (aac-enc-close handle-ptr)) (setf (encoder-handle encoder) nil)) (when (aac-encoder-out-buffer encoder) (cffi:foreign-free (aac-encoder-out-buffer encoder)) (setf (aac-encoder-out-buffer encoder) nil))) (defun encode-aac-pcm (encoder pcm-samples num-samples) "Encode PCM samples (16-bit signed interleaved) to AAC. Returns a byte vector of AAC data (ADTS frames)." (let* ((handle (encoder-handle encoder)) (channels (aac-encoder-channels encoder)) (out-buf (aac-encoder-out-buffer encoder)) (out-buf-size (aac-encoder-out-buffer-size encoder))) (cffi:with-pointer-to-vector-data (pcm-ptr pcm-samples) (cffi:with-foreign-objects ((in-buf-desc '(:struct aacenc-buf-desc)) (out-buf-desc '(:struct aacenc-buf-desc)) (in-args '(:struct aacenc-in-args)) (out-args '(:struct aacenc-out-args)) (in-buf-ptr :pointer) (in-buf-id :int) (in-buf-size :int) (in-buf-el-size :int) (out-buf-ptr :pointer) (out-buf-id :int) (out-buf-size-ptr :int) (out-buf-el-size :int)) (setf (cffi:mem-ref in-buf-ptr :pointer) pcm-ptr) (setf (cffi:mem-ref in-buf-id :int) 0) (setf (cffi:mem-ref in-buf-size :int) (* num-samples channels 2)) (setf (cffi:mem-ref in-buf-el-size :int) 2) (setf (cffi:foreign-slot-value in-buf-desc '(:struct aacenc-buf-desc) 'num-bufs) 1) (setf (cffi:foreign-slot-value in-buf-desc '(:struct aacenc-buf-desc) 'bufs) in-buf-ptr) (setf (cffi:foreign-slot-value in-buf-desc '(:struct aacenc-buf-desc) 'buf-ids) in-buf-id) (setf (cffi:foreign-slot-value in-buf-desc '(:struct aacenc-buf-desc) 'buf-sizes) in-buf-size) (setf (cffi:foreign-slot-value in-buf-desc '(:struct aacenc-buf-desc) 'buf-el-sizes) in-buf-el-size) (setf (cffi:mem-ref out-buf-ptr :pointer) out-buf) (setf (cffi:mem-ref out-buf-id :int) 1) (setf (cffi:mem-ref out-buf-size-ptr :int) out-buf-size) (setf (cffi:mem-ref out-buf-el-size :int) 1) (setf (cffi:foreign-slot-value out-buf-desc '(:struct aacenc-buf-desc) 'num-bufs) 1) (setf (cffi:foreign-slot-value out-buf-desc '(:struct aacenc-buf-desc) 'bufs) out-buf-ptr) (setf (cffi:foreign-slot-value out-buf-desc '(:struct aacenc-buf-desc) 'buf-ids) out-buf-id) (setf (cffi:foreign-slot-value out-buf-desc '(:struct aacenc-buf-desc) 'buf-sizes) out-buf-size-ptr) (setf (cffi:foreign-slot-value out-buf-desc '(:struct aacenc-buf-desc) 'buf-el-sizes) out-buf-el-size) (setf (cffi:foreign-slot-value in-args '(:struct aacenc-in-args) 'num-in-samples) (* num-samples channels)) (setf (cffi:foreign-slot-value in-args '(:struct aacenc-in-args) 'num-ancillary-bytes) 0) (let ((result (aac-enc-encode handle in-buf-desc out-buf-desc in-args out-args))) (unless (zerop result) (error 'encoding-error :format :aac :message (format nil "aacEncEncode failed: ~A" result))) (let ((bytes-written (cffi:foreign-slot-value out-args '(:struct aacenc-out-args) 'num-out-bytes))) (if (> bytes-written 0) (let ((result-vec (make-array bytes-written :element-type '(unsigned-byte 8)))) (loop for i below bytes-written do (setf (aref result-vec i) (cffi:mem-aref out-buf :unsigned-char i))) result-vec) (make-array 0 :element-type '(unsigned-byte 8)))))))))