Compare commits
2 Commits
98413ccf5f
...
c1efdca680
| Author | SHA1 | Date | |
|---|---|---|---|
| c1efdca680 | |||
| 17a4ddb661 |
6
info.rkt
6
info.rkt
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
(define scribblings
|
(define scribblings
|
||||||
'(
|
'(
|
||||||
("scrbl/racket-sound.scrbl" () (library) "racket-sound")
|
;("scrbl/racket-sound.scrbl" () (library) "racket-sound")
|
||||||
("scrbl/liboa.scrbl" () (library) "racket-sound/liboa/libao.rkt")
|
;("scrbl/liboa.scrbl" () (library) "racket-sound/liboa/libao.rkt")
|
||||||
("scrbl/flac-decoder.scrbl" () (library) "racket-sound/libflac/flac-decoder.rkt")
|
("scrbl/flac-decoder.scrbl" () (library) "racket-sound/libflac/flac-decoder.rkt")
|
||||||
("scrbl/taglib.scrbl" () (library) "racket-sound/libtag/taglib.rkt")
|
;("scrbl/taglib.scrbl" () (library) "racket-sound/libtag/taglib.rkt")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
3
scrbl/.gitignore
vendored
Normal file
3
scrbl/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.html
|
||||||
|
*.js
|
||||||
|
*.css
|
||||||
155
scrbl/flac-decoder.scrbl
Normal file
155
scrbl/flac-decoder.scrbl
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require racket/base
|
||||||
|
(for-label racket/base
|
||||||
|
racket/path
|
||||||
|
"../flac-decoder.rkt"
|
||||||
|
"../flac-definitions.rkt"))
|
||||||
|
|
||||||
|
@title{flac-decoder}
|
||||||
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
|
@defmodule["flac-decoder.rkt"]
|
||||||
|
|
||||||
|
This module provides a small decoder interface on top of the FLAC
|
||||||
|
FFI layer. It opens a decoder for a file, reads stream metadata,
|
||||||
|
reads audio frames, exposes the current decoder state, and allows
|
||||||
|
an active read loop to be stopped. It also re-exports the bindings
|
||||||
|
from @racketmodname["flac-definitions.rkt"].
|
||||||
|
|
||||||
|
A decoder handle stores the native decoder handler together with
|
||||||
|
optional callbacks for stream metadata and decoded audio.
|
||||||
|
|
||||||
|
@section{Procedures}
|
||||||
|
|
||||||
|
@defproc[(flac-open [flac-file* (or/c path? string?)]
|
||||||
|
[cb-stream-info (or/c procedure? #f)]
|
||||||
|
[cb-audio (or/c procedure? #f)])
|
||||||
|
(or/c flac-handle? #f)]{
|
||||||
|
|
||||||
|
Opens a FLAC decoder for @racket[flac-file*]. If a path is given,
|
||||||
|
it is converted with @racket[path->string]. If the file does not
|
||||||
|
exist, the result is @racket[#f].
|
||||||
|
|
||||||
|
Otherwise a native decoder handler is created with
|
||||||
|
@racket[flac-ffi-decoder-handler], initialized with the file, and
|
||||||
|
wrapped in a @racket[flac-handle]. The given callbacks are stored
|
||||||
|
in the handle.
|
||||||
|
|
||||||
|
When metadata of type @racket['streaminfo] is processed and
|
||||||
|
@racket[cb-stream-info] is a procedure, it is called with a
|
||||||
|
@racket[flac-stream-info] value.
|
||||||
|
|
||||||
|
When decoded audio data is processed and @racket[cb-audio] is a
|
||||||
|
procedure, it is called as
|
||||||
|
@racket[(cb-audio header buffers)], where @racket[header] is a
|
||||||
|
mutable hash containing the frame header fields plus
|
||||||
|
@racket['duration], and @racket[buffers] is the decoded channel
|
||||||
|
data returned by the FFI layer.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(flac-stream-state [handle flac-handle?])
|
||||||
|
(or/c 'search-for-metadata
|
||||||
|
'read-metadata
|
||||||
|
'search-for-frame-sync
|
||||||
|
'read-frames
|
||||||
|
'end-of-stream
|
||||||
|
'ogg-error
|
||||||
|
'seek-error
|
||||||
|
'aborted
|
||||||
|
'memory-allocation-error
|
||||||
|
'uninitialized
|
||||||
|
'end-of-link)]{
|
||||||
|
|
||||||
|
Returns the current decoder state reported by the native decoder
|
||||||
|
handler.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(flac-read [handle flac-handle?])
|
||||||
|
(or/c 'stopped-reading
|
||||||
|
'end-of-stream)]{
|
||||||
|
|
||||||
|
Reads the stream by repeatedly calling the native decoder with
|
||||||
|
@racket['process-single].
|
||||||
|
|
||||||
|
Before reading starts, the handle fields @racket[stop-reading]
|
||||||
|
and @racket[reading] are set to @racket[#f] and @racket[#t]. If a
|
||||||
|
stop has been requested with @racket[flac-stop], reading ends
|
||||||
|
with @racket['stopped-reading] and @racket[reading] is reset to
|
||||||
|
@racket[#f].
|
||||||
|
|
||||||
|
Whenever pending metadata is available, it is processed with
|
||||||
|
@racket[process-meta]. For metadata of type
|
||||||
|
@racket['streaminfo], a @racket[flac-stream-info] value is
|
||||||
|
constructed, stored in the handle, and passed to the
|
||||||
|
stream-info callback.
|
||||||
|
|
||||||
|
Whenever pending frame data is available, it is processed with
|
||||||
|
@racket[process-frame]. The frame header is converted to a
|
||||||
|
mutable hash, extended with a @racket['duration] entry taken
|
||||||
|
from @racket[flac-duration], and passed together with the
|
||||||
|
decoded buffers to the audio callback.
|
||||||
|
|
||||||
|
For each processed frame, the module also updates
|
||||||
|
@racket[last-buffer], @racket[last-buf-len], and @racket[kinds].
|
||||||
|
|
||||||
|
The procedure prints diagnostic messages for state changes,
|
||||||
|
metadata, stream errors, and stop handling.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(flac-read-meta [handle flac-handle?])
|
||||||
|
(or/c flac-stream-info? #f)]{
|
||||||
|
|
||||||
|
Advances the decoder until the state becomes one of
|
||||||
|
@racket['read-metadata], @racket['end-of-stream],
|
||||||
|
@racket['aborted], @racket['memory-allocation-error], or
|
||||||
|
@racket['uninitialized].
|
||||||
|
|
||||||
|
If the resulting state is @racket['read-metadata], pending
|
||||||
|
metadata is processed and the stored stream info is returned.
|
||||||
|
Otherwise the result is @racket[#f].
|
||||||
|
|
||||||
|
Only metadata of type @racket['streaminfo] is converted into a
|
||||||
|
@racket[flac-stream-info] value by this module.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(flac-stop [handle flac-handle?]) void?]{
|
||||||
|
|
||||||
|
Requests termination of an active @racket[flac-read] loop by
|
||||||
|
setting the handle field @racket[stop-reading] to @racket[#t].
|
||||||
|
The procedure then waits until the handle field
|
||||||
|
@racket[reading] becomes @racket[#f], sleeping for 10 ms between
|
||||||
|
checks.
|
||||||
|
|
||||||
|
The procedure prints timing information before and after the
|
||||||
|
wait.
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Diagnostic bindings}
|
||||||
|
|
||||||
|
@defthing[kinds hash?]{
|
||||||
|
|
||||||
|
A mutable hash used to record the frame number kinds encountered
|
||||||
|
during decoding. The keys are the values found in the
|
||||||
|
frame-header field @racket['number-type].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defthing[last-buffer (or/c #f list?)]{
|
||||||
|
|
||||||
|
The most recently decoded buffer set produced by frame
|
||||||
|
processing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defthing[last-buf-len (or/c #f exact-integer?)]{
|
||||||
|
|
||||||
|
The block size of the most recently processed frame.
|
||||||
|
}
|
||||||
|
|
||||||
|
@section{Notes}
|
||||||
|
|
||||||
|
The frame-header hash passed to the audio callback is produced
|
||||||
|
by @racket[flac-ffi-frame-header]. In this module it is extended
|
||||||
|
with a @racket['duration] field before the callback is called.
|
||||||
|
|
||||||
|
All bindings from @racketmodname["flac-definitions.rkt"] are
|
||||||
|
re-exported.
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
tags-year
|
tags-year
|
||||||
tags-genre
|
tags-genre
|
||||||
tags-track
|
tags-track
|
||||||
|
tags-composer
|
||||||
|
tags-disc-number
|
||||||
|
tags-album-artist
|
||||||
|
|
||||||
tags-length
|
tags-length
|
||||||
tags-sample-rate
|
tags-sample-rate
|
||||||
@@ -152,6 +155,8 @@
|
|||||||
[(eq? v 'bit-rate) bit-rate]
|
[(eq? v 'bit-rate) bit-rate]
|
||||||
[(eq? v 'channels) channels]
|
[(eq? v 'channels) channels]
|
||||||
[(eq? v 'keys) (hash-keys key-store)]
|
[(eq? v 'keys) (hash-keys key-store)]
|
||||||
|
[(eq? v 'album-artist) album-artist]
|
||||||
|
[(eq? v 'disc-number) disc-number]
|
||||||
[(eq? v 'val)
|
[(eq? v 'val)
|
||||||
(if (null? args)
|
(if (null? args)
|
||||||
#f
|
#f
|
||||||
@@ -207,6 +212,8 @@
|
|||||||
(tags-comment 'comment)
|
(tags-comment 'comment)
|
||||||
(tags-genre 'genre)
|
(tags-genre 'genre)
|
||||||
(tags-composer 'composer)
|
(tags-composer 'composer)
|
||||||
|
(tags-album-artist 'album-artist)
|
||||||
|
(tags-disc-number 'disc-number)
|
||||||
(tags-year 'year)
|
(tags-year 'year)
|
||||||
(tags-track 'track)
|
(tags-track 'track)
|
||||||
|
|
||||||
@@ -246,6 +253,8 @@
|
|||||||
(define (tags-picture->ext tags)
|
(define (tags-picture->ext tags)
|
||||||
(let ((mt (tags-picture->mimetype tags)))
|
(let ((mt (tags-picture->mimetype tags)))
|
||||||
(cond
|
(cond
|
||||||
|
((eq? mt #f)
|
||||||
|
#f)
|
||||||
((or (string-suffix? mt "/jpeg") (string-suffix? mt "/jpg"))
|
((or (string-suffix? mt "/jpeg") (string-suffix? mt "/jpg"))
|
||||||
'jpg)
|
'jpg)
|
||||||
((string-suffix? mt "/png")
|
((string-suffix? mt "/png")
|
||||||
|
|||||||
Reference in New Issue
Block a user