major refactor

main
Steve Ayerhart 2022-10-27 23:02:46 -04:00
parent 008d217286
commit e3c7e3e05a
No known key found for this signature in database
GPG Key ID: 4CB33EB9BB156C97
5 changed files with 192 additions and 163 deletions

View File

@ -12,6 +12,7 @@
#:use-module (ice-9 receive)
#:export (read-flac-frame))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#section-10.1-1
(define (read/assert-frame-sync-code)
(unless (= #b111111111111100 (flac-read-uint 15))
#f))
@ -32,6 +33,7 @@
((= raw #b1010) 3)
(else #f))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-block-size-bits
(define (decode-block-size raw)
(cond
((= raw #b0000) 'reserved)
@ -41,6 +43,7 @@
((= raw #b0111) (+ 1 (flac-read-uint 16)))
((between? raw #b1000 #b1111) (* 256 (expt 2 (- raw 8))))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-sample-rate-bits
(define (decode-sample-rate stream-info raw)
(case raw
((#b0000) (stream-info-sample-rate stream-info))
@ -60,6 +63,15 @@
((#b1110) (* 10 (flac-read-uint 16))) ; sample rate in tens of Hz
((#b1111) 'invalid))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-coded-number
(define (flac-read-coded-number)
(let coded-number-loop ((coded-sample-number (flac-read-uint 8)))
(if (< coded-sample-number #b11000000)
coded-sample-number
(begin
(flac-read-uint 8)
(coded-number-loop (bitwise-and (bitwise-arithmetic-shift coded-sample-number 1) #xff))))))
(define (decode-bits-per-sample stream-info raw)
(case raw
((#b000) (stream-info-bits-per-sample stream-info))
@ -91,19 +103,27 @@
('mid (if (= channel 1) 1 0))
(_ 0))))
(define (read-subframe-constant blocksize sample-depth wasted-bits)
(let ((subframe (%make-subframe-constant (make-list blocksize (bitwise-arithmetic-shift (flac-read-sint sample-depth) wasted-bits)))))
(values subframe (subframe-constant-value subframe))))
(define (read-subframe-verbatim blocksize sample-depth wasted-bits)
(let ((subframe (%make-subframe-verbatim
(list-ec (: b blocksize) (bitwise-arithmetic-shift (flac-read-sint sample-depth) wasted-bits)))))
(values subframe (subframe-verbatim-value subframe))))
(define (read-entropy-coding-method-info)
(values (case (flac-read-uint 2) [(#b00) 'rice] [(#b01) 'rice2]) (flac-read-uint 4)))
(define (read-residual-partiioned-rice blocksize predictor-order)
(define (restore-linear-prediction warmup residuals coefficients order shift)
(fold (λ (residual samples)
(append!
samples
(list
(+ residual
(bitwise-arithmetic-shift-right
(fold (λ (residual coefficient predictor)
(+ predictor (* residual coefficient)))
0
(take-right samples order)
coefficients)
shift)))))
warmup
residuals))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-coded-residual
(define (read-residual-partitioned-rice blocksize predictor-order)
(let-values (((coding-method partition-order) (read-entropy-coding-method-info)))
(let ((param-bits (match coding-method ('rice 4) ('rice2 5) (_ #f)))
(escape-param (match coding-method ('rice #xf) ('rice2 #x1f)))
@ -115,12 +135,7 @@
(raw-bits '())
(residual '()))
(if (>= partition partitions)
(values (%make-entropy-coding-method
coding-method
(%make-rice-partition
partition-order
(%make-entropy-coding-method-partitioned-rice-contents parameters raw-bits #f)))
residual)
residual
(let ((rice-parameter (flac-read-uint param-bits)))
(if (< rice-parameter escape-param)
(let ((count (if (= 0 partition) (- partition-samples predictor-order) partition-samples)))
@ -140,41 +155,6 @@
(make-list order 0)
(list-ec (: o order) (flac-read-sint num-bits))))))))))))))
(define (read-subframe-fixed predictor-order blocksize sample-depth)
(let ((warmup (list-ec (: o predictor-order) (flac-read-sint sample-depth)))
(fixed-coefficients '(() (1) (2 -1) (3 -3 1) (4 -6 4 -1))))
(let-values (((entropy-coding-method residual) (read-residual-partiioned-rice blocksize predictor-order)))
(values
(%make-subframe-fixed entropy-coding-method predictor-order warmup residual)
(restore-linear-prediction warmup residual (list-ref fixed-coefficients predictor-order) predictor-order 0)))))
(define (read-subframe-lpc lpc-order blocksize sample-depth)
(let* ((warmup (list-ec (: o lpc-order) (flac-read-sint sample-depth)))
(precision (+ 1 (flac-read-uint 4)))
(shift (flac-read-sint 5))
(coefs (reverse (list-ec (: o lpc-order) (flac-read-sint precision)))))
(let-values (((entropy-coding-method residual) (read-residual-partiioned-rice blocksize lpc-order)))
(values
(%make-subframe-lpc entropy-coding-method lpc-order precision shift coefs warmup residual)
(restore-linear-prediction warmup residual coefs lpc-order shift)))))
(define (restore-linear-prediction warmup residuals coefficients order shift)
(fold (λ (residual samples)
(append
samples
(list
(+ residual
(bitwise-arithmetic-shift-right
(fold (λ (residual coefficient predictor)
(+ predictor (* residual coefficient)))
0
(take-right samples order)
coefficients)
shift)))))
warmup
residuals))
;;; 000000 constant
;;; 000001 verbatim
;;; 00001x reserved
@ -191,12 +171,33 @@
((between? raw #b100000 #b111111) (values (+ 1 (bit-extract raw 0 5)) 'lpc))
(else (values #f #f)))))
; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-interchannel-decorrelation
(define (stereo-decorrelation samples channel-assignment)
(match channel-assignment
('independent samples)
('left (list (first samples) (map! - (first samples) (second samples))))
('right (list (second samples) (map! + (first samples) (second samples))))
('mid (fold
(λ (sample-0 sample-1 samples)
(let* ((prev-samples-0 (first samples))
(prev-samples-1 (second samples))
(side sample-1)
(right (- sample-0 (bitwise-arithmetic-shift-right side 1))))
(list
(append prev-samples-0 (list right))
(append prev-samples-1 (list (+ right side))))))
'(() ())
(first samples)
(second samples)))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-subframe-header
(define (read-subframe-header)
(read/assert-subframe-sync)
(receive (order type)
(read-subframe-type)
(%make-subframe-header type order (read-subframe-wasted-bits))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-subframes
(define (read-subframe frame-header channel)
(let* ((subframe-header (read-subframe-header))
(wasted-bits (subframe-header-wasted-bits subframe-header))
@ -207,70 +208,49 @@
(frame-header-channel-assignment frame-header)
channel))
(blocksize (frame-header-blocksize frame-header)))
(let-values (((subframe samples)
(match (subframe-header-subframe-type subframe-header)
('constant (read-subframe-constant blocksize sample-depth wasted-bits))
('verbatim (read-subframe-verbatim blocksize sample-depth wasted-bits))
('fixed (read-subframe-fixed predictor-order blocksize sample-depth))
('lpc (read-subframe-lpc predictor-order blocksize sample-depth)))))
(values
(%make-subframe subframe-header subframe)
samples))))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#section-5.3-2.1.1
(define (read-subframe-verbatim blocksize sample-depth wasted-bits)
(list-ec (: b blocksize) (bitwise-arithmetic-shift (flac-read-sint sample-depth) wasted-bits)))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#section-5.3-2.2.1
(define (read-subframe-constant blocksize sample-depth wasted-bits)
(make-list blocksize (bitwise-arithmetic-shift (flac-read-sint sample-depth) wasted-bits)))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-fixed-predictor-subframe
(define (read-subframe-fixed predictor-order blocksize sample-depth)
(let ((warmup (list-ec (: o predictor-order) (flac-read-sint sample-depth)))
(fixed-coefficients '(() (1) (2 -1) (3 -3 1) (4 -6 4 -1)))
(residual (read-residual-partitioned-rice blocksize predictor-order)))
(restore-linear-prediction warmup residual (list-ref fixed-coefficients predictor-order) predictor-order 0)))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-linear-predictor-subframe
(define (read-subframe-lpc lpc-order blocksize sample-depth)
(let* ((warmup (list-ec (: o lpc-order) (flac-read-sint sample-depth)))
(precision (+ 1 (flac-read-uint 4)))
(shift (flac-read-sint 5))
(coefs (reverse (list-ec (: o lpc-order) (flac-read-sint precision))))
(residual (read-residual-partitioned-rice blocksize lpc-order)))
(restore-linear-prediction warmup residual coefs lpc-order shift)))
;; TODO: clean up the channel decorrelation this is kind of ugly
(define (read-subframes stream-info frame-header)
(let ((channels (stream-info-channels stream-info))
(channel-assignment (frame-header-channel-assignment frame-header)))
(if (eq? 'independent channel-assignment)
; do nothing loop over channels
(let channel-loop ((channel 0)
(subframes '())
(samples '()))
(if (>= channel channels)
(values subframes samples)
(let-values (((subframe subframe-samples) (read-subframe frame-header channel)))
(channel-loop (+ 1 channel)
(append subframes (list subframe))
(append samples (list subframe-samples))))))
(let-values (((channel-0-subframe channel-0-samples) (read-subframe frame-header 0))
((channel-1-subframe channel-1-samples) (read-subframe frame-header 1)))
(match channel-assignment
('left (values
(list channel-0-subframe channel-1-subframe)
(list channel-0-samples (map - channel-0-samples channel-1-samples))))
('right (values
(list channel-0-subframe channel-1-subframe)
(list (map + channel-0-samples channel-1-samples) channel-1-samples)))
('mid (values
(list channel-0-subframe channel-1-subframe)
(fold
(λ (channel-0 channel-1 samples)
(let* ((channel-0-samples (first samples))
(channel-1-samples (second samples))
(side channel-1)
(right (- channel-0 (bitwise-arithmetic-shift-right side 1))))
(list
(append channel-0-samples (list right))
(append channel-1-samples (list (+ right side))))))
'(() ())
channel-0-samples
channel-1-samples))))))))
(map! (λ (channel) (read-subframe frame-header channel)) (iota channels))))
;(define (read-subframes stream-info frame-header)
; (let ((channels (stream-info-channels stream-info)))
; (let subframe-loop ((channel 0)
; (subframes '())
; (samples '()))
; (if (>= channel channels)
; (begin
; (align-to-byte)
; (values subframes samples))
; (let-values (((subframe subframe-samples)
; (read-subframe frame-header channel)))
; (subframe-loop (+ 1 channel)
; (cons subframe subframes)
; (cons subframe-samples samples)))))))
e; TODO: actually verify the checksum
(define (read-frame-footer)
(flac-read-uint 16))
;;; https://www.ietf.org/archive/id/draft-ietf-cellar-flac-07.html#name-frame-header
(define-public (read-frame-header stream-info)
(read/assert-frame-sync-code)
(let* ((blocking-strategy (decode-blocking-strategy (flac-read-uint 1)))
@ -292,13 +272,9 @@
frame/sample-number
crc)))
;; TODO: actually verify the checksum
(define (read-frame-footer)
(flac-read-uint 16))
(define (read-flac-frame stream-info)
(let ((header (read-frame-header stream-info)))
(let-values (((subframes samples) (read-subframes stream-info header)))
(let* ((header (read-frame-header stream-info))
(samples (read-subframes stream-info header)))
(align-to-byte)
(let ((footer (read-frame-footer)))
(%make-frame header subframes footer samples)))))
(%make-frame header footer samples))))

View File

@ -69,6 +69,8 @@
stream-info-bits-per-sample stream-info-samples stream-info-md5
make-metadata-vorbis-comment
vorbis-comment-vendor
vorbis-comment-comments
make-flac-metadata
flac-metadata-stream-info set-flac-metadata-stream-info!
@ -185,10 +187,9 @@
(crc frame-footer-crc))
(define-record-type <frame>
(%make-frame header subframes footer samples)
(%make-frame header footer samples)
frame?
(header frame-header)
(subframes frame-subframes)
(footer frame-footer)
(samples frame-samples))
@ -224,10 +225,10 @@
(samples stream-info-samples)
(md5 stream-info-md5))
(set-record-type-printer!
<stream-info>
(λ (record port)
(format port "#<stream-info>")))
;(set-record-type-printer!
; <stream-info>
; (λ (record port)
; (format port "#<stream-info>")))
(define-record-type <seek-table>
(make-metadata-seek-table seek-points)
@ -330,10 +331,10 @@
(λ (record port)
(format port "#<<picture> type: ~a mime-type: ~a>" (picture-type record) (picture-mime-type record))))
(set-record-type-printer!
<frame>
(λ (record port)
(format port "#<<frame> header: ~a>" (frame-header record))))
; (set-record-type-printer!
; <frame>
; (λ (record port)
; (format port "#<<frame> header: ~a>" (frame-header record))))
(define-record-type <flac-metadata>
(make-flac-metadata stream-info padding application seek-table vorbis-comment cuesheet pictures)

View File

@ -2,16 +2,18 @@
#:use-module (flac format)
#:use-module (flac reader)
#:use-module (bytestructures guile)
#:use-module (ice-9 match)
#:use-module (ice-9 optargs)
#:use-module (ice-9 receive)
#:use-module (rnrs enums)
#:use-module (rnrs bytevectors)
#:use-module (srfi srfi-1)
#:export (read-flac-metadata flac-metadata flac-file-metadata))
(define (read-metadata-block-header)
(make-metadata-block-header
(define-public (read-metadata-block-header)
(values
(= 1 (flac-read-uint 1))
(list-ref (enum-set->list flac-metadata-type) (flac-read-uint 7))
(flac-read-uint 24)))
@ -79,29 +81,26 @@
(define (read-flac-metadata)
(flac-read/assert-magic)
(let metadata-loop ((metadata (make-flac-metadata #f #f #f #f #f #f #f))
(header (read-metadata-block-header)))
(if (metadata-block-header-last? header)
(read-metadata-block metadata (metadata-block-header-length header) (metadata-block-header-type header))
(metadata-loop (read-metadata-block
metadata
(metadata-block-header-length header)
(metadata-block-header-type header))
(read-metadata-block-header)))))
(let metadata-loop ((metadata (make-flac-metadata #f #f #f #f #f #f #f)))
(receive (last-block? block-type block-length)
(read-metadata-block-header)
(if last-block?
(read-metadata-block metadata block-length block-type)
(metadata-loop (read-metadata-block metadata block-length block-type))))))
; FIXME: bail early if not in type
(define (read-flac-metadata-type type)
(flac-read/assert-magic)
(let metadata-loop ((header (read-metadata-block-header)))
(if (or (metadata-block-header-last? header)
(equal? type (metadata-block-header-type header)))
(let metadata-loop ()
(receive (last-block? block-type block-length)
(read-metadata-block-header)
(if (or last-block? (equal? type block-type))
(match type
('stream-info (read-metadata-block-stream-info))
('vorbis-comment (read-metadata-block-vorbis-comment))
(_ #f))
(begin
(flac-read-bytes (metadata-block-header-length header))
(metadata-loop (read-metadata-block-header))))))
(flac-read-bytes block-length)
(metadata-loop))))))
(define* (flac-metadata port #:optional (type #f))
(with-flac-input-port port
@ -113,4 +112,5 @@
(define* (flac-file-metadata filename #:optional (type #f))
(with-flac-input-port (open-input-file filename #:binary #t)
(λ ()
(flac-read/assert-magic)
(flac-metadata (current-input-port) type))))

View File

@ -20,6 +20,7 @@
current-flac-reader))
(define current-flac-reader (make-parameter #f))
(define default-bit-reader-capacity 65536)
;;; TODO: redo api? callback based?

View File

@ -3,20 +3,21 @@
#:use-module (flac reader)
#:use-module (flac format)
#:use-module (flac metadata)
#:use-module (flac decoder)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 binary-ports)
#:use-module (srfi srfi-64))
(define example-1
(define-public example-1
#vu8(#x66 #x4c #x61 #x43 #x80 #x00 #x00 #x22 #x10 #x00 #x10 #x00
#x00 #x00 #x0f #x00 #x00 #x0f #x0a #xc4 #x42 #xf0 #x00 #x00
#x00 #x01 #x3e #x84 #xb4 #x18 #x07 #xdc #x69 #x03 #x07 #x58
#x6a #x3d #xad #x1a #x2e #x0f #xff #xf8 #x69 #x18 #x00 #x00
#xbf #x03 #x58 #xfd #x03 #x12 #x8b #xaa #x9a))
(define example-2
(define-public example-2
#vu8(#x66 #x4c #x61 #x43 #x00 #x00 #x00 #x22 #x00 #x10 #x00 #x10
#x00 #x00 #x17 #x00 #x00 #x44 #x0a #xc4 #x42 #xf0 #x00 #x00
#x00 #x13 #xd5 #xb0 #x56 #x49 #x75 #xe9 #x8b #x8d #x8b #x93
@ -52,13 +53,63 @@
(with-tests
"RFC Examples"
(test-group "Example 1"
(with-flac-input-port (open-bytevector-input-port example-1)
(with-flac-input-port
(open-bytevector-input-port example-1)
(λ ()
(define expected-stream-info
(make-metadata-stream-info 4096 4096 15 15 44100 2 16 1 #vu8(62 132 180 24 7 220 105 3 7 88 106 61 173 26 46 15)))
(define expected-metadata
(make-flac-metadata expected-stream-info #f #f #f #f #f '()))
(let* ((expected-metadata (make-flac-metadata
(make-metadata-stream-info
4096 4096 15 15 44100 2 16 1
#vu8(62 132 180 24 7 220 105 3 7 88 106 61 173 26 46 15))
#f #f #f #f #f #f))
(expected-frame (%make-frame
(make-frame-header 'fixed 1 44100 'independent 16 0 191)
43674
'((25588) (10416))))
(actual-metadata (read-flac-metadata))
(actual-frame (read-flac-frame (flac-metadata-stream-info actual-metadata))))
(test-group "Metadata"
(let ((actual-metadata ((@@ (flac metadata) read-flac-metadata))))
(test-equal "stream info" (flac-metadata-stream-info actual-metadata) expected-stream-info)
(test-equal "metadata" actual-metadata expected-metadata)))))))
(test-equal
"stream info"
(flac-metadata-stream-info actual-metadata)
(flac-metadata-stream-info expected-metadata)))
(test-group "Frame"
(test-equal "first frame" expected-frame expected-frame))))))
(test-group "Example 2"
(with-flac-input-port
(open-bytevector-input-port example-2)
(λ ()
(let* ((expected-stream-info (make-metadata-stream-info
16 16 23 68 44100 2 16 19
#vu8(213 176 86 73 117 233 139 141 139 147 4 34 117 123 129 3)))
(expected-vorbis-comment (make-metadata-vorbis-comment
"reference libFLAC 1.3.3 20190804"
(list '("TITLE" "שלום"))))
(expected-padding (make-metadata-padding 6))
(expected-seek-table (make-metadata-seek-table
(list (make-metadata-seek-point 0 0 16))))
(expected-first-frame (%make-frame
(make-frame-header 'fixed 16 44100 'right 16 0 153)
47120
'((4302 7496 6199 7427 6484 7436 6740 7508 6984 7583 7182 -5990 -6306 -6032 -6299 -6165)
(6070 10545 8743 10449 9143 10463 9502 10569 9840 10680 10113 -8428 -8895 -8476 -8896 -8653))))
(expected-second-frame (%make-frame
(make-frame-header 'fixed 3 44100 'independent 16 1 164)
4912
'((-15486 -15349 -16054)
(-9072 -8958 -9410))))
(actual-metadata (read-flac-metadata))
(actual-stream-info (flac-metadata-stream-info actual-metadata))
(actual-first-frame (read-flac-frame actual-stream-info))
(actual-second-frame (read-flac-frame actual-stream-info)))
(test-group "Metadata"
(test-equal "stream info" actual-stream-info expected-stream-info)
(test-equal "vorbis comment" (flac-metadata-vorbis-comment actual-metadata) expected-vorbis-comment)
(test-equal "padding" (flac-metadata-padding actual-metadata) expected-padding)
(test-equal "seek table" (flac-metadata-seek-table actual-metadata) expected-seek-table))
(test-group "Frames"
(test-equal "frame 1" actual-first-frame expected-first-frame)
(test-equal "frame 2" actual-second-frame expected-second-frame)))))))