1
1
import * as twgl from '../../../js/twgl-full.module.js' ;
2
- import ByteBeatNode from '../../../src/ByteBeatNode.js' ;
3
2
import { drawEffect } from './effect-utils.js' ;
4
3
4
+ const kChunkSize = 1024 ;
5
+
5
6
export default class SampleEffect {
6
7
constructor ( gl ) {
7
8
this . programInfo = twgl . createProgramInfo ( gl , [
@@ -52,9 +53,6 @@ export default class SampleEffect {
52
53
gl . LUMINANCE , gl . UNSIGNED_BYTE , this . sampleBuf ) ;
53
54
}
54
55
resize ( gl ) {
55
- this . sampleContext = ByteBeatNode . createContext ( ) ;
56
- this . sampleStack = ByteBeatNode . createStack ( ) ;
57
-
58
56
this . sampleWidth = gl . drawingBufferWidth ;
59
57
const sampleBuf = new Uint8Array ( this . sampleWidth ) ;
60
58
this . samplePos = 0 ;
@@ -65,13 +63,73 @@ export default class SampleEffect {
65
63
gl . LUMINANCE , gl . UNSIGNED_BYTE , sampleBuf ) ;
66
64
this . sampleBuf = sampleBuf ;
67
65
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' ;
68
107
}
108
+
69
109
render ( gl , commonUniforms , byteBeat ) {
70
110
const { uniforms, programInfo, bufferInfo} = this ;
71
111
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
+
72
121
gl . bindTexture ( gl . TEXTURE_2D , this . sampleTex ) ;
73
122
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
+ }
75
133
gl . texSubImage2D ( gl . TEXTURE_2D , 0 , this . samplePos , 0 , 1 , 1 , gl . LUMINANCE , gl . UNSIGNED_BYTE , this . samplePixel ) ;
76
134
this . samplePos = ( this . samplePos + 1 ) % this . sampleWidth ;
77
135
}
0 commit comments