2026-01-07 09:35:43 +00:00
import React , { createContext , useContext , useState , useEffect , useRef } from 'react' ;
import { Track } from '../types' ;
export interface VisualizerSettings {
mode : 'waveform' | 'spectrogram' | 'bars' | 'off' ;
color : string ;
sensitivity : number ;
}
interface AudioContextType {
currentTrack : Track | null ;
isPlaying : boolean ;
queue : Track [ ] ;
history : Track [ ] ;
progress : number ; // 0-100
currentTime : number ;
duration : number ;
volume : number ;
isMuted : boolean ;
shuffle : boolean ;
repeatMode : 'off' | 'all' | 'one' ;
playbackRate : number ;
pitchCorrection : boolean ;
visualizerSettings : VisualizerSettings ;
autoplay : boolean ;
2026-01-07 10:15:48 +00:00
2026-01-07 09:35:43 +00:00
// Actions
playTrack : ( track : Track , context? : Track [ ] ) = > void ;
togglePlay : ( ) = > void ;
nextTrack : ( ) = > void ;
prevTrack : ( ) = > void ;
seek : ( percent : number ) = > void ;
setVolume : ( val : number ) = > void ;
toggleMute : ( ) = > void ;
toggleShuffle : ( ) = > void ;
toggleRepeat : ( ) = > void ;
setPlaybackRate : ( rate : number ) = > void ;
togglePitchCorrection : ( ) = > void ;
setVisualizerSettings : ( settings : VisualizerSettings ) = > void ;
addToQueue : ( track : Track ) = > void ;
removeFromQueue : ( trackId : string ) = > void ;
playNext : ( track : Track ) = > void ;
reorderQueue : ( fromIndex : number , toIndex : number ) = > void ;
clearQueue : ( ) = > void ;
toggleAutoplay : ( ) = > void ;
}
const AudioContext = createContext < AudioContextType | undefined > ( undefined ) ;
export const useAudio = ( ) = > {
const context = useContext ( AudioContext ) ;
if ( ! context ) throw new Error ( 'useAudio must be used within AudioProvider' ) ;
return context ;
} ;
// Mock Data for Initial State
const mockTracks : Track [ ] = [
2026-01-07 10:15:48 +00:00
{ id : '1' , title : 'Neon Nightrider' , artist : 'Cyber_Punk_OST' , album : 'Night City Vol.1' , duration : '3:45' , durationSec : 225 , plays : 12000 , like_count : 3400 , coverUrl : 'https://picsum.photos/id/55/400/400' , isPremium : true , waveformData : Array.from ( { length : 100 } , ( ) = > Math . random ( ) ) , lyrics : [ { time : 10 , text : "Neon lights flickering..." } , { time : 15 , text : "Driving through the cyber city" } , { time : 20 , text : "Bass dropping heavy on the pavement" } ] } ,
{ id : '2' , title : 'Glitch in the Matrix' , artist : 'Null Pointer' , album : 'System Failure' , duration : '4:20' , durationSec : 260 , plays : 8500 , like_count : 2100 , coverUrl : 'https://picsum.photos/id/58/400/400' , waveformData : Array.from ( { length : 100 } , ( ) = > Math . random ( ) ) } ,
{ id : '3' , title : 'Tokyo Drift (Lofi)' , artist : 'Sakura Beats' , album : 'Chillhop Essentials' , duration : '2:55' , durationSec : 175 , plays : 45000 , like_count : 12000 , coverUrl : 'https://picsum.photos/id/60/400/400' , isPremium : true , waveformData : Array.from ( { length : 100 } , ( ) = > Math . random ( ) ) } ,
{ id : '4' , title : 'Neural Link' , artist : 'Mainframe' , album : 'AI Dreams' , duration : '5:10' , durationSec : 310 , plays : 2300 , like_count : 450 , coverUrl : 'https://picsum.photos/id/70/200/200' , waveformData : Array.from ( { length : 100 } , ( ) = > Math . random ( ) ) } ,
{ id : '5' , title : 'Synthwave Sunset' , artist : 'Retro Boy' , album : 'Analog Memories' , duration : '3:30' , durationSec : 210 , plays : 1200 , like_count : 300 , coverUrl : 'https://picsum.photos/id/80/200/200' , waveformData : Array.from ( { length : 100 } , ( ) = > Math . random ( ) ) } ,
2026-01-07 09:35:43 +00:00
] ;
export const AudioProvider : React.FC < { children : React.ReactNode } > = ( { children } ) = > {
const [ currentTrack , setCurrentTrack ] = useState < Track | null > ( mockTracks [ 0 ] ) ;
const [ queue , setQueue ] = useState < Track [ ] > ( mockTracks . slice ( 1 ) ) ;
const [ history , setHistory ] = useState < Track [ ] > ( [ ] ) ;
const [ isPlaying , setIsPlaying ] = useState ( false ) ;
const [ progress , setProgress ] = useState ( 0 ) ;
const [ currentTime , setCurrentTime ] = useState ( 0 ) ;
const [ volume , setVolumeState ] = useState ( 80 ) ;
const [ isMuted , _setIsMuted ] = useState ( false ) ;
const [ shuffle , setShuffle ] = useState ( false ) ;
const [ repeatMode , setRepeatMode ] = useState < 'off' | 'all' | 'one' > ( 'off' ) ;
2026-01-07 10:15:48 +00:00
2026-01-07 09:35:43 +00:00
// Phase 8 & 9 New States
const [ playbackRate , setPlaybackRate ] = useState ( 1.0 ) ;
const [ pitchCorrection , setPitchCorrection ] = useState ( true ) ;
const [ visualizerSettings , setVisualizerSettings ] = useState < VisualizerSettings > ( {
mode : 'waveform' ,
color : '#66FCF1' ,
sensitivity : 50
} ) ;
const [ autoplay , setAutoplay ] = useState ( true ) ;
2026-01-07 10:15:48 +00:00
2026-01-07 09:35:43 +00:00
const audioInterval = useRef < number | null > ( null ) ;
// Simulation of Audio Playback
useEffect ( ( ) = > {
if ( isPlaying && currentTrack ) {
audioInterval . current = window . setInterval ( ( ) = > {
setCurrentTime ( prev = > {
if ( prev >= currentTrack . durationSec ) {
// Track finished
if ( repeatMode === 'one' ) {
2026-01-07 10:15:48 +00:00
return 0 ; // Restart
2026-01-07 09:35:43 +00:00
}
if ( queue . length > 0 || autoplay ) {
2026-01-07 10:15:48 +00:00
nextTrack ( ) ; // Auto next logic handled there
2026-01-07 09:35:43 +00:00
} else {
2026-01-07 10:15:48 +00:00
setIsPlaying ( false ) ;
return prev ;
2026-01-07 09:35:43 +00:00
}
return 0 ;
}
// Adjust increment based on playback rate
return prev + ( 1 * playbackRate ) ;
} ) ;
} , 1000 / playbackRate ) ; // Interval adjusts to speed
} else {
if ( audioInterval . current ) clearInterval ( audioInterval . current ) ;
}
return ( ) = > { if ( audioInterval . current ) clearInterval ( audioInterval . current ) ; } ;
} , [ isPlaying , currentTrack , repeatMode , playbackRate , queue , autoplay ] ) ;
// Sync Progress Percentage
useEffect ( ( ) = > {
if ( currentTrack ) {
setProgress ( ( currentTime / currentTrack . durationSec ) * 100 ) ;
}
} , [ currentTime , currentTrack ] ) ;
const playTrack = ( track : Track , context? : Track [ ] ) = > {
if ( currentTrack && currentTrack . id !== track . id ) {
2026-01-07 10:15:48 +00:00
setHistory ( prev = > [ . . . prev , currentTrack ] ) ;
2026-01-07 09:35:43 +00:00
}
setCurrentTrack ( track ) ;
if ( context ) {
2026-01-07 10:15:48 +00:00
const trackIndex = context . findIndex ( t = > t . id === track . id ) ;
if ( trackIndex !== - 1 ) {
setQueue ( context . slice ( trackIndex + 1 ) ) ;
}
2026-01-07 09:35:43 +00:00
}
setIsPlaying ( true ) ;
setCurrentTime ( 0 ) ;
} ;
const togglePlay = ( ) = > setIsPlaying ( ! isPlaying ) ;
const nextTrack = ( ) = > {
if ( queue . length > 0 ) {
2026-01-07 10:15:48 +00:00
const next = shuffle ? queue [ Math . floor ( Math . random ( ) * queue . length ) ] : queue [ 0 ] ;
setHistory ( prev = > currentTrack ? [ . . . prev , currentTrack ] : prev ) ;
if ( repeatMode !== 'all' ) {
setQueue ( prev = > prev . filter ( t = > t . id !== next . id ) ) ;
} else {
setQueue ( prev = > [ . . . prev . filter ( t = > t . id !== next . id ) , next ] ) ;
}
setCurrentTrack ( next ) ;
setCurrentTime ( 0 ) ;
setIsPlaying ( true ) ;
2026-01-07 09:35:43 +00:00
} else if ( autoplay ) {
2026-01-07 10:15:48 +00:00
// Mock Autoplay logic: Pick random track from "Network"
// In real app, this fetches recommendation
const randomMock = mockTracks [ Math . floor ( Math . random ( ) * mockTracks . length ) ] ;
// Don't add to history if it's autoplay transition usually, but for mock simplicty:
setHistory ( prev = > currentTrack ? [ . . . prev , currentTrack ] : prev ) ;
setCurrentTrack ( { . . . randomMock , id : ` auto- ${ Date . now ( ) } ` , title : ` Autoplay: ${ randomMock . title } ` } ) ;
setCurrentTime ( 0 ) ;
setIsPlaying ( true ) ;
2026-01-07 09:35:43 +00:00
} else {
2026-01-07 10:15:48 +00:00
setIsPlaying ( false ) ;
setCurrentTime ( 0 ) ;
2026-01-07 09:35:43 +00:00
}
} ;
const prevTrack = ( ) = > {
if ( currentTime > 3 ) {
2026-01-07 10:15:48 +00:00
setCurrentTime ( 0 ) ;
2026-01-07 09:35:43 +00:00
} else if ( history . length > 0 ) {
2026-01-07 10:15:48 +00:00
const prev = history [ history . length - 1 ] ;
setQueue ( prevQ = > currentTrack ? [ currentTrack , . . . prevQ ] : prevQ ) ;
setHistory ( prevH = > prevH . slice ( 0 , - 1 ) ) ;
setCurrentTrack ( prev ) ;
setCurrentTime ( 0 ) ;
setIsPlaying ( true ) ;
2026-01-07 09:35:43 +00:00
}
} ;
const seek = ( percent : number ) = > {
if ( currentTrack ) {
2026-01-07 10:15:48 +00:00
const newTime = ( percent / 100 ) * currentTrack . durationSec ;
setCurrentTime ( newTime ) ;
setProgress ( percent ) ;
2026-01-07 09:35:43 +00:00
}
} ;
const setVolume = ( val : number ) = > setVolumeState ( val ) ;
const toggleMute = ( ) = > setIsPlaying ( prev = > ! prev ) ; // Simplified mock
const toggleShuffle = ( ) = > setShuffle ( ! shuffle ) ;
const toggleRepeat = ( ) = > {
2026-01-07 10:15:48 +00:00
const modes : ( 'off' | 'all' | 'one' ) [ ] = [ 'off' , 'all' , 'one' ] ;
const next = modes [ ( modes . indexOf ( repeatMode ) + 1 ) % modes . length ] ;
setRepeatMode ( next ) ;
2026-01-07 09:35:43 +00:00
} ;
2026-01-07 10:15:48 +00:00
2026-01-07 09:35:43 +00:00
const togglePitchCorrection = ( ) = > setPitchCorrection ( ! pitchCorrection ) ;
const toggleAutoplay = ( ) = > setAutoplay ( ! autoplay ) ;
const addToQueue = ( track : Track ) = > setQueue ( prev = > [ . . . prev , track ] ) ;
const playNext = ( track : Track ) = > setQueue ( prev = > [ track , . . . prev ] ) ;
const removeFromQueue = ( id : string ) = > setQueue ( prev = > prev . filter ( t = > t . id !== id ) ) ;
const clearQueue = ( ) = > setQueue ( [ ] ) ;
2026-01-07 10:15:48 +00:00
2026-01-07 09:35:43 +00:00
const reorderQueue = ( fromIndex : number , toIndex : number ) = > {
const result = Array . from ( queue ) ;
const [ removed ] = result . splice ( fromIndex , 1 ) ;
result . splice ( toIndex , 0 , removed ) ;
setQueue ( result ) ;
} ;
return (
< AudioContext.Provider value = { {
currentTrack , isPlaying , queue , history , progress , currentTime , duration : currentTrack?.durationSec || 0 ,
volume , isMuted , shuffle , repeatMode , playbackRate , pitchCorrection , visualizerSettings , autoplay ,
playTrack , togglePlay , nextTrack , prevTrack , seek , setVolume , toggleMute , toggleShuffle , toggleRepeat ,
setPlaybackRate , togglePitchCorrection , setVisualizerSettings , toggleAutoplay ,
addToQueue , removeFromQueue , playNext , reorderQueue , clearQueue
} } >
{ children }
< / AudioContext.Provider >
) ;
} ;