         [ AL_ROLLOFF_FACTOR swap audio-rolloff alSourcef ]
     } 2cleave ;
 
-:: update-audio-clip ( audio-clip -- )
-    audio-clip update-source
+GENERIC: (update-audio-clip) ( audio-clip -- )
+
+M: static-audio-clip (update-audio-clip)
+    drop ;
+
+M:: streaming-audio-clip (update-audio-clip) ( audio-clip -- )
     audio-clip al-source>> :> al-source
-    0 c:<uint> :> buffer*
-
-    al-source AL_SOURCE_STATE get-source-param AL_STOPPED =
-    [ audio-clip dispose ] [
-        al-source AL_BUFFERS_PROCESSED get-source-param [
-            al-source 1 buffer* alSourceUnqueueBuffers
-            audio-clip buffer* c:*uint queue-clip-buffer
-        ] times
-    ] if ;
+    0 c:<uint> :> buffer
+    al-source AL_BUFFERS_PROCESSED get-source-param [
+        al-source 1 buffer alSourceUnqueueBuffers
+        audio-clip buffer c:*uint queue-clip-buffer
+    ] times ;
+
+: update-audio-clip ( audio-clip -- )
+    [ update-source ] [
+        dup al-source>> AL_SOURCE_STATE get-source-param AL_STOPPED = 
+        [ dispose ] [ (update-audio-clip) ] if
+    ] bi ;
 
 : clip-al-sources ( clips -- length sources )
     [ length ] [ [ al-source>> ] uint-array{ } map-as ] bi ;

    audio-clip [ size + ] change-next-data-offset drop ; inline

:: queue-clip-buffer ( audio-clip al-buffer -- )
    audio-clip audio-engine>> :> audio-engine
    audio-engine buffer-size>> :> buffer-size
    audio-clip audio>> :> audio
    audio-clip next-data-offset>> :> next-data-offset
    audio size>> next-data-offset - :> remaining-audio

    {
        { [ remaining-audio 0 <= ] [
            audio-clip loop?>> [
                audio-clip 0 >>next-data-offset
                al-buffer queue-clip-buffer
            ] when
        ] }
        { [ remaining-audio buffer-size < ] [
            audio-clip loop?>> [
                audio data>>
                [ next-data-offset swap <displaced-alien> remaining-audio <direct-uchar-array> ]
                [ buffer-size remaining-audio - <direct-uchar-array> ] bi append :> data
                audio-clip al-buffer audio data buffer-size (queue-clip-buffer)

                audio-clip [ audio size>> mod ] change-next-data-offset drop
            ] [
                next-data-offset audio data>> <displaced-alien> :> data
                audio-clip al-buffer audio data remaining-audio (queue-clip-buffer)
            ] if
        ] }
        [
            next-data-offset audio data>> <displaced-alien> :> data
            audio-clip al-buffer audio data buffer-size (queue-clip-buffer)
        ]
    } cond ;

: update-listener ( audio-engine -- )
    listener>> {

    [ [ alcCloseDevice*   ] when* f ] change-al-device
    drop ;

:: <static-audio-clip> ( audio-engine audio source loop? -- audio-clip/f )
    audio-engine get-available-source :> al-source

    al-source [
        1 0 c:<uint> [ alGenBuffers ] keep c:*uint :> al-buffer
        al-buffer audio { [ openal-format ] [ data>> ] [ size>> ] [ sample-rate>> ] } cleave
            alBufferData

        al-source AL_BUFFER al-buffer alSourcei
        al-source AL_LOOPING loop? c:>c-bool alSourcei

        static-audio-clip new-disposable
            audio-engine >>audio-engine
            source >>source
            al-source >>al-source
            al-buffer >>al-buffer :> clip
        clip audio-engine clips>> push
        clip
    ] [ f ] if ;

:: <streaming-audio-clip> ( audio-engine source generator buffer-size buffer-count -- audio-clip/f )
    audio-engine get-available-source :> al-source

    al-source [
        buffer-count dup (uint-array) [ alGenBuffers ] keep :> al-buffers
        generator generator-audio-format :> ( channels sample-bits sample-rate )

        audio-clip new-disposable
            audio-engine >>audio-engine
            source >>source
            al-source >>al-source
            generator >>generator
            buffer-size >>buffer-size
            channels >>channels
            sample-bits >>sample-bits
            sample-rate >>sample-rate
            al-buffers >>al-buffers
        al-buffers [ clip swap queue-clip-buffer ] each
        clip audio-engine clips>> push
        clip
    ] [ f ] if ;

M: audio-clip dispose*
    [ audio-engine>> clips>> remove! drop ]
    [ al-source>> flush-source ] bi ;

M: static-audio-clip dispose*
    [ call-next-method ]
    [ [ 1 ] dip al-buffer>> <uint> alDeleteBuffers ] bi ;

M: streaming-audio-clip dispose*
    [ call-next-method ]
    [ al-buffers>> [ length ] keep alDeleteBuffers ] bi ;

: play-clip ( audio-clip -- )
    [ update-source ]
