11import * as twgl from '../../../js/twgl-full.module.js' ;
2- import ByteBeatNode from '../../../src/ByteBeatNode.js' ;
32import { drawEffect } from './effect-utils.js' ;
43
4+ const kChunkSize = 1024 ;
5+
56export default class SampleEffect {
67 constructor ( gl ) {
78 this . programInfo = twgl . createProgramInfo ( gl , [
@@ -52,9 +53,6 @@ export default class SampleEffect {
5253 gl . LUMINANCE , gl . UNSIGNED_BYTE , this . sampleBuf ) ;
5354 }
5455 resize ( gl ) {
55- this . sampleContext = ByteBeatNode . createContext ( ) ;
56- this . sampleStack = ByteBeatNode . createStack ( ) ;
57-
5856 this . sampleWidth = gl . drawingBufferWidth ;
5957 const sampleBuf = new Uint8Array ( this . sampleWidth ) ;
6058 this . samplePos = 0 ;
@@ -65,13 +63,73 @@ export default class SampleEffect {
6563 gl . LUMINANCE , gl . UNSIGNED_BYTE , sampleBuf ) ;
6664 this . sampleBuf = sampleBuf ;
6765 this . sampleTime = 0 ;
66+ this . data = new Map ( ) ;
67+ this . state = 'init' ;
68+ }
69+
70+ async #getData( byteBeat ) {
71+ this . updating = true ;
72+ const start = Math . ceil ( this . sampleTime / kChunkSize ) * kChunkSize ;
73+ const numChannels = byteBeat . getNumChannels ( ) ;
74+ const dataP = [ ] ;
75+ for ( let channel = 0 ; channel < numChannels ; ++ channel ) {
76+ dataP . push ( byteBeat . getSamplesForTimeRange ( start , start + kChunkSize , kChunkSize , this . sampleContext , this . sampleStack , channel ) ) ;
77+ }
78+ const data = await Promise . all ( dataP ) ;
79+ const chunkId = start / kChunkSize ;
80+ this . data . set ( chunkId , data ) ;
81+ this . updating = false ;
82+ }
83+
84+ #update( byteBeat ) {
85+ const noData = this . data . length === 0 ;
86+ const passingHalfWayPoint = ( this . oldSampleTime % kChunkSize ) < kChunkSize / 2 && ( this . sampleTime % kChunkSize ) >= kChunkSize / 2 ;
87+ const passingChunk = ( this . oldSampleTime % kChunkSize ) >= kChunkSize - 2 && this . sampleTime % kChunkSize === 0 ;
88+ const oldChunkId = this . oldSampleTime / kChunkSize | 0 ;
89+ this . oldSampleTime = this . sampleTime ;
90+ if ( passingChunk ) {
91+ this . data . delete ( oldChunkId ) ;
92+ }
93+ if ( ! this . updating && ( noData || passingHalfWayPoint ) ) {
94+ this . #getData( byteBeat ) ;
95+ }
96+ }
97+
98+ async #init( byteBeat ) {
99+ if ( this . sampleContext ) {
100+ byteBeat . destroyContext ( this . sampleContext ) ;
101+ byteBeat . destroyStack ( this . sampleStack ) ;
102+ }
103+ this . sampleContext = await byteBeat . createContext ( ) ;
104+ this . sampleStack = await byteBeat . createStack ( ) ;
105+ await this . #getData( byteBeat ) ;
106+ this . state = 'running' ;
68107 }
108+
69109 render ( gl , commonUniforms , byteBeat ) {
70110 const { uniforms, programInfo, bufferInfo} = this ;
71111
112+ if ( this . state === 'init' ) {
113+ this . state = 'initializing' ;
114+ this . #init( byteBeat ) ;
115+ }
116+ if ( this . state !== 'running' ) {
117+ return ;
118+ }
119+ this . #update( byteBeat ) ;
120+
72121 gl . bindTexture ( gl . TEXTURE_2D , this . sampleTex ) ;
73122 for ( let ii = 0 ; ii < 2 ; ++ ii ) {
74- this . samplePixel [ 0 ] = Math . round ( byteBeat . getSampleForTime ( this . sampleTime ++ , this . sampleContext , this . sampleStack ) * 127 ) + 127 ;
123+ const chunkId = this . sampleTime ++ / kChunkSize | 0 ;
124+ const chunk = this . data . get ( chunkId ) ;
125+ const ndx = this . sampleTime % kChunkSize ;
126+ try {
127+ const ch = chunk [ 0 ] ;
128+ const sample = ch [ ndx ] ;
129+ this . samplePixel [ 0 ] = Math . round ( sample * 127 ) + 127 ;
130+ } catch {
131+ //
132+ }
75133 gl . texSubImage2D ( gl . TEXTURE_2D , 0 , this . samplePos , 0 , 1 , 1 , gl . LUMINANCE , gl . UNSIGNED_BYTE , this . samplePixel ) ;
76134 this . samplePos = ( this . samplePos + 1 ) % this . sampleWidth ;
77135 }
0 commit comments