@@ -7,6 +7,7 @@ import React, {
77 useMemo ,
88 useCallback ,
99} from "react" ;
10+ import { Expand , Shrink } from "lucide-react" ;
1011import { useTheme } from "next-themes" ;
1112import BandPowerGraph from "./BandPowerGraph" ;
1213import { WebglPlot , ColorRGBA , WebglLine } from "webgl-plot" ;
@@ -31,7 +32,9 @@ const FFT = forwardRef(
3132 ref
3233 ) => {
3334 const fftBufferRef = useRef < number [ ] [ ] > ( Array . from ( { length : 16 } , ( ) => [ ] ) ) ;
34- const [ fftData , setFftData ] = useState < number [ ] [ ] > ( Array . from ( { length : 16 } , ( ) => [ ] ) ) ;
35+ const [ fftData , setFftData ] = useState < number [ ] [ ] > (
36+ Array . from ( { length : 16 } , ( ) => [ ] )
37+ ) ;
3538 const fftSize = 256 ;
3639 const sampleupdateref = useRef < number > ( 50 ) ;
3740 sampleupdateref . current = currentSamplingRate / 10 ;
@@ -41,20 +44,28 @@ const FFT = forwardRef(
4144 const maxFreq = 60 ;
4245 const [ betaPower , setBetaPower ] = useState < number > ( 0 ) ;
4346 const betaPowerRef = useRef < number > ( 0 ) ;
44- const channelColors = useMemo ( ( ) => [ "red" , "green" , "blue" , "purple" , "orange" , "yellow" ] , [ ] ) ;
47+ const channelColors = useMemo (
48+ ( ) => [ "red" , "green" , "blue" , "purple" , "orange" , "yellow" ] ,
49+ [ ]
50+ ) ;
4551 const canvasContainerRef = useRef < HTMLDivElement > ( null ) ;
4652 const dataPointCountRef = useRef < number > ( 1000 ) ;
4753 const wglPlotsref = useRef < WebglPlot [ ] > ( [ ] ) ;
4854 const linesRef = useRef < WebglLine [ ] > ( [ ] ) ;
49- const sweepPositions = useRef < number [ ] > ( new Array ( 6 ) . fill ( 0 ) ) ; // Array for sweep positions
50- const [ activeBandPowerView , setActiveBandPowerView ] = useState < 'bandpower' | 'brightcandle' | 'moveup' > ( 'bandpower' ) ;
55+ const sweepPositions = useRef < number [ ] > ( new Array ( 6 ) . fill ( 0 ) ) ;
56+
57+ // Extend views to include 'fullcandle'
58+ const [ activeBandPowerView , setActiveBandPowerView ] = useState <
59+ 'bandpower' | 'brightcandle' | 'fullcandle'
60+ > ( 'bandpower' ) ;
5161
5262 const buttonStyles = ( view : string ) => `
53- px-4 py-2 text-sm font-medium transition-all duration-300 rounded-md
54- ${ activeBandPowerView === view
63+ px-4 py-2 text-sm font-medium transition-all duration-300 rounded-md
64+ ${ activeBandPowerView === view
5565 ? 'bg-primary text-primary-foreground border rounded-xl '
56- : 'border rounded-xl bg-gray-600 text-primary-foreground' }
57- ` ;
66+ : 'border rounded-xl bg-gray-600 text-primary-foreground'
67+ }
68+ ` ;
5869
5970 let samplesReceived = 0 ;
6071 class SmoothingFilter {
@@ -64,15 +75,15 @@ const FFT = forwardRef(
6475 private dataIndex : number = 0 ;
6576
6677 constructor ( bufferSize : number = 5 , initialLength : number = 0 ) {
67- this . bufferSize = bufferSize ;
78+ this . bufferSize = Math . max ( 1 , Math . floor ( bufferSize ) ) ; // Prevent negative or NaN
6879 this . circularBuffers = Array . from ( { length : initialLength } , ( ) =>
69- new Array ( bufferSize ) . fill ( 0 )
80+ Array ( this . bufferSize ) . fill ( 0 )
7081 ) ;
7182 this . sums = new Array ( initialLength ) . fill ( 0 ) ;
7283 }
7384
85+
7486 getSmoothedValues ( newValues : number [ ] ) : number [ ] {
75- // Initialize buffers if first run or size changed
7687 if ( this . circularBuffers . length !== newValues . length ) {
7788 this . circularBuffers = Array . from ( { length : newValues . length } , ( ) =>
7889 new Array ( this . bufferSize ) . fill ( 0 )
@@ -94,57 +105,69 @@ const FFT = forwardRef(
94105 }
95106 }
96107
97- // Add this useEffect to calculate initial beta power
98108 useEffect ( ( ) => {
99109 if ( fftData . length > 0 && fftData [ 0 ] . length > 0 ) {
100110 const channelData = fftData [ 0 ] ;
101- const betaPower = calculateBandPower ( channelData , [ 13 , 32 ] ) ; // Beta range
102- const totalPower = calculateBandPower ( channelData , [ 0.5 , 100 ] ) ; // Full range
103- const normalizedBeta = ( betaPower / totalPower ) * 100 ;
111+ const beta = calculateBandPower ( channelData , [ 13 , 32 ] ) ;
112+ const total = calculateBandPower ( channelData , [ 0.5 , 100 ] ) ;
113+ const normalizedBeta = ( beta / total ) * 100 ;
104114 setBetaPower ( normalizedBeta ) ;
105115 betaPowerRef . current = normalizedBeta ;
106116 }
107117 } , [ fftData ] ) ;
108118
109- // Add this calculateBandPower function to FFT.tsx
110- const calculateBandPower = useCallback ( ( magnitudes : number [ ] , range : [ number , number ] ) => {
111- const [ startFreq , endFreq ] = range ;
112- const freqStep = currentSamplingRate / fftSize ;
113- const startIndex = Math . max ( 1 , Math . floor ( startFreq / freqStep ) ) ;
114- const endIndex = Math . min ( Math . floor ( endFreq / freqStep ) , magnitudes . length - 1 ) ;
115- let power = 0 ;
116- for ( let i = startIndex ; i <= endIndex ; i ++ ) {
117- power += magnitudes [ i ] * magnitudes [ i ] ;
118- }
119- return power ;
120- } , [ currentSamplingRate , fftSize ] ) ;
119+ const calculateBandPower = useCallback (
120+ ( magnitudes : number [ ] , range : [ number , number ] ) => {
121+ const [ startFreq , endFreq ] = range ;
122+ const freqStep = currentSamplingRate / fftSize ;
123+ const startIndex = Math . max ( 1 , Math . floor ( startFreq / freqStep ) ) ;
124+ const endIndex = Math . min (
125+ Math . floor ( endFreq / freqStep ) ,
126+ magnitudes . length - 1
127+ ) ;
128+ let power = 0 ;
129+ for ( let i = startIndex ; i <= endIndex ; i ++ ) {
130+ power += magnitudes [ i ] * magnitudes [ i ] ;
131+ }
132+ return power ;
133+ } ,
134+ [ currentSamplingRate , fftSize ]
135+ ) ;
121136
122137 const renderBandPowerView = ( ) => {
123138 switch ( activeBandPowerView ) {
124139 case 'bandpower' :
125140 return (
126141 < BandPowerGraph
127142 fftData = { fftData }
128- // Update both state and ref
129143 onBetaUpdate = { ( beta ) => {
130144 betaPowerRef . current = beta ;
131145 setBetaPower ( beta ) ;
132146 } }
133-
134147 samplingRate = { currentSamplingRate }
135148 />
136149 ) ;
137150 case 'brightcandle' :
138151 return (
139152 < BrightCandleView
140- betaPower = { betaPower } // Use state value instead of ref
153+ betaPower = { betaPower }
141154 fftData = { fftData }
142155 />
143156 ) ;
144- case 'moveup ' :
157+ case 'fullcandle ' :
145158 return (
146- < div className = "w-full h-full flex items-center justify-center text-gray-400" >
147- Move Up View
159+ < div className = "fixed inset-0 bg-gradient-to-b from-gray-900 to-black z-50 flex flex-col items-center justify-center p-4" >
160+ < button
161+ onClick = { ( ) => setActiveBandPowerView ( 'brightcandle' ) }
162+ className = "absolute top-4 right-4 p-2 bg-transparent text-white hover:text-gray-300 transition-all duration-300"
163+ >
164+ < Shrink />
165+ </ button >
166+ < div className = "w-full max-w-4xl h-full flex items-center justify-center" >
167+ < div className = "transform scale-150 filter drop-shadow-2xl" >
168+ < BrightCandleView betaPower = { betaPower } fftData = { fftData } />
169+ </ div >
170+ </ div >
148171 </ div >
149172 ) ;
150173 default :
@@ -155,13 +178,15 @@ const FFT = forwardRef(
155178 betaPowerRef . current = beta ;
156179 setBetaPower ( beta ) ;
157180 } }
158-
159181 samplingRate = { currentSamplingRate }
160182 />
161183 ) ;
162184 }
163185 } ;
164- const filter = new SmoothingFilter ( ( currentSamplingRate / sampleupdateref . current ) * 2 , 1 ) ;
186+ const rawBufferSize = ( currentSamplingRate / sampleupdateref . current ) * 2 ;
187+ const safeBufferSize = Math . max ( 1 , Math . floor ( rawBufferSize ) || 1 ) ;
188+ const filter = new SmoothingFilter ( safeBufferSize , 1 ) ;
189+
165190
166191 useImperativeHandle (
167192 ref ,
@@ -177,8 +202,7 @@ const FFT = forwardRef(
177202 }
178203 samplesReceived ++ ;
179204
180- // Trigger FFT computation more frequently
181- if ( samplesReceived % sampleupdateref . current === 0 ) { // Changed from 25 to 5
205+ if ( samplesReceived % sampleupdateref . current === 0 ) {
182206 const processedBuffer = fftBufferRef . current [ i ] . slice ( 0 , fftSize ) ;
183207 const floatInput = new Float32Array ( processedBuffer ) ;
184208 const fftMags = fftProcessor . computeMagnitudes ( floatInput ) ;
@@ -454,7 +478,8 @@ const FFT = forwardRef(
454478 < main
455479 ref = { canvasContainerRef }
456480 className = "flex-1 bg-highlight rounded-2xl m-2 overflow-hidden min-h-0 "
457- >
481+ >
482+
458483 </ main >
459484 < div className = "flex-1 m-2 flex flex-col md:flex-row justify-center overflow-hidden min-h-0 "
460485 >
@@ -467,17 +492,47 @@ const FFT = forwardRef(
467492 className = "w-full h-full"
468493 />
469494 </ div >
470- < div className = "flex-1 flex flex-col overflow-hidden min-h-0 min-w-0 ml-4 bg-highlight rounded-2xl" >
495+ < div
496+ className = "
497+ relative /* ← make this container the positioning context */
498+ flex-1 flex flex-col
499+ overflow-hidden min-h-0 min-w-0
500+ ml-4 bg-highlight rounded-2xl
501+ "
502+ >
503+ { /* only show when we’re on the Beta Candle view */ }
504+ { activeBandPowerView === 'brightcandle' && (
505+ < button
506+ onClick = { ( ) => setActiveBandPowerView ( 'fullcandle' ) }
507+ className = "
508+ absolute top-2 right-2
509+ p-2 bg-transparent
510+ text-gray-500 hover:text-gray-700
511+ transition-all duration-300
512+ "
513+ >
514+ < Expand />
515+ </ button >
516+ ) }
517+
471518 < div className = "flex justify-center space-x-2 pt-2 rounded-t-xl" >
472- < button onClick = { ( ) => setActiveBandPowerView ( 'bandpower' ) } className = { buttonStyles ( 'bandpower' ) } >
519+ < button
520+ onClick = { ( ) => setActiveBandPowerView ( 'bandpower' ) }
521+ className = { buttonStyles ( 'bandpower' ) }
522+ >
473523 Band Power
474524 </ button >
475- < button onClick = { ( ) => setActiveBandPowerView ( 'brightcandle' ) } className = { buttonStyles ( 'brightcandle' ) } >
525+ < button
526+ onClick = { ( ) => setActiveBandPowerView ( 'brightcandle' ) }
527+ className = { buttonStyles ( 'brightcandle' ) }
528+ >
476529 Beta Candle
477530 </ button >
478531 </ div >
532+
479533 { renderBandPowerView ( ) }
480534 </ div >
535+
481536 </ div >
482537 </ div >
483538 ) ;
0 commit comments