Browse Source

add configurable fade type

master
jeffvli 3 years ago
parent
commit
31c2f9d579
  1. 245
      src/components/player/Player.tsx
  2. 14
      src/components/settings/Config.tsx
  3. 4
      src/components/shared/setDefaultSettings.ts
  4. 1
      src/redux/playQueueSlice.ts

245
src/components/player/Player.tsx

@ -25,6 +25,100 @@ import { setCurrentSeek } from '../../redux/playerSlice';
import cacheSong from '../shared/cacheSong'; import cacheSong from '../shared/cacheSong';
import { getSongCachePath, isCached } from '../../shared/utils'; import { getSongCachePath, isCached } from '../../shared/utils';
const listenHandler = (
currentPlayerRef: any,
nextPlayerRef: any,
playQueue: any,
currentEntryList: any,
dispatch: any,
player: number
) => {
const fadeDuration = Number(settings.getSync('fadeDuration')) || 0;
const fadeType = String(settings.getSync('fadeType'));
const currentSeek =
currentPlayerRef.current?.audioEl.current?.currentTime || 0;
const seekable =
currentPlayerRef.current.audioEl.current.seekable.length >= 1
? currentPlayerRef.current.audioEl.current.seekable.end(
currentPlayerRef.current.audioEl.current.seekable.length - 1
)
: 0;
const duration = currentPlayerRef.current?.audioEl.current?.duration;
const fadeAtTime = duration - fadeDuration;
// Fade only if repeat is 'all' or if not on the last track
if (
playQueue[`player${player}`].index + 1 <
playQueue[currentEntryList].length ||
playQueue.repeat === 'all'
) {
if (currentSeek >= fadeAtTime) {
// If it's not the last track in the queue OR we want to repeat
// Once fading starts, start playing player 2 and set current to 2
if (fadeDuration > 1.5) {
const timeLeft = duration - currentSeek;
if (nextPlayerRef.current.audioEl.current) {
const currentPlayerVolumeCalculation =
fadeType === 'equalPower'
? Math.sqrt(0.5 * ((timeLeft / fadeDuration) * 2)) *
playQueue.volume
: fadeType === 'linear'
? (timeLeft / fadeDuration) * playQueue.volume
: 0;
const currentPlayerVolume =
currentPlayerVolumeCalculation <= 0
? 0
: currentPlayerVolumeCalculation;
const nextPlayerVolumeCalculation =
fadeType === 'equalPower'
? Math.sqrt(0.5 * (2 - (timeLeft / fadeDuration) * 2)) *
playQueue.volume
: fadeType === 'linear'
? ((fadeDuration - timeLeft) / fadeDuration) * playQueue.volume
: 0;
const nextPlayerVolume =
nextPlayerVolumeCalculation >= playQueue.volume
? playQueue.volume
: nextPlayerVolumeCalculation;
if (player === 1) {
dispatch(
setPlayerVolume({ player: 1, volume: currentPlayerVolume })
);
dispatch(setPlayerVolume({ player: 2, volume: nextPlayerVolume }));
} else {
dispatch(
setPlayerVolume({ player: 2, volume: currentPlayerVolume })
);
dispatch(setPlayerVolume({ player: 1, volume: nextPlayerVolume }));
}
nextPlayerRef.current.audioEl.current.play();
dispatch(setIsFading(true));
}
} else {
// If fade time is less than 1 second, don't fade and just start at
// full volume. Due to the low fade duration, it causes the volume to
// blast from low to full incredibly quickly due to the intervalled polling
if (player === 1) {
dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
} else {
dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
}
nextPlayerRef.current.audioEl.current.play();
dispatch(setIsFading(true));
}
}
}
if (playQueue.currentPlayer === player) {
dispatch(setCurrentSeek({ seek: currentSeek, seekable }));
}
};
const Player = ({ children }: any, ref: any) => { const Player = ({ children }: any, ref: any) => {
const player1Ref = useRef<any>(); const player1Ref = useRef<any>();
const player2Ref = useRef<any>(); const player2Ref = useRef<any>();
@ -69,126 +163,37 @@ const Player = ({ children }: any, ref: any) => {
} }
} }
} else { } else {
player1Ref.current.audioEl.current.pause(); // Add short timeout otherwise sometimes fading-in player doesn't pause
player2Ref.current.audioEl.current.pause(); setTimeout(() => {
player1Ref.current.audioEl.current.pause();
player2Ref.current.audioEl.current.pause();
}, 100);
} }
}, [playQueue.currentPlayer, player.status]); }, [playQueue.currentPlayer, player.status]);
const handleListen1 = () => { const handleListenPlayer1 = () => {
const fadeDuration = Number(settings.getSync('fadeDuration')) || 0; listenHandler(
const currentSeek = player1Ref.current?.audioEl.current?.currentTime || 0; player1Ref,
const seekable = player2Ref,
player1Ref.current.audioEl.current.seekable.length >= 1 playQueue,
? player1Ref.current.audioEl.current.seekable.end( currentEntryList,
player1Ref.current.audioEl.current.seekable.length - 1 dispatch,
) 1
: 0; );
const duration = player1Ref.current?.audioEl.current?.duration;
const fadeAtTime = duration - fadeDuration;
// Don't fade if player2Index <= player1Index unless repeat==='all'
if (
playQueue.player1.index + 1 < playQueue[currentEntryList].length ||
playQueue.repeat === 'all'
) {
if (currentSeek >= fadeAtTime) {
// If it's not the last track in the queue OR we want to repeat
// Once fading starts, start playing player 2 and set current to 2
if (fadeDuration > 1.5) {
const timeLeft = duration - currentSeek;
if (player2Ref.current.audioEl.current) {
const player1Volume =
playQueue.player1.volume -
(playQueue.volume / timeLeft) * 0.095 <=
0
? 0
: playQueue.player1.volume -
(playQueue.volume / timeLeft) * 0.095;
const player2Volume =
playQueue.player2.volume +
(playQueue.volume / timeLeft) * 0.095 >=
playQueue.volume
? playQueue.volume
: playQueue.player2.volume +
(playQueue.volume / timeLeft) * 0.095;
dispatch(setPlayerVolume({ player: 1, volume: player1Volume }));
dispatch(setPlayerVolume({ player: 2, volume: player2Volume }));
player2Ref.current.audioEl.current.play();
dispatch(setIsFading(true));
}
} else {
// If fade time is less than 1 second, don't fade and just start at
// full volume. Due to the low fade duration, it causes the volume to
// blast from low to full incredibly quickly due to the intervalled polling
dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
player2Ref.current.audioEl.current.play();
dispatch(setIsFading(true));
}
}
}
if (playQueue.currentPlayer === 1) {
dispatch(setCurrentSeek({ seek: currentSeek, seekable }));
}
}; };
const handleListen2 = () => { const handleListenPlayer2 = () => {
const fadeDuration = Number(settings.getSync('fadeDuration')) || 0; listenHandler(
const currentSeek = player2Ref.current?.audioEl.current?.currentTime || 0; player2Ref,
const seekable = player1Ref,
player2Ref.current.audioEl.current.seekable.length >= 1 playQueue,
? player2Ref.current.audioEl.current.seekable.end( currentEntryList,
player2Ref.current.audioEl.current.seekable.length - 1 dispatch,
) 2
: 0; );
const duration = player2Ref.current?.audioEl.current?.duration;
const fadeAtTime = duration - fadeDuration;
if (
playQueue.player2.index + 1 < playQueue[currentEntryList].length ||
playQueue.repeat === 'all'
) {
if (currentSeek >= fadeAtTime) {
if (fadeDuration > 1.5) {
const timeLeft = duration - currentSeek;
if (player1Ref.current.audioEl.current) {
const player1Volume =
playQueue.player1.volume +
(playQueue.volume / timeLeft) * 0.095 >=
playQueue.volume
? playQueue.volume
: playQueue.player1.volume +
(playQueue.volume / timeLeft) * 0.095;
const player2Volume =
playQueue.player2.volume -
(playQueue.volume / timeLeft) * 0.095 <=
0
? 0
: playQueue.player2.volume -
(playQueue.volume / timeLeft) * 0.095;
dispatch(setPlayerVolume({ player: 1, volume: player1Volume }));
dispatch(setPlayerVolume({ player: 2, volume: player2Volume }));
player1Ref.current.audioEl.current.play();
dispatch(setIsFading(true));
}
} else {
dispatch(setPlayerVolume({ player: 1, volume: playQueue.volume }));
player1Ref.current.audioEl.current.play();
dispatch(setIsFading(true));
}
}
}
if (playQueue.currentPlayer === 2) {
dispatch(setCurrentSeek({ seek: currentSeek, seekable }));
}
}; };
const handleOnEnded1 = () => { const handleOnEndedPlayer1 = () => {
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`,
@ -229,7 +234,7 @@ const Player = ({ children }: any, ref: any) => {
} }
}; };
const handleOnEnded2 = () => { const handleOnEndedPlayer2 = () => {
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`,
@ -312,10 +317,10 @@ const Player = ({ children }: any, ref: any) => {
}.mp3` }.mp3`
: playQueue[currentEntryList][playQueue.player1.index]?.streamUrl : playQueue[currentEntryList][playQueue.player1.index]?.streamUrl
} }
listenInterval={150} listenInterval={50}
preload="auto" preload="auto"
onListen={handleListen1} onListen={handleListenPlayer1}
onEnded={handleOnEnded1} onEnded={handleOnEndedPlayer1}
volume={playQueue.player1.volume} volume={playQueue.player1.volume}
autoPlay={ autoPlay={
playQueue.player1.index === playQueue.currentIndex && playQueue.player1.index === playQueue.currentIndex &&
@ -336,10 +341,10 @@ const Player = ({ children }: any, ref: any) => {
}.mp3` }.mp3`
: playQueue[currentEntryList][playQueue.player2.index]?.streamUrl : playQueue[currentEntryList][playQueue.player2.index]?.streamUrl
} }
listenInterval={150} listenInterval={50}
preload="auto" preload="auto"
onListen={handleListen2} onListen={handleListenPlayer2}
onEnded={handleOnEnded2} onEnded={handleOnEndedPlayer2}
volume={playQueue.player2.volume} volume={playQueue.player2.volume}
autoPlay={ autoPlay={
playQueue.player2.index === playQueue.currentIndex && playQueue.player2.index === playQueue.currentIndex &&

14
src/components/settings/Config.tsx

@ -162,7 +162,7 @@ const Config = () => {
> >
<ConfigPanel header="Playback" bordered> <ConfigPanel header="Playback" bordered>
<p> <p>
Fading works by polling the audio player on an interval (150ms) to Fading works by polling the audio player on an interval (50ms) to
determine when to start fading to the next track. Due to this, you may determine when to start fading to the next track. Due to this, you may
notice the fade timing may not be 100% perfect. notice the fade timing may not be 100% perfect.
</p> </p>
@ -188,6 +188,18 @@ const Config = () => {
}} }}
style={{ width: '150px' }} style={{ width: '150px' }}
/> />
<br />
<ControlLabel>Crossfade type</ControlLabel>
<RadioGroup
name="radioList"
appearance="default"
defaultValue={String(settings.getSync('fadeType'))}
onChange={(e) => settings.setSync('fadeType', e)}
>
<Radio value="equalPower">Equal Power</Radio>
<Radio value="linear">Linear</Radio>
</RadioGroup>
</div> </div>
</ConfigPanel> </ConfigPanel>
<ConfigPanel header="Look & Feel" bordered> <ConfigPanel header="Look & Feel" bordered>

4
src/components/shared/setDefaultSettings.ts

@ -39,6 +39,10 @@ const setDefaultSettings = (force: boolean) => {
settings.setSync('fadeDuration', '9.0'); settings.setSync('fadeDuration', '9.0');
} }
if (force || !settings.hasSync('fadeType')) {
settings.setSync('fadeType', 'equalPower');
}
if (force || !settings.hasSync('playlistViewType')) { if (force || !settings.hasSync('playlistViewType')) {
settings.setSync('playlistViewType', 'list'); settings.setSync('playlistViewType', 'list');
} }

1
src/redux/playQueueSlice.ts

@ -98,6 +98,7 @@ const resetToPlayer1 = (state: PlayQueue) => {
state.isFading = false; state.isFading = false;
state.player1.volume = state.volume; state.player1.volume = state.volume;
state.player1.index = state.currentIndex; state.player1.index = state.currentIndex;
state.player2.volume = 0;
}; };
const insertItem = (array: any, index: any, item: any) => { const insertItem = (array: any, index: any, item: any) => {

Loading…
Cancel
Save