diff --git a/extra/audio/audio.factor b/extra/audio/audio.factor new file mode 100644 index 0000000000..04df36ebd6 --- /dev/null +++ b/extra/audio/audio.factor @@ -0,0 +1,23 @@ +USING: accessors alien arrays combinators kernel math openal ; +IN: audio + +TUPLE: audio + { channels integer } + { sample-bits integer } + { sample-rate integer } + { size integer } + { data c-ptr } ; + +C: <audio> audio + +ERROR: format-unsupported-by-openal audio ; + +: openal-format ( audio -- format ) + dup [ channels>> ] [ sample-bits>> ] bi 2array { + { { 1 8 } [ drop AL_FORMAT_MONO8 ] } + { { 1 16 } [ drop AL_FORMAT_MONO16 ] } + { { 2 8 } [ drop AL_FORMAT_STEREO8 ] } + { { 2 16 } [ drop AL_FORMAT_STEREO16 ] } + [ drop format-unsupported-by-openal ] + } case ; + diff --git a/extra/audio/wav/wav.factor b/extra/audio/wav/wav.factor new file mode 100644 index 0000000000..6f8ee49395 --- /dev/null +++ b/extra/audio/wav/wav.factor @@ -0,0 +1,74 @@ +USING: alien.c-types alien.syntax audio combinators +combinators.short-circuit io io.binary io.encodings.binary +io.files io.streams.memory kernel locals sequences ; +IN: audio.wav + +CONSTANT: RIFF-MAGIC "RIFF" +CONSTANT: WAVE-MAGIC "WAVE" +CONSTANT: FMT-MAGIC "fmt " +CONSTANT: DATA-MAGIC "data" + +C-STRUCT: riff-chunk-header + { "char[4]" "id" } + { "uchar[4]" "size" } + ; + +C-STRUCT: riff-chunk + { "riff-chunk-header" "header" } + { "char[4]" "format" } + { "uchar[0]" "body" } + ; + +C-STRUCT: wav-fmt-chunk + { "riff-chunk-header" "header" } + { "uchar[2]" "audio-format" } + { "uchar[2]" "num-channels" } + { "uchar[4]" "sample-rate" } + { "uchar[4]" "byte-rate" } + { "uchar[2]" "block-align" } + { "uchar[2]" "bits-per-sample" } + ; + +C-STRUCT: wav-data-chunk + { "riff-chunk-header" "header" } + { "uchar[0]" "body" } + ; + +: read-chunk ( -- byte-array/f ) + 4 read [ 4 read le> [ <uint> ] [ read ] bi 3append ] [ f ] if* ; + +: id= ( chunk id -- ? ) + [ 4 memory>byte-array ] dip sequence= ; + +:: read-wav-chunks ( -- fmt data ) + f :> fmt! f :> data! + [ { [ fmt data and not ] [ read-chunk ] } 0&& dup ] + [ { + { [ dup FMT-MAGIC id= ] [ fmt! ] } + { [ dup DATA-MAGIC id= ] [ data! ] } + } cond ] while drop + fmt data ; + +ERROR: invalid-wav-file ; + +: verify-wav ( chunk -- ) + { [ RIFF-MAGIC id= ] [ riff-chunk-format WAVE-MAGIC id= ] } 1&& + [ invalid-wav-file ] unless ; + +: (read-wav) ( -- audio ) + read-wav-chunks + [ + [ wav-fmt-chunk-num-channels 2 memory>byte-array le> ] + [ wav-fmt-chunk-bits-per-sample 2 memory>byte-array le> ] + [ wav-fmt-chunk-sample-rate 4 memory>byte-array le> ] tri + ] [ + [ riff-chunk-header-size 4 memory>byte-array le> dup ] + [ wav-data-chunk-body ] bi swap memory>byte-array + ] bi* <audio> ; + +: read-wav ( filename -- audio ) + binary [ + read-chunk + [ verify-wav ] + [ riff-chunk-body <memory-stream> [ (read-wav) ] with-input-stream* ] bi + ] with-file-reader ; diff --git a/extra/game-loop/game-loop.factor b/extra/game-loop/game-loop.factor new file mode 100644 index 0000000000..8e7c7017d4 --- /dev/null +++ b/extra/game-loop/game-loop.factor @@ -0,0 +1,93 @@ +USING: accessors destructors kernel math math.order namespaces +system threads ; +IN: game-loop + +TUPLE: game-loop + { tick-length integer read-only } + delegate + { last-tick integer } + thread + { running? boolean } + { tick-number integer } + { frame-number integer } + { benchmark-time integer } + { benchmark-tick-number integer } + { benchmark-frame-number integer } ; + +GENERIC: tick* ( delegate -- ) +GENERIC: draw* ( tick-slice delegate -- ) + +SYMBOL: game-loop + +: since-last-tick ( loop -- milliseconds ) + last-tick>> millis swap - ; + +: tick-slice ( loop -- slice ) + [ since-last-tick ] [ tick-length>> ] bi /f 1.0 min ; + +CONSTANT: MAX-FRAMES-TO-SKIP 5 + +<PRIVATE + +: redraw ( loop -- ) + [ 1+ ] change-frame-number + [ tick-slice ] [ delegate>> ] bi draw* ; + +: tick ( loop -- ) + delegate>> tick* ; + +: increment-tick ( loop -- ) + [ 1+ ] change-tick-number + dup tick-length>> [ + ] curry change-last-tick + drop ; + +: ?tick ( loop count -- ) + dup zero? [ drop millis >>last-tick drop ] [ + over [ since-last-tick ] [ tick-length>> ] bi >= + [ [ drop increment-tick ] [ drop tick ] [ 1- ?tick ] 2tri ] + [ 2drop ] if + ] if ; + +: (run-loop) ( loop -- ) + dup running?>> + [ [ MAX-FRAMES-TO-SKIP ?tick ] [ redraw ] [ yield (run-loop) ] tri ] + [ drop ] if ; + +: run-loop ( loop -- ) + dup game-loop [ (run-loop) ] with-variable ; + +: benchmark-millis ( loop -- millis ) + millis swap benchmark-time>> - ; + +PRIVATE> + +: reset-loop-benchmark ( loop -- ) + millis >>benchmark-time + dup tick-number>> >>benchmark-tick-number + dup frame-number>> >>benchmark-frame-number + drop ; + +: benchmark-ticks-per-second ( loop -- n ) + [ tick-number>> ] [ benchmark-tick-number>> - ] [ benchmark-millis ] tri /f ; +: benchmark-frames-per-second ( loop -- n ) + [ frame-number>> ] [ benchmark-frame-number>> - ] [ benchmark-millis ] tri /f ; + +: start-loop ( loop -- ) + millis >>last-tick + t >>running? + [ reset-loop-benchmark ] + [ [ run-loop ] curry "game loop" spawn ] + [ (>>thread) ] tri ; + +: stop-loop ( loop -- ) + f >>running? + f >>thread + drop ; + +: <game-loop> ( tick-length delegate -- loop ) + millis f f 0 0 millis 0 0 + game-loop boa ; + +M: game-loop dispose + stop-loop ; +