Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Gapless Playback #521

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"package:checksums": "bash scripts/checksum.sh"
},
"dependencies": {
"@synesthesia-project/precise-audio": "^0.3.1",
"bluebird": "3.7.2",
"chardet": "0.8.0",
"classnames": "2.2.6",
Expand Down
1 change: 1 addition & 0 deletions src/ui/actions/AppActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const init = async () => {

// Bind player events
// Audio Events
Player.getAudio().addEventListener('next', PlayerActions.next);
Player.getAudio().addEventListener('ended', PlayerActions.next);
Player.getAudio().addEventListener('error', PlayerActions.audioError);
Player.getAudio().addEventListener('timeupdate', async () => {
Expand Down
42 changes: 22 additions & 20 deletions src/ui/actions/PlayerActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import types from '../constants/action-types';
import SORT_ORDERS from '../constants/sort-orders';

import * as app from '../lib/app';
import * as utils from '../utils/utils';
import Player from '../lib/player';
import { sortTracks, filterTracks } from '../utils/utils-library';
import { shuffleTracks } from '../utils/utils-player';
import { TrackModel, PlayerStatus, Repeat } from '../../shared/types/interfaces';
import { updatePlayerTrackQueue } from '../reducers/player';
import * as ToastsActions from './ToastsActions';

const { ipcRenderer } = electron;
Expand Down Expand Up @@ -71,7 +71,7 @@ export const start = async (queue?: TrackModel[], _id?: string) => {
}
}

const { shuffle } = state.player;
const { shuffle, repeat } = state.player;

const oldQueue = [...newQueue];
const trackId = _id || newQueue[0]._id;
Expand All @@ -83,11 +83,6 @@ export const start = async (queue?: TrackModel[], _id?: string) => {

// If a track exists
if (queuePosition > -1) {
const uri = utils.parseUri(newQueue[queuePosition].path);

Player.setAudioSrc(uri);
await Player.play();

let queueCursor = queuePosition; // Clean that variable mess later

// Check if we have to shuffle the queue
Expand All @@ -98,6 +93,13 @@ export const start = async (queue?: TrackModel[], _id?: string) => {
queueCursor = 0;
}

updatePlayerTrackQueue({
queue: newQueue,
queueCursor,
repeat
});
await Player.play();

store.dispatch({
type: types.PLAYER_START,
payload: {
Expand Down Expand Up @@ -156,13 +158,13 @@ export const next = async () => {
newQueueCursor = queueCursor + 1;
}

const track = queue[newQueueCursor];

// tslint:disable-next-line strict-type-predicates
if (track !== undefined) {
const uri = utils.parseUri(track.path);

Player.setAudioSrc(uri);
if (queue[newQueueCursor] !== undefined) {
updatePlayerTrackQueue({
queue,
queueCursor: newQueueCursor,
repeat
});
await Player.play();
store.dispatch({
type: types.PLAYER_NEXT,
Expand All @@ -184,7 +186,7 @@ export const previous = async () => {
const currentTime = Player.getCurrentTime();

// TODO (y.solovyov | martpie): calling getState is a hack.
const { queue, queueCursor } = store.getState().player;
const { queue, queueCursor, repeat } = store.getState().player;
let newQueueCursor = queueCursor;

if (queueCursor !== null && newQueueCursor !== null) {
Expand All @@ -194,13 +196,13 @@ export const previous = async () => {
newQueueCursor = queueCursor - 1;
}

const newTrack = queue[newQueueCursor];

// tslint:disable-next-line
if (newTrack !== undefined) {
const uri = utils.parseUri(newTrack.path);

Player.setAudioSrc(uri);
if (queue[newQueueCursor] !== undefined) {
updatePlayerTrackQueue({
queue,
queueCursor: newQueueCursor,
repeat
});
await Player.play();

store.dispatch({
Expand Down
12 changes: 8 additions & 4 deletions src/ui/actions/QueueActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import types from '../constants/action-types';

import * as app from '../lib/app';
import Player from '../lib/player';
import * as utils from '../utils/utils';

import { Track } from '../../shared/types/interfaces';
import { updatePlayerTrackQueue } from '../reducers/player';

/**
* Start audio playback from the queue
*/
export const start = async (index: number) => {
const { queue } = store.getState().player;
const uri = utils.parseUri(queue[index].path);
// TODO (y.solovyov | martpie): calling getState is a hack.
const { queue, repeat } = store.getState().player;

Player.setAudioSrc(uri);
updatePlayerTrackQueue({
queue,
queueCursor: index,
repeat
});
await Player.play();

store.dispatch({
Expand Down
17 changes: 9 additions & 8 deletions src/ui/lib/player.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PreciseAudio from '@synesthesia-project/precise-audio';
import * as app from './app';

interface PlayerOptions {
Expand All @@ -8,7 +9,7 @@ interface PlayerOptions {
}

class Player {
private audio: HTMLAudioElement;
private audio: PreciseAudio;
private durationThresholdReached: boolean;
public threshold: number;

Expand All @@ -21,11 +22,12 @@ class Player {
...options
};

this.audio = new Audio();
this.audio = new PreciseAudio();

// Disable gapless for any songs longer than 10 minutes
// TODO: allow user configuration of this
this.audio.thresholds.basicModeThresholdSeconds = 60 * 10;
this.audio.defaultPlaybackRate = mergedOptions.playbackRate;
// eslint-disable-next-line
// @ts-ignore
this.audio.setSinkId(mergedOptions.audioOutputDevice);
this.audio.playbackRate = mergedOptions.playbackRate;
this.audio.volume = mergedOptions.volume;
Expand Down Expand Up @@ -81,15 +83,14 @@ class Player {
}

async setOutputDevice(deviceId: string) {
// eslint-disable-next-line
// @ts-ignore
await this.audio.setSinkId(deviceId);
}

setAudioSrc(src: string) {
setTracks(srcs: string[]) {
console.log('setTracks', srcs);
// When we change song, need to update the thresholdReached indicator.
this.durationThresholdReached = false;
this.audio.src = src;
this.audio.updateTracks(...srcs);
}

setAudioCurrentTime(currentTime: number) {
Expand Down
Loading