129 lines
7.6 KiB
Common Lisp
129 lines
7.6 KiB
Common Lisp
(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)))))))))
|