diff --git a/package.json b/package.json index ef28bbe..e78c635 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webstudio", - "version": "1.4.0", + "version": "1.5.0", "private": true, "dependencies": { "@babel/core": "7.6.0", diff --git a/src/optionsMenu/helpers/VUMeter.tsx b/src/optionsMenu/helpers/VUMeter.tsx index 0e7791b..6b85960 100644 --- a/src/optionsMenu/helpers/VUMeter.tsx +++ b/src/optionsMenu/helpers/VUMeter.tsx @@ -30,18 +30,21 @@ export function VUMeter(props: VUMeterProps) { const FPS = 30; // Limit the FPS so that lower spec machines have a better time juggling CPU. useEffect(() => { + let isMounted = true; // This VU exists as we're calling useEffect const animate = () => { - if (!isMic || isMicOpen) { + if ((!isMic || isMicOpen) && isMounted) { const result = audioEngine.getLevels( props.source, props.stereo ? props.stereo : false ); setPeakL(result[0]); - if (props.stereo) { + if (props.stereo && isMounted) { setPeakR(result[1]); } setTimeout((current = rafRef.current, a = animate) => { - current = requestAnimationFrame(a); + if (isMounted) { + current = requestAnimationFrame(a); + } }, 1000 / FPS); } }; @@ -49,6 +52,7 @@ export function VUMeter(props: VUMeterProps) { rafRef.current = requestAnimationFrame(animate); } return () => { + isMounted = false; // Tell the async stuff above to not bother if the VU meter has gone away. if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; diff --git a/src/showplanner/Item.tsx b/src/showplanner/Item.tsx index 5e5fc83..fb90ea7 100644 --- a/src/showplanner/Item.tsx +++ b/src/showplanner/Item.tsx @@ -8,6 +8,7 @@ import * as MixerState from "../mixer/state"; import { Draggable } from "react-beautiful-dnd"; import { contextMenu } from "react-contexify"; import "./item.scss"; +import { HHMMTosec, secToHHMM } from "../lib/utils"; import { PLAYER_ID_PREVIEW } from "../mixer/audio"; export const TS_ITEM_MENU_ID = "SongMenu"; @@ -80,12 +81,33 @@ export const Item = memo(function Item({ if ("artist" in x && x.artist !== "") data.push("Artist: " + x.artist); if ("album" in x && x.album.title !== "") data.push("Album: " + x.album.title); - data.push("Length: " + x.length.toString()); - if ("intro" in x) data.push("Intro: " + x.intro + " secs"); - if ("cue" in x) data.push("Cue: " + x.cue + " secs"); - if ("outro" in x) data.push("Outro: " + x.outro + " secs"); + data.push("Length: " + x.length); + if ("intro" in x) + data.push( + "Intro: " + (x.intro > 60 ? secToHHMM(x.intro) : x.intro + " secs") + ); + if ("cue" in x) + data.push("Cue: " + (x.cue > 60 ? secToHHMM(x.cue) : x.cue + " secs")); + if ("outro" in x) { + // Outro seconds are counted from end of track, except 0 = no outro; + const outroSecs = x.outro === 0 ? 0 : HHMMTosec(x.length) - x.outro; + data.push( + "Outro: " + + (outroSecs > 60 ? secToHHMM(outroSecs) : outroSecs + " secs") + ); + } data.push("Played: " + ("played" in x ? (x.played ? "Yes" : "No") : "No")); - + data.push( + "ID: " + ("trackid" in x ? x.trackid : "managedid" in x && x.managedid) + ); + if (showDebug) { + data.push( + "Debug: itemId(): " + + itemId(x) + + " - Channel/weight: " + + ("channel" in x && x.channel + "/" + x.weight) + ); + } return data.join("¬"); // Something obscure to split against. } diff --git a/src/showplanner/Player.tsx b/src/showplanner/Player.tsx index 1706035..543bd0f 100644 --- a/src/showplanner/Player.tsx +++ b/src/showplanner/Player.tsx @@ -246,7 +246,13 @@ function LoadedTrackInfo({ id }: { id: number }) { ); } -export function Player({ id, pfl }: { id: number; pfl: boolean }) { +export function Player({ + id, + isPreviewChannel, +}: { + id: number; + isPreviewChannel: boolean; +}) { // Define time remaining (secs) when the play icon should flash. const SECS_REMAINING_WARNING = 20; @@ -295,7 +301,7 @@ export function Player({ id, pfl }: { id: number; pfl: boolean }) { } >