mirror of
https://github.com/jeffvli/sonixd.git
synced 2026-05-18 13:52:02 -04:00
update gapless
- current track now changes after track ends, instead of at fade start - calculate the fade by time left instead of total fade duration
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import React, {
|
||||
useState,
|
||||
createContext,
|
||||
useRef,
|
||||
useEffect,
|
||||
@@ -14,12 +13,13 @@ import {
|
||||
setCurrentPlayer,
|
||||
setPlayerVolume,
|
||||
setCurrentSeek,
|
||||
setIsFading,
|
||||
setAutoIncremented,
|
||||
} from '../../redux/playQueueSlice';
|
||||
|
||||
export const PlayerContext = createContext<any>({});
|
||||
|
||||
const Player = ({ children }: any, ref: any) => {
|
||||
const [incremented, setIncremented] = useState(false);
|
||||
const player1Ref = useRef<any>();
|
||||
const player2Ref = useRef<any>();
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -62,35 +62,31 @@ const Player = ({ children }: any, ref: any) => {
|
||||
const fadeAtTime = duration - fadeDuration;
|
||||
|
||||
if (currentSeek >= fadeAtTime) {
|
||||
// Once fading starts, start playing player 2 and set current to 2
|
||||
const timeLeft = duration - currentSeek;
|
||||
|
||||
if (player2Ref.current.audioEl.current) {
|
||||
// Once fading starts, start playing player 2 and set current to 2
|
||||
const player1Volume =
|
||||
playQueue.player1.volume - playQueue.volume / (fadeDuration * 2) <= 0
|
||||
playQueue.player1.volume - playQueue.volume / timeLeft <= 0
|
||||
? 0
|
||||
: playQueue.player1.volume - playQueue.volume / (fadeDuration * 2);
|
||||
: playQueue.player1.volume - playQueue.volume / timeLeft;
|
||||
|
||||
const player2Volume =
|
||||
playQueue.player2.volume + playQueue.volume / (fadeDuration * 1.5) >=
|
||||
playQueue.player2.volume + playQueue.volume / timeLeft >=
|
||||
playQueue.volume
|
||||
? playQueue.volume
|
||||
: playQueue.player2.volume +
|
||||
playQueue.volume / (fadeDuration * 1.5);
|
||||
: playQueue.player2.volume + playQueue.volume / timeLeft;
|
||||
|
||||
dispatch(setPlayerVolume({ player: 1, volume: player1Volume }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: player2Volume }));
|
||||
|
||||
player2Ref.current.audioEl.current.play();
|
||||
if (!incremented) {
|
||||
dispatch(incrementCurrentIndex('none'));
|
||||
setIncremented(true);
|
||||
}
|
||||
dispatch(setCurrentPlayer(2));
|
||||
dispatch(setIsFading(true));
|
||||
}
|
||||
console.log('fading player1...');
|
||||
} else {
|
||||
}
|
||||
|
||||
if (playQueue.currentPlayer === 1) {
|
||||
dispatch(setCurrentSeek(currentSeek));
|
||||
}
|
||||
console.log(`player1: ${currentSeek} / ${fadeAtTime}`);
|
||||
};
|
||||
|
||||
const handleListen2 = () => {
|
||||
@@ -100,59 +96,61 @@ const Player = ({ children }: any, ref: any) => {
|
||||
const fadeAtTime = duration - fadeDuration;
|
||||
|
||||
if (currentSeek >= fadeAtTime) {
|
||||
const timeLeft = duration - currentSeek;
|
||||
|
||||
// Once fading starts, start playing player 1 and set current to 1
|
||||
if (player1Ref.current.audioEl.current) {
|
||||
// Once fading starts, start playing player 1 and set current to 1
|
||||
const player1Volume =
|
||||
playQueue.player1.volume + playQueue.volume / (fadeDuration * 1.5) >=
|
||||
playQueue.player1.volume + playQueue.volume / timeLeft >=
|
||||
playQueue.volume
|
||||
? playQueue.volume
|
||||
: playQueue.player1.volume +
|
||||
playQueue.volume / (fadeDuration * 1.5);
|
||||
: playQueue.player1.volume + playQueue.volume / timeLeft;
|
||||
|
||||
const player2Volume =
|
||||
playQueue.player2.volume - playQueue.volume / (fadeDuration * 2) <= 0
|
||||
playQueue.player2.volume - playQueue.volume / timeLeft <= 0
|
||||
? 0
|
||||
: playQueue.player2.volume - playQueue.volume / (fadeDuration * 2);
|
||||
: playQueue.player2.volume - playQueue.volume / timeLeft;
|
||||
|
||||
dispatch(setPlayerVolume({ player: 1, volume: player1Volume }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: player2Volume }));
|
||||
|
||||
player1Ref.current.audioEl.current.play();
|
||||
if (!incremented) {
|
||||
dispatch(incrementCurrentIndex('none'));
|
||||
setIncremented(true);
|
||||
}
|
||||
dispatch(setCurrentPlayer(1));
|
||||
dispatch(setIsFading(true));
|
||||
}
|
||||
console.log('fading player2...');
|
||||
} else {
|
||||
dispatch(setCurrentSeek(currentSeek));
|
||||
}
|
||||
|
||||
console.log(`player2: ${currentSeek} / ${fadeAtTime}`);
|
||||
if (playQueue.currentPlayer === 2) {
|
||||
dispatch(setCurrentSeek(currentSeek));
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnEnded1 = () => {
|
||||
if (!playQueue.autoIncremented) {
|
||||
dispatch(incrementCurrentIndex('none'));
|
||||
dispatch(setAutoIncremented(true));
|
||||
}
|
||||
dispatch(setCurrentPlayer(2));
|
||||
dispatch(incrementPlayerIndex(1));
|
||||
dispatch(setPlayerVolume({ player: 1, volume: 0 }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
|
||||
setIncremented(false);
|
||||
dispatch(setIsFading(false));
|
||||
dispatch(setAutoIncremented(false));
|
||||
};
|
||||
|
||||
const handleOnEnded2 = () => {
|
||||
if (!playQueue.autoIncremented) {
|
||||
dispatch(incrementCurrentIndex('none'));
|
||||
dispatch(setAutoIncremented(true));
|
||||
}
|
||||
dispatch(setCurrentPlayer(1));
|
||||
dispatch(incrementPlayerIndex(2));
|
||||
dispatch(setPlayerVolume({ player: 1, volume: playQueue.volume }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: 0 }));
|
||||
setIncremented(false);
|
||||
dispatch(setIsFading(false));
|
||||
dispatch(setAutoIncremented(false));
|
||||
};
|
||||
|
||||
return (
|
||||
<PlayerContext.Provider
|
||||
value={{
|
||||
incremented,
|
||||
setIncremented,
|
||||
}}
|
||||
>
|
||||
<PlayerContext.Provider value={{}}>
|
||||
<ReactAudioPlayer
|
||||
ref={player1Ref}
|
||||
src={playQueue.entry[playQueue.player1.index]?.streamUrl}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { FlexboxGrid, Icon, Slider, Button } from 'rsuite';
|
||||
import { FlexboxGrid, Icon, Slider } from 'rsuite';
|
||||
import format from 'format-duration';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from '../../redux/playQueueSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
|
||||
import Player from './Player';
|
||||
import 'react-rangeslider/lib/index.css';
|
||||
|
||||
const PlayerContainer = styled.div`
|
||||
background: #000000;
|
||||
@@ -102,8 +101,24 @@ const PlayerBar = () => {
|
||||
|
||||
const handleSeekSlider = (e: number) => {
|
||||
setIsDragging(true);
|
||||
|
||||
// If trying to seek back while fading to the next track, we need to
|
||||
// pause and reset the next track so that they don't begin overlapping
|
||||
if (playQueue.isFading) {
|
||||
if (playQueue.currentPlayer === 1) {
|
||||
playersRef.current.player2.audioEl.current.pause();
|
||||
playersRef.current.player2.audioEl.current.currentTime = 0;
|
||||
dispatch(setPlayerVolume({ player: 1, volume: playQueue.volume }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: 0 }));
|
||||
} else {
|
||||
playersRef.current.player1.audioEl.current.pause();
|
||||
playersRef.current.player1.audioEl.current.currentTime = 0;
|
||||
dispatch(setPlayerVolume({ player: 1, volume: 0 }));
|
||||
dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
|
||||
}
|
||||
}
|
||||
|
||||
setManualSeek(e);
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const handleOnWaiting = () => {
|
||||
@@ -115,126 +130,115 @@ const PlayerBar = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<PlayerContainer>
|
||||
<Player ref={playersRef} isDragging={isDragging} />
|
||||
<Button onClick={handleOnWaiting} />
|
||||
|
||||
<FlexboxGrid align="middle" style={{ height: '100%' }}>
|
||||
<FlexboxGrid.Item
|
||||
colspan={6}
|
||||
style={{ textAlign: 'left', paddingLeft: '25px' }}
|
||||
>
|
||||
<PlayerColumn left height="50px">
|
||||
<div>Is seeking: {isDragging ? 'true' : 'false'}</div>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={12}
|
||||
style={{ textAlign: 'center', verticalAlign: 'middle' }}
|
||||
>
|
||||
<PlayerColumn center height="45px">
|
||||
<PlayerControlIcon icon="random" size="lg" />
|
||||
<PlayerControlIcon
|
||||
icon="step-backward"
|
||||
size="lg"
|
||||
onClick={handleClickPrevious}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon={
|
||||
playQueue.status === 'PLAYING' ? 'pause-circle' : 'play-circle'
|
||||
}
|
||||
size="3x"
|
||||
onClick={handleClickPlayPause}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon="step-forward"
|
||||
size="lg"
|
||||
onClick={handleClickNext}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon="repeat"
|
||||
size="lg"
|
||||
onClick={() => console.log('h')}
|
||||
/>
|
||||
</PlayerColumn>
|
||||
<PlayerColumn center height="35px">
|
||||
<FlexboxGrid
|
||||
justify="center"
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '35px',
|
||||
}}
|
||||
>
|
||||
<FlexboxGrid.Item
|
||||
colspan={4}
|
||||
style={{ textAlign: 'right', paddingRight: '10px' }}
|
||||
<Player ref={playersRef}>
|
||||
<PlayerContainer>
|
||||
<FlexboxGrid align="middle" style={{ height: '100%' }}>
|
||||
<FlexboxGrid.Item
|
||||
colspan={6}
|
||||
style={{ textAlign: 'left', paddingLeft: '25px' }}
|
||||
>
|
||||
<PlayerColumn left height="50px">
|
||||
<div>Is seeking: {isDragging ? 'true' : 'false'}</div>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={12}
|
||||
style={{ textAlign: 'center', verticalAlign: 'middle' }}
|
||||
>
|
||||
<PlayerColumn center height="45px">
|
||||
<PlayerControlIcon icon="random" size="lg" />
|
||||
<PlayerControlIcon
|
||||
icon="step-backward"
|
||||
size="lg"
|
||||
onClick={handleClickPrevious}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon={
|
||||
playQueue.status === 'PLAYING'
|
||||
? 'pause-circle'
|
||||
: 'play-circle'
|
||||
}
|
||||
size="3x"
|
||||
onClick={handleClickPlayPause}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon="step-forward"
|
||||
size="lg"
|
||||
onClick={handleClickNext}
|
||||
/>
|
||||
<PlayerControlIcon
|
||||
icon="repeat"
|
||||
size="lg"
|
||||
onClick={() => console.log('Set Repeat')}
|
||||
/>
|
||||
</PlayerColumn>
|
||||
<PlayerColumn center height="35px">
|
||||
<FlexboxGrid
|
||||
justify="center"
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '35px',
|
||||
}}
|
||||
>
|
||||
{format((isDragging ? manualSeek : seek) * 1000)}
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item colspan={16}>
|
||||
<Slider
|
||||
progress
|
||||
defaultValue={0}
|
||||
value={isDragging ? manualSeek : seek}
|
||||
tooltip={false}
|
||||
max={
|
||||
playQueue.entry[playQueue.currentIndex]?.duration -
|
||||
10 * 1.3 || 0
|
||||
}
|
||||
onChange={handleSeekSlider}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={4}
|
||||
style={{ textAlign: 'left', paddingLeft: '10px' }}
|
||||
>
|
||||
{format(
|
||||
playQueue.entry[playQueue.currentIndex]?.duration * 1000 || 0
|
||||
)}
|
||||
</FlexboxGrid.Item>
|
||||
</FlexboxGrid>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={6}
|
||||
style={{ textAlign: 'right', paddingRight: '25px' }}
|
||||
>
|
||||
<PlayerColumn right height="45px">
|
||||
<Icon
|
||||
icon={
|
||||
playQueue.volume > 0.7
|
||||
? 'volume-up'
|
||||
: playQueue.volume < 0.3
|
||||
? 'volume-off'
|
||||
: 'volume-down'
|
||||
}
|
||||
size="lg"
|
||||
style={{ marginRight: '15px' }}
|
||||
/>
|
||||
<Slider
|
||||
progress
|
||||
value={Math.floor(playQueue.volume * 100)}
|
||||
style={{ width: '80px' }}
|
||||
onChange={handleVolumeSlider}
|
||||
/>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
</FlexboxGrid>
|
||||
{/* <Button onClick={() => console.log(playQueue.entry)}>Length</Button>
|
||||
<div>
|
||||
{`Current index: ${playQueue.currentIndex} | `}
|
||||
{`Player1 index: ${playQueue.player1Index} - ${
|
||||
playQueue.entry[playQueue.player1Index]?.title
|
||||
} | `}
|
||||
{`Player2 index: ${playQueue.player2Index} - ${
|
||||
playQueue.entry[playQueue.player2Index]?.title
|
||||
} | `}
|
||||
{`CurrentPlayer: ${playQueue.currentPlayer}`}
|
||||
</div> */}
|
||||
</PlayerContainer>
|
||||
<FlexboxGrid.Item
|
||||
colspan={4}
|
||||
style={{ textAlign: 'right', paddingRight: '10px' }}
|
||||
>
|
||||
{format((isDragging ? manualSeek : seek) * 1000)}
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item colspan={16}>
|
||||
<Slider
|
||||
progress
|
||||
// disabled={playQueue.isFading}
|
||||
defaultValue={0}
|
||||
value={isDragging ? manualSeek : seek}
|
||||
tooltip={false}
|
||||
max={playQueue.entry[playQueue.currentIndex]?.duration || 0}
|
||||
onChange={handleSeekSlider}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={4}
|
||||
style={{ textAlign: 'left', paddingLeft: '10px' }}
|
||||
>
|
||||
{format(
|
||||
playQueue.entry[playQueue.currentIndex]?.duration * 1000 ||
|
||||
0
|
||||
)}
|
||||
</FlexboxGrid.Item>
|
||||
</FlexboxGrid>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
<FlexboxGrid.Item
|
||||
colspan={6}
|
||||
style={{ textAlign: 'right', paddingRight: '25px' }}
|
||||
>
|
||||
<PlayerColumn right height="45px">
|
||||
<Icon
|
||||
icon={
|
||||
playQueue.volume > 0.7
|
||||
? 'volume-up'
|
||||
: playQueue.volume < 0.3
|
||||
? 'volume-off'
|
||||
: 'volume-down'
|
||||
}
|
||||
size="lg"
|
||||
style={{ marginRight: '15px' }}
|
||||
/>
|
||||
<Slider
|
||||
progress
|
||||
value={Math.floor(playQueue.volume * 100)}
|
||||
style={{ width: '80px' }}
|
||||
onChange={handleVolumeSlider}
|
||||
/>
|
||||
</PlayerColumn>
|
||||
</FlexboxGrid.Item>
|
||||
</FlexboxGrid>
|
||||
</PlayerContainer>
|
||||
</Player>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ export interface PlayQueue {
|
||||
currentSongId: string;
|
||||
currentPlayer: number;
|
||||
currentSeek: number;
|
||||
isFading: boolean;
|
||||
autoIncremented: boolean;
|
||||
player1: {
|
||||
index: number;
|
||||
volume: number;
|
||||
@@ -55,6 +57,8 @@ const initialState: PlayQueue = {
|
||||
currentSongId: '',
|
||||
currentPlayer: 1,
|
||||
currentSeek: 0,
|
||||
isFading: false,
|
||||
autoIncremented: false,
|
||||
player1: {
|
||||
index: 0,
|
||||
volume: 0.5,
|
||||
@@ -69,6 +73,8 @@ const initialState: PlayQueue = {
|
||||
entry: [],
|
||||
};
|
||||
|
||||
// TODO: Needs refactoring due to rapid experimental changes to add gapless playback
|
||||
|
||||
const playQueueSlice = createSlice({
|
||||
name: 'nowPlaying',
|
||||
initialState,
|
||||
@@ -79,6 +85,10 @@ const playQueueSlice = createSlice({
|
||||
}
|
||||
},
|
||||
|
||||
setAutoIncremented: (state, action: PayloadAction<boolean>) => {
|
||||
state.autoIncremented = action.payload;
|
||||
},
|
||||
|
||||
setVolume: (state, action: PayloadAction<number>) => {
|
||||
state.volume = action.payload;
|
||||
},
|
||||
@@ -102,6 +112,7 @@ const playQueueSlice = createSlice({
|
||||
state.currentIndex += 1;
|
||||
if (action.payload === 'usingHotkey') {
|
||||
state.currentPlayer = 1;
|
||||
state.isFading = false;
|
||||
state.player1.volume = state.volume;
|
||||
state.player1.index = state.currentIndex;
|
||||
state.player2.index = state.currentIndex + 1;
|
||||
@@ -111,6 +122,7 @@ const playQueueSlice = createSlice({
|
||||
if (state.repeatAll) {
|
||||
state.currentIndex = 0;
|
||||
if (action.payload === 'usingHotkey') {
|
||||
state.isFading = false;
|
||||
state.currentPlayer = 1;
|
||||
state.player1.index = 0;
|
||||
state.player2.index = 1;
|
||||
@@ -137,6 +149,7 @@ const playQueueSlice = createSlice({
|
||||
);
|
||||
|
||||
state.currentSeek = 0;
|
||||
state.isFading = false;
|
||||
state.player1.index = findIndex;
|
||||
state.player1.volume = state.volume;
|
||||
state.player2.index = findIndex + 1;
|
||||
@@ -186,6 +199,7 @@ const playQueueSlice = createSlice({
|
||||
// Reset player defaults
|
||||
state.entry = [];
|
||||
state.status = 'PAUSED';
|
||||
state.isFading = false;
|
||||
state.currentIndex = 0;
|
||||
state.currentSongId = '';
|
||||
state.currentPlayer = 1;
|
||||
@@ -207,6 +221,7 @@ const playQueueSlice = createSlice({
|
||||
clearPlayQueue: (state) => {
|
||||
state.entry = [];
|
||||
state.status = 'PAUSED';
|
||||
state.isFading = false;
|
||||
state.currentIndex = 0;
|
||||
state.currentSongId = '';
|
||||
state.currentPlayer = 1;
|
||||
@@ -223,6 +238,10 @@ const playQueueSlice = createSlice({
|
||||
state.isLoading = false;
|
||||
},
|
||||
|
||||
setIsFading: (state, action: PayloadAction<boolean>) => {
|
||||
state.isFading = action.payload;
|
||||
},
|
||||
|
||||
moveUp: (state, action: PayloadAction<number[]>) => {
|
||||
// Create a copy of the queue so we can mutate it in place with arrayMove.mutate
|
||||
const tempQueue = state.entry.slice();
|
||||
@@ -304,5 +323,7 @@ export const {
|
||||
setPlayerVolume,
|
||||
setVolume,
|
||||
setCurrentSeek,
|
||||
setIsFading,
|
||||
setAutoIncremented,
|
||||
} = playQueueSlice.actions;
|
||||
export default playQueueSlice.reducer;
|
||||
|
||||
Reference in New Issue
Block a user