@@ -47,6 +47,10 @@ export class Player {
4747 this . lastManualReconnect = 0
4848 this . audioMixer = null
4949 this . _initAudioMixer ( )
50+ this . fading = this . nodelink . options ?. audio ?. fading
51+ this . _fadeTimers = { trackEnd : null , pause : null , stop : null }
52+ this . _isResuming = false
53+ this . _pendingTrackStartFade = false
5054
5155 this . isLyricsSubscribed = false
5256 this . currentLyrics = null
@@ -292,6 +296,7 @@ export class Player {
292296 this . isPaused = false
293297
294298 if ( ! wasResuming && ! this . _isRestoring ) {
299+ this . _fading ( 'trackStart' )
295300 this . _emitTrackStart ( )
296301 }
297302 } else if ( state . status === 'paused' ) {
@@ -391,6 +396,7 @@ export class Player {
391396 this . position = 0
392397 this . currentLyrics = null
393398 this . lyricsLineIndex = - 1
399+ this . _fading ( 'reset' )
394400 }
395401
396402 async _emitTrackStart ( ) {
@@ -720,6 +726,8 @@ export class Player {
720726 if ( this . volumePercent !== 100 ) {
721727 resource . setVolume ( this . volumePercent / 100 )
722728 }
729+ this . _fading ( 'trackStartArm' , { resource } )
730+ this . _fading ( 'trackEndSchedule' , { startPosition : startTime || 0 } )
723731
724732 this . setFilters ( this . filters )
725733
@@ -775,6 +783,7 @@ export class Player {
775783 }
776784
777785 this . track = { encoded, info, endTime, userData, audioTrackId }
786+ this . _fading ( 'reset' )
778787
779788 if ( ! this . voice . endpoint || ! this . voice . token ) {
780789 logger (
@@ -897,6 +906,8 @@ export class Player {
897906 if ( result ) {
898907 this . emitEvent ( GatewayEvents . SEEK , { position : this . position } )
899908 if ( this . isLyricsSubscribed ) this . _recalculateLyricsIndex ( )
909+ this . _fading ( 'seek' )
910+ this . _fading ( 'trackEndSchedule' , { startPosition : this . position } )
900911 }
901912 return result
902913 } finally {
@@ -975,6 +986,7 @@ export class Player {
975986 if ( this . volumePercent !== 100 ) {
976987 resource . setVolume ( this . volumePercent / 100 )
977988 }
989+ this . _fading ( 'seekPrepare' , { resource } )
978990
979991 this . setFilters ( this . filters )
980992
@@ -1032,6 +1044,7 @@ export class Player {
10321044 if ( this . volumePercent !== 100 ) {
10331045 resource . setVolume ( this . volumePercent / 100 )
10341046 }
1047+ this . _fading ( 'seekPrepare' , { resource } )
10351048 resource . setFilters ( this . filters )
10361049
10371050 const oldStream = this . connection . play ( resource )
@@ -1140,6 +1153,7 @@ export class Player {
11401153 if ( this . volumePercent !== 100 ) {
11411154 resource . setVolume ( this . volumePercent / 100 )
11421155 }
1156+ this . _fading ( 'seekPrepare' , { resource } )
11431157
11441158 this . setFilters ( this . filters )
11451159
@@ -1160,6 +1174,7 @@ export class Player {
11601174 if ( this . destroying || ! this . track ) return false
11611175 if ( this . connection && this . connStatus !== 'destroyed' ) {
11621176 if ( this . connection . audioStream ) {
1177+ if ( this . _fading ( 'trackStop' ) ) return true
11631178 this . connection . stop ( EndReasons . STOPPED )
11641179 } else {
11651180 this . _emitTrackEnd ( EndReasons . STOPPED )
@@ -1211,6 +1226,11 @@ export class Player {
12111226 return true
12121227 }
12131228
1229+ setFading ( config ) {
1230+ this . fading = config
1231+ return true
1232+ }
1233+
12141234 setFilters ( filters ) {
12151235 if ( this . destroying || ! this . track ) return false
12161236 logger (
@@ -1553,6 +1573,7 @@ export class Player {
15531573 guildId : this . guildId ,
15541574 track : this . track ,
15551575 volume : this . volumePercent ,
1576+ fading : this . fading ,
15561577 paused : this . isPaused ,
15571578 filters : this . filters ,
15581579 state : {
@@ -1564,4 +1585,118 @@ export class Player {
15641585 voice : { ...this . voice }
15651586 }
15661587 }
1588+
1589+ _fading ( action , payload = { } ) {
1590+ const timers = this . _fadeTimers
1591+ if ( ! timers ) return false
1592+
1593+ if ( action === 'reset' ) {
1594+ if ( timers . trackEnd ) clearTimeout ( timers . trackEnd )
1595+ if ( timers . pause ) clearTimeout ( timers . pause )
1596+ if ( timers . stop ) clearTimeout ( timers . stop )
1597+ timers . trackEnd = null
1598+ timers . pause = null
1599+ timers . stop = null
1600+ this . _pendingTrackStartFade = false
1601+ return false
1602+ }
1603+
1604+ if ( action === 'trackEndSchedule' && timers . trackEnd ) {
1605+ clearTimeout ( timers . trackEnd )
1606+ timers . trackEnd = null
1607+ }
1608+
1609+ if ( ! this . fading || this . fading . enabled !== true ) return false
1610+
1611+ let section = null
1612+ if ( action === 'trackStart' || action === 'trackStartArm' )
1613+ section = this . fading . trackStart
1614+ else if ( action === 'trackEndSchedule' ) section = this . fading . trackEnd
1615+ else if ( action === 'trackStop' ) section = this . fading . trackStop
1616+ else if ( action === 'seek' ) section = this . fading . seek
1617+ else if ( action === 'seekPrepare' ) section = this . fading . seek
1618+ else return false
1619+
1620+ if (
1621+ ! section ||
1622+ ! Number . isFinite ( section . duration ) ||
1623+ section . duration <= 0
1624+ )
1625+ return false
1626+
1627+ if ( action === 'trackStartArm' ) {
1628+ const resource = payload . resource
1629+ if ( ! resource ?. setFadeVolume ) return false
1630+ resource . setFadeVolume ( 0 )
1631+ this . _pendingTrackStartFade = true
1632+ return true
1633+ }
1634+
1635+ if ( action === 'trackStart' ) {
1636+ if ( ! this . _pendingTrackStartFade ) return false
1637+ const stream = payload . resource || this . connection ?. audioStream
1638+ if ( ! stream ?. fadeTo ) return false
1639+ this . _pendingTrackStartFade = false
1640+ stream . fadeTo ( 1 , section . duration , section . curve )
1641+ return true
1642+ }
1643+
1644+ if ( action === 'seekPrepare' ) {
1645+ const resource = payload . resource
1646+ if ( ! resource ?. setFadeVolume ) return false
1647+ resource . setFadeVolume ( 0 )
1648+ return true
1649+ }
1650+
1651+ if ( action === 'seek' ) {
1652+ const stream = this . connection ?. audioStream
1653+ if ( ! stream ?. setFadeVolume ) return false
1654+ stream . setFadeVolume ( 0 )
1655+ stream . fadeTo ( 1 , section . duration , section . curve )
1656+ return true
1657+ }
1658+
1659+ if ( action === 'trackStop' ) {
1660+ const stream = this . connection ?. audioStream
1661+ if ( ! stream ?. fadeTo ) return false
1662+ if ( timers . stop ) clearTimeout ( timers . stop )
1663+ stream . fadeTo ( 0 , section . duration , section . curve )
1664+ timers . stop = setTimeout ( ( ) => {
1665+ this . connection ?. stop ( EndReasons . STOPPED )
1666+ if ( timers . stop ) {
1667+ clearTimeout ( timers . stop )
1668+ timers . stop = null
1669+ }
1670+ } , section . duration )
1671+ return true
1672+ }
1673+
1674+ if ( action === 'trackEndSchedule' ) {
1675+ if ( ! this . track ?. info ) return false
1676+ const total =
1677+ this . track . endTime && this . track . endTime > 0
1678+ ? this . track . endTime
1679+ : this . track . info . length || 0
1680+ if ( ! Number . isFinite ( total ) || total <= 0 ) return false
1681+
1682+ const startPosition = payload . startPosition || 0
1683+ const remaining = Math . max ( 0 , total - startPosition )
1684+ const fadeDuration = Math . min ( section . duration , remaining )
1685+ const delay = Math . max ( 0 , remaining - fadeDuration )
1686+
1687+ timers . trackEnd = setTimeout ( ( ) => {
1688+ const stream = this . connection ?. audioStream
1689+ if ( stream ?. fadeTo ) {
1690+ stream . fadeTo ( 0 , fadeDuration , section . curve )
1691+ }
1692+ if ( timers . trackEnd ) {
1693+ clearTimeout ( timers . trackEnd )
1694+ timers . trackEnd = null
1695+ }
1696+ } , delay )
1697+ return true
1698+ }
1699+
1700+ return false
1701+ }
15671702}
0 commit comments