Un-revert, add duration to llama track

It seems the bug I saw (the demo track had an infinite duration), was not
a bug in my code, but a bug/change in the latest version of mobile
Safari.

Undoing the revert, since it doesn't really help, and adding back the
channel detection code.
This commit is contained in:
Jordan Eldredge 2018-04-08 19:13:58 -07:00
parent 5b39cb24a3
commit 373d613e19
6 changed files with 69 additions and 13 deletions

View file

@ -65,3 +65,4 @@ export const MEDIA_TAG_REQUEST_FAILED = "MEDIA_TAG_REQUEST_FAILED";
export const NETWORK_CONNECTED = "NETWORK_CONNECTED";
export const NETWORK_DISCONNECTED = "NETWORK_DISCONNECTED";
export const UPDATE_WINDOW_POSITIONS = "UPDATE_WINDOW_POSITIONS";
export const CHANNEL_COUNT_CHANGED = "CHANNEL_COUNT_CHANGED";

View file

@ -23,7 +23,16 @@ export const skinUrl = config.skinUrl === undefined ? skin : config.skinUrl;
export const initialTracks = config.initialTracks || [
{
metaData: { artist: "DJ Mike Llama", title: "Llama Whippin' Intro" },
url: llamaAudio
url: llamaAudio,
duration: 5.322286
},
{
url: "https://cdn.changelog.com/uploads/podcast/291/the-changelog-291.mp3",
metaData: {
artist: "Changelog Media",
title: "Winamp2-js with Jordan Eldredge"
},
duration: 4841.038367
}
];

View file

@ -48,7 +48,7 @@ function filterBreadcrumbActions(action) {
Raven.config(sentryDsn, {
/* global COMMITHASH */
release: COMMITHASH || "DEV"
release: typeof COMMITHASH !== "undefined" ? COMMITHASH : "DEV"
}).install();
// Don't prompt user to install Winamp2-js. It's probably not

View file

@ -1,6 +1,7 @@
/* Emulate the native <audio> element with Web Audio API */
import { BANDS } from "../constants";
import ElementSource from "./elementSource";
import detectChannels from "./detectChannels";
export default class Media {
constructor() {
@ -32,10 +33,12 @@ export default class Media {
timeupdate: function() {},
visualizerupdate: function() {},
ended: function() {},
fileLoaded: function() {}
fileLoaded: function() {},
channelupdate: function() {}
};
// We don't currently know how many channels
this._channels = null;
this._balance = 0;
this.name = null;
// The _source node has to be recreated each time it's stopped or
// paused, so we don't create it here. Instead we create this dummy
@ -60,6 +63,10 @@ export default class Media {
// Create the analyser node for the visualizer
this._analyser = this._context.createAnalyser();
this._analyser.fftSize = 2048;
// TODO: Tune these to something that looks like Winamp
this._analyser.minDecibels = -90;
this._analyser.maxDecibels = -10;
this._analyser.smoothingTimeConstant = 0.8;
// Create the gain node for the volume control
this._gainNode = this._context.createGain();
@ -154,8 +161,29 @@ export default class Media {
this._chanMerge.connect(this._analyser);
this._gainNode.connect(this._context.destination);
window.media = this;
}
_setChannels(num) {
const assumedChannels = num == null ? 2 : num;
this._chanSplit.disconnect();
this._chanSplit.connect(this._leftGain, 0);
// If we only have one channel, use it for both left and right.
this._chanSplit.connect(this._rightGain, assumedChannels === 1 ? 0 : 1);
this._channels = num;
this._callbacks.channelupdate();
}
_makeMono() {
this._setChannels(1);
}
_makeStereo() {
this._setChannels(2);
}
_resetChannels() {
this._setChannels(null);
}
/* Properties */
duration() {
return this._source.getDuration();
@ -174,7 +202,7 @@ export default class Media {
}
channels() {
return this._source.getNumberOfChannels();
return this._channels == null ? 2 : this._channels;
}
sampleRate() {
@ -182,8 +210,17 @@ export default class Media {
}
/* Actions */
play() {
this._source.play();
async play() {
await this._source.play();
if (this._channels == null) {
detectChannels(this._staticSource)
.then(channels => {
this._setChannels(channels);
})
.catch(() => {
this._setChannels(null);
});
}
}
pause() {
@ -259,10 +296,10 @@ export default class Media {
}
// Used only for the initial load, since it must have a CORS header
async loadFromUrl(url, fileName, autoPlay) {
this.name = fileName;
async loadFromUrl(url, autoPlay) {
this._callbacks.waiting();
await this._source.loadUrl(url);
this._resetChannels();
this._callbacks.stopWaiting();
if (autoPlay) {
this.play();

View file

@ -15,7 +15,8 @@ import {
SET_EQ_OFF,
SET_EQ_ON,
PLAY_TRACK,
BUFFER_TRACK
BUFFER_TRACK,
CHANNEL_COUNT_CHANGED
} from "./actionTypes";
import { next as nextTrack } from "./actionCreators";
import { getCurrentTrackId } from "./selectors";
@ -63,6 +64,13 @@ export default media => store => {
});
});
media.addEventListener("channelupdate", () => {
store.dispatch({
type: CHANNEL_COUNT_CHANGED,
channels: media.channels()
});
});
return next => action => {
// TODO: Consider doing this after the action, and using the state as the source of truth.
switch (action.type) {
@ -87,14 +95,12 @@ export default media => store => {
case PLAY_TRACK:
media.loadFromUrl(
store.getState().playlist.tracks[action.id].url,
action.name,
true
);
break;
case BUFFER_TRACK:
media.loadFromUrl(
store.getState().playlist.tracks[action.id].url,
action.name,
false
);
break;

View file

@ -11,7 +11,8 @@ import {
TOGGLE_SHUFFLE,
TOGGLE_TIME_MODE,
UPDATE_TIME_ELAPSED,
ADD_TRACK_FROM_URL
ADD_TRACK_FROM_URL,
CHANNEL_COUNT_CHANGED
} from "../actionTypes";
const media = (state, action) => {
@ -44,6 +45,8 @@ const media = (state, action) => {
case STOP:
case IS_STOPPED:
return { ...state, status: "STOPPED" };
case CHANNEL_COUNT_CHANGED:
return { ...state, channels: action.channels };
case TOGGLE_TIME_MODE:
const newMode = state.timeMode === "REMAINING" ? "ELAPSED" : "REMAINING";
return { ...state, timeMode: newMode };