1 ! Copyright (C) 2007, 2010 Chris Double, Joe Groff.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types alien.data audio.engine
4 byte-arrays classes.struct combinators destructors fry io
5 io.files io.encodings.binary kernel libc locals make math
6 math.order math.parser ogg ogg.vorbis sequences
7 specialized-arrays specialized-vectors ;
8 QUALIFIED-WITH: alien.c-types c
9 SPECIALIZED-ARRAYS: c:float c:void* ;
10 SPECIALIZED-VECTOR: c:short
13 TUPLE: vorbis-stream < disposable
17 { sync-state ogg-sync-state }
19 { stream-state ogg-stream-state }
21 { dsp-state vorbis-dsp-state }
22 { block vorbis-block }
23 { comment vorbis-comment }
24 { temp-state ogg-stream-state }
25 { #vorbis-headers integer initial: 0 } ;
27 CONSTANT: stream-buffer-size 4096
29 ERROR: ogg-error code ;
30 ERROR: vorbis-error code ;
31 ERROR: no-vorbis-in-ogg ;
34 : init-vorbis ( vorbis-stream -- )
35 [ sync-state>> ogg_sync_init drop ]
36 [ info>> vorbis_info_init ]
37 [ comment>> vorbis_comment_init ] tri ;
39 : sync-buffer ( vorbis-stream -- buffer size )
40 sync-state>> stream-buffer-size ogg_sync_buffer
41 stream-buffer-size ; inline
43 : read-bytes-into ( dest size stream -- len )
44 ! Read the given number of bytes from a stream
45 ! and store them in the destination byte array.
46 stream-read >byte-array dup length [ memcpy ] keep ;
48 : stream-into-buffer ( buffer size vorbis-stream -- len )
49 stream>> read-bytes-into ; inline
52 dup 0 < [ ogg-error ] [ drop ] if ; inline
54 : confirm-buffer ( len vorbis-stream -- ? )
55 '[ _ sync-state>> swap ogg_sync_wrote ?ogg-error ] keep zero? not ; inline
57 : buffer-data-from-stream ( vorbis-stream -- ? )
58 [ sync-buffer ] [ stream-into-buffer ] [ confirm-buffer ] tri ; inline
60 : queue-page ( vorbis-stream -- )
61 [ stream-state>> ] [ page>> ] bi ogg_stream_pagein drop ; inline
63 : retrieve-page ( vorbis-stream -- ? )
64 [ sync-state>> ] [ page>> ] bi ogg_sync_pageout 0 > ; inline
66 : (sync-pages) ( vorbis-stream ? -- ? )
68 [ drop [ queue-page ] [ t (sync-pages) ] bi ] [
69 over buffer-data-from-stream
70 [ (sync-pages) ] [ nip ] if
72 : sync-pages ( vorbis-stream -- ? )
73 f (sync-pages) ; inline
75 : standard-initial-header? ( vorbis-stream -- bool )
76 page>> ogg_page_bos zero? not ; inline
78 : ogg-stream-init ( vorbis-stream -- state )
80 [ page>> ogg_page_serialno ogg_stream_init ?ogg-error ] bi ; inline
82 : ogg-stream-pagein ( state vorbis-stream -- )
83 page>> ogg_stream_pagein drop ; inline
85 : ogg-stream-packetout ( state vorbis-stream -- )
86 packet>> ogg_stream_packetout drop ; inline
88 : decode-packet ( vorbis-stream -- state )
89 [ ogg-stream-init ] keep
90 [ ogg-stream-pagein ] [ ogg-stream-packetout ] [ drop ] 2tri ; inline
92 : vorbis-header? ( vorbis-stream -- ? )
93 [ info>> ] [ comment>> ] [ packet>> ] tri vorbis_synthesis_headerin 0 >= ; inline
95 : is-initial-vorbis-packet? ( vorbis-stream -- ? )
96 dup #vorbis-headers>> zero? [ vorbis-header? ] [ drop f ] if ; inline
98 : save-initial-vorbis-header ( state vorbis-stream -- )
99 [ stream-state>> swap dup byte-length memcpy ]
100 [ 1 >>#vorbis-headers drop ] bi ; inline
102 : drop-initial-other-header ( state vorbis-stream -- )
103 swap ogg_stream_clear 2drop ; inline
105 : process-initial-header ( vorbis-stream -- ? )
106 dup standard-initial-header? [
107 [ decode-packet ] keep
108 dup is-initial-vorbis-packet?
109 [ save-initial-vorbis-header ]
110 [ drop-initial-other-header ] if
114 : parse-initial-headers ( vorbis-stream -- )
116 [ dup process-initial-header [ parse-initial-headers ] [ queue-page ] if ]
117 [ dup buffer-data-from-stream [ parse-initial-headers ] [ drop ] if ] if ;
119 : have-required-vorbis-headers? ( vorbis-stream -- ? )
120 #vorbis-headers>> 1 2 between? not ; inline
122 : ?vorbis-error ( code -- )
123 [ vorbis-error ] unless-zero ; inline
125 : get-remaining-vorbis-header-packet ( player -- ? )
126 [ stream-state>> ] [ packet>> ] bi ogg_stream_packetout {
127 { [ dup 0 < ] [ vorbis-error ] }
128 { [ dup zero? ] [ drop f ] }
132 : decode-remaining-vorbis-header-packet ( vorbis-stream -- )
133 [ info>> ] [ comment>> ] [ packet>> ] tri vorbis_synthesis_headerin ?vorbis-error ;
135 : parse-remaining-vorbis-headers ( vorbis-stream -- )
136 dup have-required-vorbis-headers? not [
137 dup get-remaining-vorbis-header-packet [
138 [ decode-remaining-vorbis-header-packet ]
139 [ [ 1 + ] change-#vorbis-headers drop ]
140 [ parse-remaining-vorbis-headers ] tri
144 : parse-remaining-headers ( vorbis-stream -- )
145 dup have-required-vorbis-headers? not [
146 [ parse-remaining-vorbis-headers ]
147 [ dup retrieve-page [ queue-page ] [ buffer-data-from-stream drop ] if ]
148 [ parse-remaining-headers ] tri
151 : init-vorbis-codec ( vorbis-stream -- )
152 [ [ dsp-state>> ] [ info>> ] bi vorbis_synthesis_init drop ]
153 [ [ dsp-state>> ] [ block>> ] bi vorbis_block_init drop ] bi ;
155 : initialize-decoder ( vorbis-stream -- )
156 dup #vorbis-headers>> zero?
158 [ init-vorbis-codec ] if ;
160 : get-pending-decoded-audio ( vorbis-stream -- pcm len )
161 dsp-state>> f void* <ref> [ vorbis_synthesis_pcmout ] keep void* deref swap ;
163 : float>short-sample ( float -- short )
164 -32767.5 * 0.5 - >integer -32768 32767 clamp ; inline
166 :: write-pcm-to-buffer ( vorbis-stream offset pcm len -- offset' )
167 vorbis-stream buffer>> :> buffer
168 buffer length -1 shift :> buffer-length
169 offset -1 shift :> sample-offset
170 buffer buffer-length c:short <c-direct-array> sample-offset short-vector boa :> short-buffer
171 vorbis-stream info>> channels>> :> #channels
172 buffer-length sample-offset - #channels /i :> max-len
173 len max-len min :> len'
174 pcm #channels void* <c-direct-array> :> channel*s
176 len' <iota> [| sample |
177 #channels <iota> [| channel |
178 channel channel*s nth len c:float <c-direct-array>
180 float>short-sample short-buffer push
183 vorbis-stream dsp-state>> len' vorbis_synthesis_read drop
184 short-buffer length 1 shift ; inline
186 : queue-audio ( vorbis-stream -- ? )
187 dup [ stream-state>> ] [ packet>> ] bi ogg_stream_packetout 0 > [
188 dup [ block>> ] [ packet>> ] bi vorbis_synthesis 0 = [
189 [ dsp-state>> ] [ block>> ] bi vorbis_synthesis_blockin drop
193 : (decode-audio) ( vorbis-stream offset -- offset' )
194 over get-pending-decoded-audio dup 0 > [ write-pcm-to-buffer ] [
195 2drop over queue-audio [ (decode-audio) ] [ nip ] if
198 : decode-audio ( vorbis-stream offset -- offset' )
199 2dup (decode-audio) {
201 [ 3dup [ buffer>> length ] [ drop ] [ ] tri* = ]
208 over sync-pages [ decode-audio ] [ nip ] if
215 :: <vorbis-stream> ( stream buffer-size -- vorbis-stream )
217 vorbis-stream new-disposable
219 buffer-size <byte-array> >>buffer
220 ogg-packet malloc-struct |free >>packet
221 ogg-sync-state malloc-struct |free >>sync-state
222 ogg-page malloc-struct |free >>page
223 ogg-stream-state malloc-struct |free >>stream-state
224 vorbis-info malloc-struct |free >>info
225 vorbis-dsp-state malloc-struct |free >>dsp-state
226 vorbis-block malloc-struct |free >>block
227 vorbis-comment malloc-struct |free >>comment
228 ogg-stream-state malloc-struct |free >>temp-state
231 [ parse-initial-headers ]
232 [ parse-remaining-headers ]
233 [ initialize-decoder ]
237 : read-vorbis-stream ( filename buffer-size -- vorbis-stream )
238 [ [ binary <file-reader> |dispose ] dip <vorbis-stream> ] with-destructors ; inline
240 M: vorbis-stream dispose*
242 [ temp-state>> [ free ] when* ]
243 [ comment>> [ [ vorbis_comment_clear ] [ free ] bi ] when* ]
244 [ block>> [ free ] when* ]
245 [ dsp-state>> [ free ] when* ]
246 [ info>> [ [ vorbis_info_clear ] [ free ] bi ] when* ]
247 [ stream-state>> [ free ] when* ]
248 [ page>> [ free ] when* ]
249 [ sync-state>> [ free ] when* ]
250 [ packet>> [ free ] when* ]
251 [ stream>> [ dispose ] when* ]
254 M: vorbis-stream generator-audio-format
255 [ info>> channels>> ] [ drop 16 ] [ info>> rate>> ] tri ;
256 M: vorbis-stream generate-audio
257 [ buffer>> ] [ 0 decode-audio ] bi ;